Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include <cppunit/TestAssert.h> 19 : #include <cppunit/TestFixture.h> 20 : #include <cppunit/extensions/HelperMacros.h> 21 : 22 : #include <condition_variable> 23 : #include <filesystem> 24 : #include <string> 25 : 26 : #include "jami.h" 27 : #include "../common.h" 28 : #include "jamidht/swarm/swarm_manager.h" 29 : #include <dhtnet/multiplexed_socket.h> 30 : 31 : #include <opendht/thread_pool.h> 32 : 33 : #include "../../test_runner.h" 34 : #include "account_const.h" 35 : #include "conversation/conversationcommon.h" 36 : #include "manager.h" 37 : 38 : using namespace dht; 39 : using NodeId = dht::PkId; 40 : using namespace std::literals::chrono_literals; 41 : 42 : namespace jami { 43 : namespace test { 44 : 45 : struct ConvData 46 : { 47 : std::string id {}; 48 : bool requestReceived {false}; 49 : bool needsHost {false}; 50 : bool conferenceChanged {false}; 51 : bool conferenceRemoved {false}; 52 : std::string hostState {}; 53 : std::vector<std::map<std::string, std::string>> messages {}; 54 : }; 55 : 56 : class SwarmConversationTest : public CppUnit::TestFixture 57 : { 58 : public: 59 : ~SwarmConversationTest(); 60 2 : static std::string name() { return "SwarmConversationTest"; } 61 : void setUp(); 62 : 63 : std::map<std::string, ConvData> accountMap; 64 : std::vector<std::string> accountIds; 65 : 66 : std::mutex mtx; 67 : std::unique_lock<std::mutex> lk {mtx}; 68 : std::condition_variable cv; 69 : 70 : private: 71 : void connectSignals(); 72 : 73 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; 74 : 75 : void testSendMessage(); 76 : 77 2 : CPPUNIT_TEST_SUITE(SwarmConversationTest); 78 1 : CPPUNIT_TEST(testSendMessage); 79 4 : CPPUNIT_TEST_SUITE_END(); 80 : }; 81 : 82 : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SwarmConversationTest, SwarmConversationTest::name()); 83 : 84 : void 85 1 : SwarmConversationTest::setUp() 86 : { 87 1 : libjami::init( 88 : libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG)); 89 1 : if (not Manager::instance().initialized) 90 1 : CPPUNIT_ASSERT(libjami::start("jami-sample.yml")); 91 : 92 1 : auto actors = load_actors("actors/account_list.yml"); 93 : 94 27 : for (const auto& act : actors) { 95 26 : auto id = act.second; 96 26 : accountIds.emplace_back(id); 97 26 : std::cout << act.second << std::endl; 98 26 : accountMap.insert({id, {}}); 99 26 : } 100 : 101 1 : wait_for_announcement_of(accountIds, 60s); 102 1 : } 103 : 104 2 : SwarmConversationTest::~SwarmConversationTest() 105 : { 106 1 : wait_for_removal_of(accountIds); 107 1 : libjami::fini(); 108 2 : } 109 : 110 : void 111 1 : SwarmConversationTest::connectSignals() 112 : { 113 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; 114 : 115 1 : confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( 116 26 : [&](const std::string& accountId, const std::string& conversationId) { 117 702 : for (const auto& accId : accountIds) { 118 676 : if (accountId == accId) { 119 26 : accountMap[accId].id = conversationId; 120 : } 121 : } 122 26 : cv.notify_one(); 123 26 : })); 124 1 : confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( 125 676 : [&](const std::string& accountId, 126 : const std::string& conversationId, 127 : std::map<std::string, std::string> message) { 128 18252 : for (const auto& accId : accountIds) { 129 17576 : if (accountId == accId && accountMap[accId].id == conversationId) { 130 676 : accountMap[accId].messages.emplace_back(message); 131 : } 132 : } 133 676 : cv.notify_one(); 134 676 : })); 135 1 : confHandlers.insert( 136 2 : libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( 137 25 : [&](const std::string& accountId, 138 : const std::string&, 139 : std::map<std::string, std::string>) { 140 675 : for (const auto& accId : accountIds) { 141 650 : if (accountId == accId) { 142 25 : accountMap[accId].requestReceived = true; 143 : } 144 : } 145 25 : cv.notify_one(); 146 25 : })); 147 : 148 1 : libjami::registerSignalHandlers(confHandlers); 149 1 : } 150 : 151 : void 152 1 : SwarmConversationTest::testSendMessage() 153 : { 154 1 : std::map<std::string, std::shared_ptr<JamiAccount>> jamiAccounts; 155 : 156 27 : for (const auto& accId : accountIds) { 157 26 : jamiAccounts.insert({accId, Manager::instance().getAccount<JamiAccount>(accId)}); 158 26 : std::cout << "created account for: " << accId << std::endl; 159 : } 160 : 161 1 : std::mutex mtx; 162 1 : std::unique_lock lk {mtx}; 163 1 : std::condition_variable cv; 164 : 165 1 : connectSignals(); 166 : 167 1 : auto aliceId = jamiAccounts.begin()->first; 168 1 : auto convId = libjami::startConversation(aliceId); 169 : 170 1 : std::cout << "started conversation: " << convId << std::endl; 171 : 172 26 : for (auto it = std::next(jamiAccounts.begin()); it != jamiAccounts.end(); ++it) { 173 25 : auto userUri = it->second->getUsername(); 174 25 : std::cout << "adding member: " << userUri << std::endl; 175 25 : libjami::addConversationMember(aliceId, convId, userUri); 176 : 177 75 : CPPUNIT_ASSERT_EQUAL_MESSAGE("ERROR", true, cv.wait_for(lk, 40s, [&]() { 178 : return accountMap[it->first].requestReceived == true; 179 : })); 180 : 181 25 : libjami::acceptConversationRequest(it->first, convId); 182 25 : std::this_thread::sleep_for(std::chrono::seconds(10)); 183 25 : } 184 : 185 1 : std::cout << "waiting for conversation ready" << std::endl; 186 26 : for (size_t i = 1; i < accountIds.size(); i++) { 187 50 : CPPUNIT_ASSERT( 188 : cv.wait_for(lk, 40s, [&]() { return accountMap[accountIds.at(i)].id == convId; })); 189 : } 190 1 : std::cout << "messages size " << accountMap[accountIds.at(0)].messages.size() << std::endl; 191 : 192 2 : CPPUNIT_ASSERT( 193 : cv.wait_for(lk, 70s, [&]() { return accountMap[accountIds.at(0)].messages.size() >= 2; })); 194 : 195 1 : libjami::sendMessage(aliceId, convId, "hi"s, ""); 196 : 197 26 : for (size_t i = 1; i < accountIds.size(); i++) { 198 25 : std::cout << "COUNTER: " << i << " messages size " 199 25 : << accountMap[accountIds.at(i)].messages.size() << std::endl; 200 : 201 25 : if (accountMap[accountIds.at(i)].messages.size() >= 105) { 202 0 : for (const auto& msg : accountMap[accountIds.at(i)].messages) { 203 0 : std::cout << "Message id: " << msg.at("id") << " type: " << msg.at("type") 204 0 : << std::endl; 205 : } 206 : } 207 : 208 51 : CPPUNIT_ASSERT(cv.wait_for(lk, 40s, [&]() { 209 : return accountMap[accountIds.at(i)].messages.size() >= 1; 210 : })); 211 : } 212 : 213 1 : libjami::unregisterSignalHandlers(); 214 1 : } 215 : 216 : } // namespace test 217 : } // namespace jami 218 : 219 1 : RING_TEST_RUNNER(jami::test::SwarmConversationTest::name())