LCOV - code coverage report
Current view: top level - test/unitTest/presence - presence.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 201 202 99.5 %
Date: 2024-05-12 08:41:20 Functions: 44 44 100.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 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 "../../test_runner.h"
      27             : #include "account_const.h"
      28             : #include "common.h"
      29             : #include "conversation/conversationcommon.h"
      30             : #include "manager.h"
      31             : #include "media_const.h"
      32             : #include "sip/sipcall.h"
      33             : 
      34             : using namespace std::literals::chrono_literals;
      35             : 
      36             : namespace jami {
      37             : namespace test {
      38             : 
      39             : struct UserData
      40             : {
      41             :     std::map<std::string, int> status;
      42             :     std::map<std::string, std::string> statusNote;
      43             : 
      44             :     std::string conversationId;
      45             :     bool requestReceived {false};
      46             : };
      47             : 
      48             : class PresenceTest : public CppUnit::TestFixture
      49             : {
      50             : public:
      51          10 :     ~PresenceTest() { libjami::fini(); }
      52           2 :     static std::string name() { return "PresenceTest"; }
      53             :     void setUp();
      54             :     void tearDown();
      55             : 
      56             :     std::string aliceId;
      57             :     std::string bobId;
      58             :     std::string carlaId;
      59             :     UserData aliceData_;
      60             :     UserData bobData_;
      61             :     UserData carlaData_;
      62             : 
      63             :     std::mutex mtx;
      64             :     std::unique_lock<std::mutex> lk {mtx};
      65             :     std::condition_variable cv;
      66             : 
      67             : private:
      68             :     void connectSignals();
      69             :     void enableCarla();
      70             : 
      71             :     void testGetSetSubscriptions();
      72             :     void testPresenceStatus();
      73             :     void testPresenceStatusNote();
      74             :     void testPresenceInvalidStatusNote();
      75             :     void testPresenceStatusNoteBeforeConnection();
      76             : 
      77           2 :     CPPUNIT_TEST_SUITE(PresenceTest);
      78           1 :     CPPUNIT_TEST(testGetSetSubscriptions);
      79           1 :     CPPUNIT_TEST(testPresenceStatus);
      80           1 :     CPPUNIT_TEST(testPresenceStatusNote);
      81           1 :     CPPUNIT_TEST(testPresenceInvalidStatusNote);
      82           1 :     CPPUNIT_TEST(testPresenceStatusNoteBeforeConnection);
      83           4 :     CPPUNIT_TEST_SUITE_END();
      84             : };
      85             : 
      86             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PresenceTest, PresenceTest::name());
      87             : 
      88             : void
      89           5 : PresenceTest::setUp()
      90             : {
      91             :     // Init daemon
      92           5 :     libjami::init(
      93             :         libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      94           5 :     if (not Manager::instance().initialized)
      95           1 :         CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      96             : 
      97           5 :     auto actors = load_actors("actors/alice-bob-carla.yml");
      98           5 :     aliceId = actors["alice"];
      99           5 :     bobId = actors["bob"];
     100           5 :     carlaId = actors["carla"];
     101           5 :     aliceData_ = {};
     102           5 :     bobData_ = {};
     103           5 :     carlaData_ = {};
     104             : 
     105           5 :     Manager::instance().sendRegister(carlaId, false);
     106          15 :     wait_for_announcement_of({aliceId, bobId});
     107           5 : }
     108             : 
     109             : void
     110           5 : PresenceTest::tearDown()
     111             : {
     112          20 :     wait_for_removal_of({aliceId, bobId, carlaId});
     113           5 : }
     114             : 
     115             : void
     116           5 : PresenceTest::connectSignals()
     117             : {
     118           5 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     119           5 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     120           6 :         [&](const std::string& accountId, const std::string& conversationId) {
     121           6 :             if (accountId == aliceId)
     122           4 :                 aliceData_.conversationId = conversationId;
     123           2 :             else if (accountId == bobId)
     124           1 :                 bobData_.conversationId = conversationId;
     125           1 :             else if (accountId == carlaId)
     126           1 :                 carlaData_.conversationId = conversationId;
     127           6 :             cv.notify_one();
     128           6 :         }));
     129           5 :     confHandlers.insert(
     130          10 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     131           8 :             [&](const std::string& accountId,
     132             :                 const std::string& /*conversationId*/,
     133             :                 std::map<std::string, std::string> /*metadatas*/) {
     134           8 :                 if (accountId == aliceId)
     135           0 :                     aliceData_.requestReceived = true;
     136           8 :                 if (accountId == bobId)
     137           4 :                     bobData_.requestReceived = true;
     138           8 :                 if (accountId == carlaId)
     139           4 :                     carlaData_.requestReceived = true;
     140           8 :                 cv.notify_one();
     141           8 :             }));
     142           5 :     confHandlers.insert(
     143          10 :         libjami::exportable_callback<libjami::PresenceSignal::NewBuddyNotification>(
     144          37 :             [&](const std::string& accountId,
     145             :                 const std::string& peerId,
     146             :                 int status,
     147             :                 const std::string& note) {
     148          37 :                 if (accountId == aliceId) {
     149          17 :                     aliceData_.status[peerId] = status;
     150          17 :                     aliceData_.statusNote[peerId] = note;
     151          20 :                 } else if (accountId == bobId) {
     152          10 :                     bobData_.status[peerId] = status;
     153          10 :                     bobData_.statusNote[peerId] = note;
     154          10 :                 } else if (accountId == carlaId) {
     155          10 :                     carlaData_.status[peerId] = status;
     156          10 :                     carlaData_.statusNote[peerId] = note;
     157             :                 }
     158          37 :                 cv.notify_one();
     159          37 :             }));
     160             : 
     161           5 :     libjami::registerSignalHandlers(confHandlers);
     162           5 : }
     163             : 
     164             : void
     165           3 : PresenceTest::enableCarla()
     166             : {
     167           3 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     168             :     // Enable carla
     169           3 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     170           3 :     bool carlaConnected = false;
     171           3 :     confHandlers.insert(
     172           6 :         libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
     173           9 :             [&](const std::string&, const std::map<std::string, std::string>&) {
     174           9 :                 auto details = carlaAccount->getVolatileAccountDetails();
     175             :                 auto deviceAnnounced
     176          18 :                     = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED];
     177           9 :                 if (deviceAnnounced == "true") {
     178           3 :                     carlaConnected = true;
     179           3 :                     cv.notify_one();
     180             :                 }
     181           9 :             }));
     182           3 :     libjami::registerSignalHandlers(confHandlers);
     183             : 
     184           3 :     Manager::instance().sendRegister(carlaId, true);
     185           9 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; }));
     186           3 :     confHandlers.clear();
     187           3 :     libjami::unregisterSignalHandlers();
     188           3 : }
     189             : 
     190             : void
     191           1 : PresenceTest::testGetSetSubscriptions()
     192             : {
     193           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     194           1 :     auto bobUri = bobAccount->getUsername();
     195           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     196           1 :     auto carlaUri = carlaAccount->getUsername();
     197           1 :     connectSignals();
     198             : 
     199           1 :     CPPUNIT_ASSERT(libjami::getSubscriptions(aliceId).empty());
     200           3 :     libjami::setSubscriptions(aliceId, {bobUri, carlaUri});
     201           1 :     auto subscriptions = libjami::getSubscriptions(aliceId);
     202           1 :     CPPUNIT_ASSERT(subscriptions.size() == 2);
     203           2 :     auto checkSub = [&](const std::string& uri) {
     204           2 :         return std::find_if(subscriptions.begin(), subscriptions.end(), [&](const auto& sub) {
     205           3 :                    return sub.at("Buddy") == uri;
     206           4 :                }) != subscriptions.end();
     207           1 :     };
     208           1 :     CPPUNIT_ASSERT(checkSub(bobUri) && checkSub(carlaUri));
     209           1 : }
     210             : 
     211             : void
     212           1 : PresenceTest::testPresenceStatus()
     213             : {
     214           1 :     connectSignals();
     215             : 
     216           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     217           1 :     auto aliceUri = aliceAccount->getUsername();
     218           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     219           1 :     auto bobUri = bobAccount->getUsername();
     220           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     221           1 :     auto carlaUri = carlaAccount->getUsername();
     222             : 
     223             :     // Track Presence
     224           1 :     aliceAccount->trackBuddyPresence(bobUri, true);
     225           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     226             : 
     227           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     228             :         return aliceData_.status.find(bobUri) != aliceData_.status.end() && aliceData_.status[bobUri] == 1;
     229             :     }));
     230           1 :     CPPUNIT_ASSERT(aliceData_.status.find(carlaUri) == aliceData_.status.end()); // For now, still offline so no change
     231             : 
     232             :     // Carla is now online
     233           1 :     Manager::instance().sendRegister(carlaId, true);
     234           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     235             :         return aliceData_.status.find(carlaUri) != aliceData_.status.end() && aliceData_.status[carlaUri] == 1;
     236             :     }));
     237             : 
     238             :     // Start conversation
     239           1 :     libjami::startConversation(aliceId);
     240           2 :     cv.wait_for(lk, 30s, [&]() { return !aliceData_.conversationId.empty(); });
     241             : 
     242           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, bobUri);
     243           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, carlaUri);
     244           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     245             :         return bobData_.requestReceived && carlaData_.requestReceived;
     246             :     }));
     247           1 :     libjami::acceptConversationRequest(bobId, aliceData_.conversationId);
     248           1 :     libjami::acceptConversationRequest(carlaId, aliceData_.conversationId);
     249             : 
     250             :     // Should connect to peers
     251           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     252             :         return aliceData_.status[bobUri] == 2 && aliceData_.status[carlaUri] == 2;
     253             :     }));
     254             : 
     255             :     // Carla disconnects, should just let the presence on the DHT for a few minutes
     256           1 :     Manager::instance().sendRegister(carlaId, false);
     257           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     258             :         return aliceData_.status[carlaUri] == 1;
     259             :     }));
     260           1 : }
     261             : 
     262             : void
     263           1 : PresenceTest::testPresenceStatusNote()
     264             : {
     265           1 :     enableCarla();
     266           1 :     connectSignals();
     267           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     268           1 :     auto aliceUri = aliceAccount->getUsername();
     269           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     270           1 :     auto bobUri = bobAccount->getUsername();
     271           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     272           1 :     auto carlaUri = carlaAccount->getUsername();
     273             : 
     274             :     // Track Presence
     275           1 :     aliceAccount->trackBuddyPresence(bobUri, true);
     276           1 :     bobAccount->trackBuddyPresence(aliceUri, true);
     277           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     278           1 :     carlaAccount->trackBuddyPresence(aliceUri, true);
     279             : 
     280           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     281             :         return aliceData_.status.find(bobUri) != aliceData_.status.end() && aliceData_.status[bobUri] == 1
     282             :             && aliceData_.status.find(carlaUri) != aliceData_.status.end() && aliceData_.status[carlaUri] == 1;
     283             :     }));
     284             : 
     285             :     // Start conversation
     286           1 :     libjami::startConversation(aliceId);
     287           2 :     cv.wait_for(lk, 30s, [&]() { return !aliceData_.conversationId.empty(); });
     288             : 
     289           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, bobUri);
     290           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, carlaUri);
     291          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     292             :         return bobData_.requestReceived && carlaData_.requestReceived;
     293             :     }));
     294           1 :     libjami::acceptConversationRequest(bobId, aliceData_.conversationId);
     295           1 :     libjami::acceptConversationRequest(carlaId, aliceData_.conversationId);
     296             : 
     297             :     // Should connect to peers
     298           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     299             :         return aliceData_.status[bobUri] == 2 && aliceData_.status[carlaUri] == 2;
     300             :     }));
     301             : 
     302             :     // Alice sends a status note, should be received by Bob and Carla
     303           1 :     libjami::publish(aliceId, true, "Testing Jami");
     304             : 
     305           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     306             :         return bobData_.statusNote.find(aliceUri) != bobData_.statusNote.end() && bobData_.statusNote[aliceUri] == "Testing Jami"
     307             :             && carlaData_.statusNote.find(aliceUri) != carlaData_.statusNote.end() && carlaData_.statusNote[aliceUri] == "Testing Jami";
     308             :     }));
     309           1 : }
     310             : 
     311             : 
     312             : void
     313           1 : PresenceTest::testPresenceInvalidStatusNote()
     314             : {
     315           1 :     enableCarla();
     316           1 :     connectSignals();
     317           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     318           1 :     auto aliceUri = aliceAccount->getUsername();
     319           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     320           1 :     auto bobUri = bobAccount->getUsername();
     321           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     322           1 :     auto carlaUri = carlaAccount->getUsername();
     323             : 
     324             :     // Track Presence
     325           1 :     aliceAccount->trackBuddyPresence(bobUri, true);
     326           1 :     bobAccount->trackBuddyPresence(aliceUri, true);
     327           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     328           1 :     carlaAccount->trackBuddyPresence(aliceUri, true);
     329             : 
     330           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     331             :         return aliceData_.status.find(bobUri) != aliceData_.status.end() && aliceData_.status[bobUri] == 1
     332             :             && aliceData_.status.find(carlaUri) != aliceData_.status.end() && aliceData_.status[carlaUri] == 1;
     333             :     }));
     334             : 
     335             :     // Start conversation
     336           1 :     libjami::startConversation(aliceId);
     337           2 :     cv.wait_for(lk, 30s, [&]() { return !aliceData_.conversationId.empty(); });
     338             : 
     339           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, bobUri);
     340           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, carlaUri);
     341           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     342             :         return bobData_.requestReceived && carlaData_.requestReceived;
     343             :     }));
     344           1 :     libjami::acceptConversationRequest(bobId, aliceData_.conversationId);
     345           1 :     libjami::acceptConversationRequest(carlaId, aliceData_.conversationId);
     346             : 
     347             :     // Should connect to peers
     348           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     349             :         return aliceData_.status[bobUri] == 2 && aliceData_.status[carlaUri] == 2;
     350             :     }));
     351             : 
     352             :     // Alice sends a status note that will generate an invalid XML message
     353           1 :     libjami::publish(aliceId, true, "Testing<BAD>");
     354             : 
     355           7 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() {
     356             :         return bobData_.statusNote.find(aliceUri) != bobData_.statusNote.end() && bobData_.statusNote[aliceUri] == "Testing<BAD>"
     357             :             && carlaData_.statusNote.find(aliceUri) != carlaData_.statusNote.end() && carlaData_.statusNote[aliceUri] == "Testing<BAD>";
     358             :     }));
     359           1 : }
     360             : 
     361             : void
     362           1 : PresenceTest::testPresenceStatusNoteBeforeConnection()
     363             : {
     364           1 :     enableCarla();
     365           1 :     connectSignals();
     366           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     367           1 :     auto aliceUri = aliceAccount->getUsername();
     368           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     369           1 :     auto bobUri = bobAccount->getUsername();
     370           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     371           1 :     auto carlaUri = carlaAccount->getUsername();
     372             : 
     373             :     // Track Presence
     374           1 :     aliceAccount->trackBuddyPresence(bobUri, true);
     375           1 :     bobAccount->trackBuddyPresence(aliceUri, true);
     376           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     377           1 :     carlaAccount->trackBuddyPresence(aliceUri, true);
     378           1 :     libjami::publish(aliceId, true, "Testing Jami");
     379             : 
     380           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     381             :         return aliceData_.status.find(bobUri) != aliceData_.status.end() && aliceData_.status[bobUri] == 1
     382             :             && aliceData_.status.find(carlaUri) != aliceData_.status.end() && aliceData_.status[carlaUri] == 1;
     383             :     }));
     384             : 
     385             :     // Start conversation
     386           1 :     libjami::startConversation(aliceId);
     387           2 :     cv.wait_for(lk, 30s, [&]() { return !aliceData_.conversationId.empty(); });
     388             : 
     389           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, bobUri);
     390           1 :     libjami::addConversationMember(aliceId, aliceData_.conversationId, carlaUri);
     391          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() {
     392             :         return bobData_.requestReceived && carlaData_.requestReceived;
     393             :     }));
     394           1 :     libjami::acceptConversationRequest(bobId, aliceData_.conversationId);
     395           1 :     libjami::acceptConversationRequest(carlaId, aliceData_.conversationId);
     396             : 
     397             :     // Should connect to peers
     398           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     399             :         return aliceData_.status[bobUri] == 2 && aliceData_.status[carlaUri] == 2;
     400             :     }));
     401             : 
     402             :     // Alice sends a status note, should be received by Bob and Carla
     403           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     404             :         return bobData_.statusNote.find(aliceUri) != bobData_.statusNote.end() && bobData_.statusNote[aliceUri] == "Testing Jami"
     405             :             && carlaData_.statusNote.find(aliceUri) != carlaData_.statusNote.end() && carlaData_.statusNote[aliceUri] == "Testing Jami";
     406             :     }));
     407             : 
     408           1 : }
     409             : 
     410             : } // namespace test
     411             : } // namespace jami
     412             : 
     413           1 : RING_TEST_RUNNER(jami::test::PresenceTest::name())

Generated by: LCOV version 1.14