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