LCOV - code coverage report
Current view: top level - test/unitTest/call - conference.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 629 638 98.6 %
Date: 2024-04-17 21:30:23 Functions: 133 135 98.5 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2021-2024 Savoir-faire Linux Inc.
       3             :  *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
       4             :  *
       5             :  *  This program is free software; you can redistribute it and/or modify
       6             :  *  it under the terms of the GNU General Public License as published by
       7             :  *  the Free Software Foundation; either version 3 of the License, or
       8             :  *  (at your option) any later version.
       9             :  *
      10             :  *  This program is distributed in the hope that it will be useful,
      11             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  *  GNU General Public License for more details.
      14             :  *
      15             :  *  You should have received a copy of the GNU General Public License
      16             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include <cppunit/TestAssert.h>
      20             : #include <cppunit/TestFixture.h>
      21             : #include <cppunit/extensions/HelperMacros.h>
      22             : 
      23             : #include <condition_variable>
      24             : #include <string>
      25             : 
      26             : #include "manager.h"
      27             : #include "client/videomanager.h"
      28             : #include "jamidht/jamiaccount.h"
      29             : #include "../../test_runner.h"
      30             : #include "jami.h"
      31             : #include "account_const.h"
      32             : #include "common.h"
      33             : #include "media_const.h"
      34             : #include "media/video/sinkclient.h"
      35             : #include "sip/sipcall.h"
      36             : #include "sip/siptransport.h"
      37             : 
      38             : #include <dhtnet/connectionmanager.h>
      39             : 
      40             : using namespace libjami::Account;
      41             : using namespace std::literals::chrono_literals;
      42             : 
      43             : namespace jami {
      44             : namespace test {
      45             : 
      46             : struct CallData
      47             : {
      48             :     std::string callId {};
      49             :     std::string state {};
      50             :     std::string device {};
      51             :     std::string streamId {};
      52             :     std::string hostState {};
      53             :     std::atomic_bool moderatorMuted {false};
      54             :     std::atomic_bool raisedHand {false};
      55             :     std::atomic_bool active {false};
      56             :     std::atomic_bool recording {false};
      57             : 
      58          72 :     void reset()
      59             :     {
      60          72 :         callId = "";
      61          72 :         state = "";
      62          72 :         device = "";
      63          72 :         streamId = "";
      64          72 :         hostState = "";
      65          72 :         moderatorMuted = false;
      66          72 :         active = false;
      67          72 :         raisedHand = false;
      68          72 :         recording = false;
      69          72 :     }
      70             : };
      71             : 
      72             : class ConferenceTest : public CppUnit::TestFixture
      73             : {
      74             : public:
      75          23 :     ConferenceTest()
      76          23 :     {
      77             :         // Init daemon
      78          23 :         libjami::init(
      79             :             libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      80          23 :         if (not Manager::instance().initialized)
      81           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      82          23 :     }
      83          46 :     ~ConferenceTest() { libjami::fini(); }
      84           2 :     static std::string name() { return "Conference"; }
      85             :     void setUp();
      86             :     void tearDown();
      87             : 
      88             : private:
      89             :     void testGetConference();
      90             :     void testModeratorMuteUpdateParticipantsInfos();
      91             :     void testUnauthorizedMute();
      92             :     void testAudioVideoMutedStates();
      93             :     void testMuteStatusAfterAdd();
      94             :     void testCreateParticipantsSinks();
      95             :     void testMuteStatusAfterRemove();
      96             :     void testActiveStatusAfterRemove();
      97             :     void testHandsUp();
      98             :     void testPeerLeaveConference();
      99             :     void testJoinCallFromOtherAccount();
     100             :     void testDevices();
     101             :     void testUnauthorizedSetActive();
     102             :     void testHangup();
     103             :     void testIsConferenceParticipant();
     104             :     void testAudioConferenceConfInfo();
     105             :     void testHostAddRmSecondVideo();
     106             :     void testParticipantAddRmSecondVideo();
     107             :     void testPropagateRecording();
     108             :     void testBrokenParticipantAudioAndVideo();
     109             :     void testBrokenParticipantAudioOnly();
     110             :     void testAudioOnlyLeaveLayout();
     111             :     void testRemoveConferenceInOneOne();
     112             : 
     113           2 :     CPPUNIT_TEST_SUITE(ConferenceTest);
     114           1 :     CPPUNIT_TEST(testGetConference);
     115           1 :     CPPUNIT_TEST(testModeratorMuteUpdateParticipantsInfos);
     116           1 :     CPPUNIT_TEST(testUnauthorizedMute);
     117           1 :     CPPUNIT_TEST(testAudioVideoMutedStates);
     118           1 :     CPPUNIT_TEST(testMuteStatusAfterAdd);
     119           1 :     CPPUNIT_TEST(testCreateParticipantsSinks);
     120           1 :     CPPUNIT_TEST(testMuteStatusAfterRemove);
     121           1 :     CPPUNIT_TEST(testActiveStatusAfterRemove);
     122           1 :     CPPUNIT_TEST(testHandsUp);
     123           1 :     CPPUNIT_TEST(testPeerLeaveConference);
     124           1 :     CPPUNIT_TEST(testJoinCallFromOtherAccount);
     125           1 :     CPPUNIT_TEST(testDevices);
     126           1 :     CPPUNIT_TEST(testUnauthorizedSetActive);
     127           1 :     CPPUNIT_TEST(testHangup);
     128           1 :     CPPUNIT_TEST(testIsConferenceParticipant);
     129           1 :     CPPUNIT_TEST(testAudioConferenceConfInfo);
     130           1 :     CPPUNIT_TEST(testHostAddRmSecondVideo);
     131           1 :     CPPUNIT_TEST(testParticipantAddRmSecondVideo);
     132           1 :     CPPUNIT_TEST(testPropagateRecording);
     133           1 :     CPPUNIT_TEST(testBrokenParticipantAudioAndVideo);
     134           1 :     CPPUNIT_TEST(testBrokenParticipantAudioOnly);
     135           1 :     CPPUNIT_TEST(testAudioOnlyLeaveLayout);
     136           1 :     CPPUNIT_TEST(testRemoveConferenceInOneOne);
     137           4 :     CPPUNIT_TEST_SUITE_END();
     138             : 
     139             :     // Common parts
     140             :     std::string aliceId;
     141             :     std::atomic_bool hostRecording {false};
     142             :     std::string bobId;
     143             :     std::string carlaId;
     144             :     std::string daviId;
     145             :     std::string confId {};
     146             :     std::mutex pInfosMtx_ {};
     147             :     std::vector<std::map<std::string, std::string>> pInfos_ {};
     148             :     bool confChanged {false};
     149             : 
     150             :     CallData bobCall {};
     151             :     CallData carlaCall {};
     152             :     CallData daviCall {};
     153             : 
     154             :     std::mutex mtx;
     155             :     std::unique_lock<std::mutex> lk {mtx};
     156             :     std::condition_variable cv;
     157             : 
     158             :     void registerSignalHandlers();
     159             :     void startConference(bool audioOnly = false, bool addDavi = false);
     160             :     void hangupConference();
     161             : };
     162             : 
     163             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConferenceTest, ConferenceTest::name());
     164             : 
     165             : void
     166          23 : ConferenceTest::setUp()
     167             : {
     168          46 :     auto actors = load_actors_and_wait_for_announcement("actors/alice-bob-carla-davi.yml");
     169          23 :     aliceId = actors["alice"];
     170          23 :     bobId = actors["bob"];
     171          23 :     carlaId = actors["carla"];
     172          23 :     daviId = actors["davi"];
     173             : 
     174          23 :     bobCall.reset();
     175          23 :     carlaCall.reset();
     176          23 :     daviCall.reset();
     177          23 :     confId = {};
     178          23 :     confChanged = false;
     179          23 :     hostRecording = false;
     180          23 : }
     181             : 
     182             : void
     183          23 : ConferenceTest::tearDown()
     184             : {
     185         115 :     wait_for_removal_of({aliceId, bobId, carlaId, daviId});
     186          23 : }
     187             : 
     188             : void
     189          23 : ConferenceTest::registerSignalHandlers()
     190             : {
     191          23 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     192          23 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     193          23 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     194          23 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     195          23 :     auto aliceUri = aliceAccount->getUsername();
     196          23 :     auto bobUri = bobAccount->getUsername();
     197          23 :     auto carlaUri = carlaAccount->getUsername();
     198          23 :     auto daviUri = daviAccount->getUsername();
     199             : 
     200          23 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     201             :     // Watch signals
     202          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     203          58 :         [=](const std::string& accountId,
     204             :             const std::string& callId,
     205             :             const std::string&,
     206             :             const std::vector<std::map<std::string, std::string>>&) {
     207          58 :             if (accountId == bobId) {
     208          23 :                 bobCall.callId = callId;
     209          35 :             } else if (accountId == carlaId) {
     210          23 :                 carlaCall.callId = callId;
     211          12 :             } else if (accountId == daviId) {
     212          12 :                 daviCall.callId = callId;
     213             :             }
     214          58 :             cv.notify_one();
     215          58 :         }));
     216          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
     217         621 :         [=](const std::string& accountId,
     218             :             const std::string& callId,
     219             :             const std::string& state,
     220             :             signed) {
     221         621 :             if (accountId == aliceId) {
     222         281 :                 auto details = libjami::getCallDetails(aliceId, callId);
     223         281 :                 if (details["PEER_NUMBER"].find(bobUri) != std::string::npos)
     224         112 :                     bobCall.hostState = state;
     225         169 :                 else if (details["PEER_NUMBER"].find(carlaUri) != std::string::npos)
     226         109 :                     carlaCall.hostState = state;
     227          60 :                 else if (details["PEER_NUMBER"].find(daviUri) != std::string::npos)
     228          60 :                     daviCall.hostState = state;
     229         621 :             } else if (bobCall.callId == callId)
     230          67 :                 bobCall.state = state;
     231         273 :             else if (carlaCall.callId == callId)
     232          63 :                 carlaCall.state = state;
     233         210 :             else if (daviCall.callId == callId)
     234          36 :                 daviCall.state = state;
     235         621 :             cv.notify_one();
     236         621 :         }));
     237          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::ConferenceCreated>(
     238          23 :         [=](const std::string&, const std::string& conferenceId) {
     239          23 :             confId = conferenceId;
     240          23 :             cv.notify_one();
     241          23 :         }));
     242          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::ConferenceRemoved>(
     243          22 :         [=](const std::string&, const std::string& conferenceId) {
     244          22 :             if (confId == conferenceId)
     245          22 :                 confId = "";
     246          22 :             cv.notify_one();
     247          22 :         }));
     248          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::ConferenceChanged>(
     249          89 :         [=](const std::string&, const std::string& conferenceId, const std::string&) {
     250          89 :             if (confId == conferenceId)
     251          68 :                 confChanged = true;
     252          89 :             cv.notify_one();
     253          89 :         }));
     254          23 :     confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::OnConferenceInfosUpdated>(
     255         256 :         [=](const std::string&,
     256             :             const std::vector<std::map<std::string, std::string>> participantsInfos) {
     257         256 :             std::lock_guard lock(pInfosMtx_);
     258         256 :             pInfos_ = participantsInfos;
     259        1040 :             for (const auto& infos : participantsInfos) {
     260         784 :                 if (infos.at("uri").find(bobUri) != std::string::npos) {
     261         254 :                     bobCall.active = infos.at("active") == "true";
     262         254 :                     bobCall.recording = infos.at("recording") == "true";
     263         254 :                     bobCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
     264         254 :                     bobCall.raisedHand = infos.at("handRaised") == "true";
     265         254 :                     bobCall.device = infos.at("device");
     266         254 :                     bobCall.streamId = infos.at("sinkId");
     267         530 :                 } else if (infos.at("uri").find(carlaUri) != std::string::npos) {
     268         198 :                     carlaCall.active = infos.at("active") == "true";
     269         198 :                     carlaCall.recording = infos.at("recording") == "true";
     270         198 :                     carlaCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
     271         198 :                     carlaCall.raisedHand = infos.at("handRaised") == "true";
     272         198 :                     carlaCall.device = infos.at("device");
     273         198 :                     carlaCall.streamId = infos.at("sinkId");
     274         332 :                 } else if (infos.at("uri").find(daviUri) != std::string::npos) {
     275          74 :                     daviCall.active = infos.at("active") == "true";
     276          74 :                     daviCall.recording = infos.at("recording") == "true";
     277          74 :                     daviCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
     278          74 :                     daviCall.raisedHand = infos.at("handRaised") == "true";
     279          74 :                     daviCall.device = infos.at("device");
     280          74 :                     daviCall.streamId = infos.at("sinkId");
     281         258 :                 } else if (infos.at("uri").find(aliceUri) != std::string::npos) {
     282         175 :                     hostRecording = infos.at("recording") == "true";
     283             :                 }
     284             :             }
     285         256 :             cv.notify_one();
     286         256 :         }));
     287             : 
     288          23 :     libjami::registerSignalHandlers(confHandlers);
     289          23 : }
     290             : 
     291             : void
     292          21 : ConferenceTest::startConference(bool audioOnly, bool addDavi)
     293             : {
     294          21 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     295          21 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     296          21 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     297          21 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     298          21 :     auto bobUri = bobAccount->getUsername();
     299          21 :     auto carlaUri = carlaAccount->getUsername();
     300          21 :     auto daviUri = daviAccount->getUsername();
     301             : 
     302          21 :     std::vector<std::map<std::string, std::string>> mediaList;
     303          21 :     if (audioOnly) {
     304             :         std::map<std::string, std::string> mediaAttribute
     305             :             = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     306             :                 libjami::Media::MediaAttributeValue::AUDIO},
     307             :                {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
     308             :                {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
     309             :                {libjami::Media::MediaAttributeKey::SOURCE, ""},
     310          21 :                {libjami::Media::MediaAttributeKey::LABEL, "audio_0"}};
     311           3 :         mediaList.emplace_back(mediaAttribute);
     312           3 :     }
     313             : 
     314          21 :     JAMI_INFO("Start call between Alice and Bob");
     315          21 :     auto call1 = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
     316         147 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
     317          21 :     Manager::instance().answerCall(bobId, bobCall.callId);
     318          64 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.hostState == "CURRENT"; }));
     319             : 
     320          21 :     JAMI_INFO("Start call between Alice and Carla");
     321          21 :     auto call2 = libjami::placeCallWithMedia(aliceId, carlaUri, mediaList);
     322         147 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !carlaCall.callId.empty(); }));
     323          21 :     Manager::instance().answerCall(carlaId, carlaCall.callId);
     324          63 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return carlaCall.hostState == "CURRENT"; }));
     325             : 
     326          21 :     JAMI_INFO("Start conference");
     327          21 :     confChanged = false;
     328          21 :     Manager::instance().joinParticipant(aliceId, call1, aliceId, call2);
     329             :     // ConfChanged is the signal emitted when the 2 calls will be added to the conference
     330             :     // Also, wait that participants appears in conf info to get all good informations
     331         117 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
     332             :         return !confId.empty() && confChanged && !carlaCall.device.empty()
     333             :                && !bobCall.device.empty();
     334             :     }));
     335             : 
     336          21 :     if (addDavi) {
     337           7 :         JAMI_INFO("Start call between Alice and Davi");
     338           7 :         auto call1 = libjami::placeCallWithMedia(aliceId, daviUri, mediaList);
     339          57 :         CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     340           7 :         Manager::instance().answerCall(daviId, daviCall.callId);
     341          21 :         CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     342           7 :         Manager::instance().addParticipant(aliceId, call1, aliceId, confId);
     343          21 :         CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     344           7 :     }
     345          21 : }
     346             : 
     347             : void
     348          19 : ConferenceTest::hangupConference()
     349             : {
     350          19 :     JAMI_INFO("Stop conference");
     351          19 :     Manager::instance().hangupConference(aliceId, confId);
     352          51 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
     353             :         return bobCall.state == "OVER" && carlaCall.state == "OVER" && confId.empty();
     354             :     }));
     355          40 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
     356             :         return daviCall.callId.empty() ? true : daviCall.state == "OVER";
     357             :     }));
     358          19 : }
     359             : 
     360             : void
     361           1 : ConferenceTest::testGetConference()
     362             : {
     363           1 :     registerSignalHandlers();
     364             : 
     365           1 :     CPPUNIT_ASSERT(libjami::getConferenceList(aliceId).size() == 0);
     366             : 
     367           1 :     startConference();
     368             : 
     369           1 :     CPPUNIT_ASSERT(libjami::getConferenceList(aliceId).size() == 1);
     370           1 :     CPPUNIT_ASSERT(libjami::getConferenceList(aliceId)[0] == confId);
     371             : 
     372           1 :     hangupConference();
     373             : 
     374           1 :     CPPUNIT_ASSERT(libjami::getConferenceList(aliceId).size() == 0);
     375             : 
     376           1 :     libjami::unregisterSignalHandlers();
     377           1 : }
     378             : 
     379             : void
     380           1 : ConferenceTest::testModeratorMuteUpdateParticipantsInfos()
     381             : {
     382           1 :     registerSignalHandlers();
     383             : 
     384           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     385           1 :     auto bobUri = bobAccount->getUsername();
     386             : 
     387           1 :     startConference();
     388             : 
     389           1 :     JAMI_INFO("Play with mute from the moderator");
     390           1 :     libjami::muteStream(aliceId, confId, bobUri, bobCall.device, bobCall.streamId, true);
     391           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.moderatorMuted.load(); }));
     392             : 
     393           1 :     libjami::muteStream(aliceId, confId, bobUri, bobCall.device, bobCall.streamId, false);
     394           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !bobCall.moderatorMuted.load(); }));
     395             : 
     396           1 :     hangupConference();
     397             : 
     398           1 :     libjami::unregisterSignalHandlers();
     399           1 : }
     400             : 
     401             : void
     402           1 : ConferenceTest::testUnauthorizedMute()
     403             : {
     404           1 :     registerSignalHandlers();
     405             : 
     406           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     407           1 :     auto bobUri = bobAccount->getUsername();
     408             : 
     409           1 :     startConference();
     410             : 
     411           1 :     JAMI_INFO("Play with mute from unauthorized");
     412           1 :     libjami::muteStream(carlaId, confId, bobUri, bobCall.device, bobCall.streamId, true);
     413           5 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 15s, [&] { return bobCall.moderatorMuted.load(); }));
     414             : 
     415           1 :     hangupConference();
     416             : 
     417           1 :     libjami::unregisterSignalHandlers();
     418           1 : }
     419             : 
     420             : void
     421           1 : ConferenceTest::testAudioVideoMutedStates()
     422             : {
     423           1 :     registerSignalHandlers();
     424             : 
     425           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     426           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     427           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     428           1 :     auto bobUri = bobAccount->getUsername();
     429           1 :     auto carlaUri = carlaAccount->getUsername();
     430             : 
     431           1 :     JAMI_INFO("Start call between Alice and Bob");
     432           1 :     auto call1Id = libjami::placeCallWithMedia(aliceId, bobUri, {});
     433           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
     434           1 :     Manager::instance().answerCall(bobId, bobCall.callId);
     435           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.hostState == "CURRENT"; }));
     436           1 :     auto call1 = aliceAccount->getCall(call1Id);
     437           1 :     call1->muteMedia(libjami::Media::MediaAttributeValue::AUDIO, true);
     438           1 :     call1->muteMedia(libjami::Media::MediaAttributeValue::VIDEO, true);
     439             : 
     440           1 :     JAMI_INFO("Start call between Alice and Carla");
     441           1 :     auto call2Id = libjami::placeCallWithMedia(aliceId, carlaUri, {});
     442           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !carlaCall.callId.empty(); }));
     443           1 :     Manager::instance().answerCall(carlaId, carlaCall.callId);
     444           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return carlaCall.hostState == "CURRENT"; }));
     445             : 
     446           1 :     auto call2 = aliceAccount->getCall(call2Id);
     447           1 :     call2->muteMedia(libjami::Media::MediaAttributeValue::AUDIO, true);
     448           1 :     call2->muteMedia(libjami::Media::MediaAttributeValue::VIDEO, true);
     449             : 
     450           1 :     JAMI_INFO("Start conference");
     451           1 :     Manager::instance().joinParticipant(aliceId, call1Id, aliceId, call2Id);
     452           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !confId.empty(); }));
     453             : 
     454           1 :     auto conf = aliceAccount->getConference(confId);
     455           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     456             :         return conf->isMediaSourceMuted(jami::MediaType::MEDIA_AUDIO);
     457             :     }));
     458           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     459             :         return conf->isMediaSourceMuted(jami::MediaType::MEDIA_VIDEO);
     460             :     }));
     461             : 
     462           1 :     hangupConference();
     463             : 
     464           1 :     libjami::unregisterSignalHandlers();
     465           1 : }
     466             : 
     467             : void
     468           1 : ConferenceTest::testMuteStatusAfterAdd()
     469             : {
     470           1 :     registerSignalHandlers();
     471             : 
     472           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     473           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     474           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     475           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     476           1 :     auto bobUri = bobAccount->getUsername();
     477           1 :     auto carlaUri = carlaAccount->getUsername();
     478           1 :     auto daviUri = daviAccount->getUsername();
     479             : 
     480           1 :     std::vector<std::map<std::string, std::string>> mediaList;
     481             :     std::map<std::string, std::string> mediaAttribute
     482             :         = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     483             :             libjami::Media::MediaAttributeValue::AUDIO},
     484             :            {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
     485             :            {libjami::Media::MediaAttributeKey::MUTED, TRUE_STR},
     486             :            {libjami::Media::MediaAttributeKey::SOURCE, ""},
     487           7 :            {libjami::Media::MediaAttributeKey::LABEL, "audio_0"}};
     488           1 :     mediaList.emplace_back(mediaAttribute);
     489             : 
     490           1 :     JAMI_INFO("Start call between Alice and Bob");
     491           1 :     auto call1 = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
     492           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
     493           1 :     Manager::instance().answerCall(bobId, bobCall.callId);
     494           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.hostState == "CURRENT"; }));
     495             : 
     496           1 :     JAMI_INFO("Start call between Alice and Carla");
     497           1 :     auto call2 = libjami::placeCallWithMedia(aliceId, carlaUri, mediaList);
     498           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !carlaCall.callId.empty(); }));
     499           1 :     Manager::instance().answerCall(carlaId, carlaCall.callId);
     500           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return carlaCall.hostState == "CURRENT"; }));
     501             : 
     502           1 :     JAMI_INFO("Start conference");
     503           1 :     Manager::instance().joinParticipant(aliceId, call1, aliceId, call2);
     504           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !confId.empty(); }));
     505             : 
     506           1 :     JAMI_INFO("Add Davi");
     507           1 :     auto call3 = libjami::placeCallWithMedia(aliceId, daviUri, {});
     508          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     509           1 :     Manager::instance().answerCall(daviId, daviCall.callId);
     510           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     511           1 :     Manager::instance().addParticipant(aliceId, call3, aliceId, confId);
     512           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     513             : 
     514           1 :     auto aliceConf = aliceAccount->getConference(confId);
     515           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     516             :         return aliceConf->isMediaSourceMuted(jami::MediaType::MEDIA_AUDIO);
     517             :     }));
     518             : 
     519           1 :     hangupConference();
     520             : 
     521           1 :     libjami::unregisterSignalHandlers();
     522           1 : }
     523             : 
     524             : void
     525           1 : ConferenceTest::testCreateParticipantsSinks()
     526             : {
     527           1 :     registerSignalHandlers();
     528             : 
     529           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     530           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     531           1 :     auto bobUri = bobAccount->getUsername();
     532           1 :     auto carlaUri = carlaAccount->getUsername();
     533             : 
     534           1 :     startConference();
     535             : 
     536           1 :     auto expectedNumberOfParticipants = 3u;
     537             : 
     538             :     // Check participants number
     539           2 :     CPPUNIT_ASSERT(
     540             :         cv.wait_for(lk, 30s, [&] { return pInfos_.size() == expectedNumberOfParticipants; }));
     541             : 
     542           1 :     if (not jami::getVideoDeviceMonitor().getDeviceList().empty()) {
     543           0 :         JAMI_INFO() << "Check sinks if video device available.";
     544           0 :         std::lock_guard lock(pInfosMtx_);
     545           0 :         for (auto& info : pInfos_) {
     546           0 :             auto uri = string_remove_suffix(info["uri"], '@');
     547           0 :             if (uri == bobUri) {
     548           0 :                 CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     549             :                     return Manager::instance().getSinkClient(info["sinkId"]) != nullptr;
     550             :                 }));
     551           0 :             } else if (uri == carlaUri) {
     552           0 :                 CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     553             :                     return Manager::instance().getSinkClient(info["sinkId"]) != nullptr;
     554             :                 }));
     555             :             }
     556             :         }
     557           0 :     } else {
     558           1 :         JAMI_INFO() << "Check sinks if no video device available.";
     559           1 :         std::lock_guard lock(pInfosMtx_);
     560           4 :         for (auto& info : pInfos_) {
     561           3 :             auto uri = string_remove_suffix(info["uri"], '@');
     562           3 :             if (uri == bobUri) {
     563           2 :                 CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     564             :                     return Manager::instance().getSinkClient(info["sinkId"]) == nullptr;
     565             :                 }));
     566           2 :             } else if (uri == carlaUri) {
     567           2 :                 CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     568             :                     return Manager::instance().getSinkClient(info["sinkId"]) == nullptr;
     569             :                 }));
     570             :             }
     571             :         }
     572           1 :     }
     573             : 
     574           1 :     hangupConference();
     575             : 
     576           1 :     libjami::unregisterSignalHandlers();
     577           1 : }
     578             : 
     579             : void
     580           1 : ConferenceTest::testMuteStatusAfterRemove()
     581             : {
     582           1 :     registerSignalHandlers();
     583             : 
     584           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     585           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     586           1 :     auto bobUri = bobAccount->getUsername();
     587           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     588           1 :     auto daviUri = daviAccount->getUsername();
     589             : 
     590           1 :     startConference(false, true);
     591             : 
     592           1 :     libjami::muteStream(aliceId, confId, daviUri, daviCall.device, daviCall.streamId, true);
     593           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return daviCall.moderatorMuted.load(); }));
     594             : 
     595           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     596           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     597           1 :     daviCall.reset();
     598             : 
     599           1 :     auto call2 = libjami::placeCallWithMedia(aliceId, daviUri, {});
     600           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     601           1 :     Manager::instance().answerCall(daviId, daviCall.callId);
     602           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     603           1 :     Manager::instance().addParticipant(aliceId, call2, aliceId, confId);
     604           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     605             : 
     606           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !daviCall.moderatorMuted.load(); }));
     607             : 
     608           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     609           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     610           1 :     hangupConference();
     611             : 
     612           1 :     libjami::unregisterSignalHandlers();
     613           1 : }
     614             : 
     615             : void
     616           1 : ConferenceTest::testActiveStatusAfterRemove()
     617             : {
     618           1 :     registerSignalHandlers();
     619             : 
     620           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     621           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     622           1 :     auto bobUri = bobAccount->getUsername();
     623           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     624           1 :     auto daviUri = daviAccount->getUsername();
     625             : 
     626           1 :     startConference(false, true);
     627             : 
     628           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     629           1 :     defaultAudio.label_ = "audio_0";
     630           1 :     defaultAudio.enabled_ = true;
     631             : 
     632           1 :     libjami::setActiveStream(aliceId, confId, daviUri, daviCall.device, daviCall.streamId, true);
     633           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return daviCall.active.load(); }));
     634             : 
     635           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     636           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     637           1 :     daviCall.reset();
     638             : 
     639           1 :     auto call2 = libjami::placeCallWithMedia(aliceId,
     640             :                                              daviUri,
     641           3 :                                              MediaAttribute::mediaAttributesToMediaMaps(
     642           1 :                                                  {defaultAudio}));
     643           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     644           1 :     Manager::instance().answerCall(daviId, daviCall.callId);
     645           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     646           1 :     Manager::instance().addParticipant(aliceId, call2, aliceId, confId);
     647           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     648             : 
     649           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !daviCall.active.load(); }));
     650             : 
     651           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     652           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     653           1 :     hangupConference();
     654             : 
     655           1 :     libjami::unregisterSignalHandlers();
     656           1 : }
     657             : 
     658             : void
     659           1 : ConferenceTest::testHandsUp()
     660             : {
     661           1 :     registerSignalHandlers();
     662             : 
     663           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     664           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     665           1 :     auto bobUri = bobAccount->getUsername();
     666           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     667           1 :     auto carlaUri = carlaAccount->getUsername();
     668           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     669           1 :     auto daviUri = daviAccount->getUsername();
     670             : 
     671           1 :     startConference(false, true);
     672             : 
     673           1 :     JAMI_INFO("Play with raise hand");
     674           1 :     libjami::raiseHand(bobId, bobCall.callId, bobUri, bobCall.device, true);
     675           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.raisedHand.load(); }));
     676             : 
     677           1 :     libjami::raiseHand(bobId, bobCall.callId, bobUri, bobCall.device, false);
     678           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !bobCall.raisedHand.load(); }));
     679             : 
     680             :     // Remove davi from moderators
     681           1 :     libjami::setModerator(aliceId, confId, daviUri, false);
     682             : 
     683             :     // Test to raise hand
     684           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     685           1 :     libjami::raiseHand(daviId, daviCall.callId, daviUri, daviCall.device, true);
     686          11 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return daviCall.raisedHand.load(); }));
     687             : 
     688             :     // Test to raise hand for another one (should fail)
     689           1 :     libjami::raiseHand(bobId, bobCall.callId, carlaUri, carlaCall.device, true);
     690           6 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 5s, [&] { return carlaCall.raisedHand.load(); }));
     691             : 
     692             :     // However, a moderator should be able to lower the hand (but not a non moderator)
     693           1 :     libjami::raiseHand(carlaId, carlaCall.callId, carlaUri, carlaCall.device, true);
     694           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return carlaCall.raisedHand.load(); }));
     695             : 
     696           1 :     libjami::raiseHand(daviId, carlaCall.callId, carlaUri, carlaCall.device, false);
     697           6 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 5s, [&] { return !carlaCall.raisedHand.load(); }));
     698             : 
     699           1 :     libjami::raiseHand(bobId, bobCall.callId, carlaUri, carlaCall.device, false);
     700           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !carlaCall.raisedHand.load(); }));
     701             : 
     702           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     703           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     704           1 :     daviCall.reset();
     705             : 
     706           1 :     auto call2 = libjami::placeCallWithMedia(aliceId, daviUri, {});
     707           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     708           1 :     Manager::instance().answerCall(daviId, daviCall.callId);
     709           8 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     710           1 :     Manager::instance().addParticipant(aliceId, call2, aliceId, confId);
     711           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.device.empty(); }));
     712             : 
     713           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !daviCall.raisedHand.load(); }));
     714             : 
     715           1 :     Manager::instance().hangupCall(daviId, daviCall.callId);
     716           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.state == "OVER"; }));
     717           1 :     hangupConference();
     718             : 
     719           1 :     libjami::unregisterSignalHandlers();
     720           1 : }
     721             : 
     722             : void
     723           1 : ConferenceTest::testPeerLeaveConference()
     724             : {
     725           1 :     registerSignalHandlers();
     726             : 
     727           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     728           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     729           1 :     auto bobUri = bobAccount->getUsername();
     730             : 
     731           1 :     startConference();
     732           1 :     Manager::instance().hangupCall(bobId, bobCall.callId);
     733           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER" && confId.empty(); }));
     734             : 
     735           1 :     libjami::unregisterSignalHandlers();
     736           1 : }
     737             : 
     738             : void
     739           1 : ConferenceTest::testJoinCallFromOtherAccount()
     740             : {
     741           1 :     registerSignalHandlers();
     742             : 
     743           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     744           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     745           1 :     auto bobUri = bobAccount->getUsername();
     746           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     747           1 :     auto daviUri = daviAccount->getUsername();
     748             : 
     749           1 :     startConference();
     750             : 
     751           1 :     JAMI_INFO("Play with raise hand");
     752           1 :     libjami::raiseHand(aliceId, confId, bobUri, bobCall.device, true);
     753           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.raisedHand.load(); }));
     754             : 
     755           1 :     libjami::raiseHand(aliceId, confId, bobUri, bobCall.device, false);
     756           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !bobCall.raisedHand.load(); }));
     757             : 
     758           1 :     JAMI_INFO("Start call between Alice and Davi");
     759           1 :     auto call1 = libjami::placeCallWithMedia(aliceId, daviUri, {});
     760          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
     761           1 :     Manager::instance().answerCall(daviId, daviCall.callId);
     762           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return daviCall.hostState == "CURRENT"; }));
     763           1 :     CPPUNIT_ASSERT(Manager::instance().addParticipant(daviId, daviCall.callId, aliceId, confId));
     764           1 :     hangupConference();
     765             : 
     766           1 :     libjami::unregisterSignalHandlers();
     767           1 : }
     768             : 
     769             : void
     770           1 : ConferenceTest::testDevices()
     771             : {
     772           1 :     registerSignalHandlers();
     773             : 
     774           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     775           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     776           1 :     auto bobDevice = std::string(bobAccount->currentDeviceId());
     777           1 :     auto carlaDevice = std::string(carlaAccount->currentDeviceId());
     778             : 
     779           1 :     startConference();
     780             : 
     781           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] {
     782             :         return bobCall.device == bobDevice && carlaDevice == carlaCall.device;
     783             :     }));
     784             : 
     785           1 :     hangupConference();
     786             : 
     787           1 :     libjami::unregisterSignalHandlers();
     788           1 : }
     789             : 
     790             : void
     791           1 : ConferenceTest::testUnauthorizedSetActive()
     792             : {
     793           1 :     registerSignalHandlers();
     794             : 
     795           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     796           1 :     auto bobUri = bobAccount->getUsername();
     797           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     798           1 :     auto carlaUri = carlaAccount->getUsername();
     799             : 
     800           1 :     startConference();
     801             : 
     802           1 :     libjami::setActiveStream(carlaId, confId, bobUri, bobCall.device, bobCall.streamId, true);
     803           5 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 15s, [&] { return bobCall.active.load(); }));
     804             : 
     805           1 :     hangupConference();
     806             : 
     807           1 :     libjami::unregisterSignalHandlers();
     808           1 : }
     809             : 
     810             : void
     811           1 : ConferenceTest::testHangup()
     812             : {
     813           1 :     registerSignalHandlers();
     814             : 
     815           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     816           1 :     auto bobUri = bobAccount->getUsername();
     817           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     818           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
     819           1 :     auto daviUri = daviAccount->getUsername();
     820             : 
     821           1 :     startConference(false, true);
     822             : 
     823           1 :     libjami::hangupParticipant(carlaId, confId, daviUri, daviCall.device); // Unauthorized
     824           6 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&] { return daviCall.state == "OVER"; }));
     825           1 :     libjami::hangupParticipant(aliceId, confId, daviUri, daviCall.device);
     826           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return daviCall.state == "OVER"; }));
     827             : 
     828           1 :     hangupConference();
     829             : 
     830           1 :     libjami::unregisterSignalHandlers();
     831           1 : }
     832             : 
     833             : void
     834           1 : ConferenceTest::testIsConferenceParticipant()
     835             : {
     836           1 :     registerSignalHandlers();
     837             : 
     838           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     839           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     840           1 :     auto bobUri = bobAccount->getUsername();
     841             : 
     842           1 :     startConference();
     843             : 
     844             :     // is Conference participant should be true for Carla
     845           1 :     auto participants = aliceAccount->getConference(confId)->getParticipantList();
     846           1 :     CPPUNIT_ASSERT(participants.size() == 2);
     847           1 :     auto call1 = *participants.begin();
     848           1 :     auto call2 = *participants.rbegin();
     849           1 :     CPPUNIT_ASSERT(aliceAccount->getCall(call1)->isConferenceParticipant());
     850           1 :     CPPUNIT_ASSERT(aliceAccount->getCall(call2)->isConferenceParticipant());
     851             : 
     852             :     // hangup bob will stop the conference
     853           1 :     Manager::instance().hangupCall(aliceId, call1);
     854           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return confId.empty(); }));
     855           1 :     CPPUNIT_ASSERT(!aliceAccount->getCall(call2)->isConferenceParticipant());
     856           1 :     Manager::instance().hangupCall(aliceId, call2);
     857             : 
     858           1 :     libjami::unregisterSignalHandlers();
     859           1 : }
     860             : 
     861             : void
     862           1 : ConferenceTest::testHostAddRmSecondVideo()
     863             : {
     864           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     865           1 :     auto aliceUri = aliceAccount->getUsername();
     866           1 :     registerSignalHandlers();
     867             : 
     868           1 :     startConference();
     869             : 
     870             :     // Alice adds new media
     871           1 :     pInfos_.clear();
     872             :     std::vector<std::map<std::string, std::string>> mediaList
     873             :         = {{{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     874             :              libjami::Media::MediaAttributeValue::AUDIO},
     875             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     876             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     877             :             {libjami::Media::MediaAttributeKey::SOURCE, ""},
     878             :             {libjami::Media::MediaAttributeKey::LABEL, "audio_0"}},
     879             :            {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     880             :              libjami::Media::MediaAttributeValue::VIDEO},
     881             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     882             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     883             :             {libjami::Media::MediaAttributeKey::SOURCE, "bar"},
     884             :             {libjami::Media::MediaAttributeKey::LABEL, "video_0"}},
     885             :            {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     886             :              libjami::Media::MediaAttributeValue::VIDEO},
     887             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     888             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     889             :             {libjami::Media::MediaAttributeKey::SOURCE, "foo"},
     890          23 :             {libjami::Media::MediaAttributeKey::LABEL, "video_1"}}};
     891           1 :     libjami::requestMediaChange(aliceId, confId, mediaList);
     892             : 
     893             :     // Check that alice has two videos attached to the conference
     894           6 :     auto aliceVideos = [&]() {
     895           6 :         int result = 0;
     896           6 :         std::lock_guard lock(pInfosMtx_);
     897          27 :         for (auto i = 0u; i < pInfos_.size(); ++i)
     898          21 :             if (pInfos_[i]["uri"].find(aliceUri) != std::string::npos)
     899           6 :                 result += 1;
     900           6 :         return result;
     901           6 :     };
     902           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceVideos() == 2; }));
     903             : 
     904             :     // Alice removes her second video
     905             :     {
     906           1 :         std::lock_guard lock(pInfosMtx_);
     907           1 :         pInfos_.clear();
     908           1 :     }
     909           1 :     mediaList.pop_back();
     910           1 :     libjami::requestMediaChange(aliceId, confId, mediaList);
     911             : 
     912             :     // Check that alice has ont video attached to the conference
     913           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceVideos() == 1; }));
     914             : 
     915           1 :     hangupConference();
     916             : 
     917           1 :     libjami::unregisterSignalHandlers();
     918           1 : }
     919             : 
     920             : void
     921           1 : ConferenceTest::testAudioConferenceConfInfo()
     922             : {
     923           1 :     registerSignalHandlers();
     924           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     925           1 :     auto aliceUri = aliceAccount->getUsername();
     926             : 
     927           1 :     startConference(true);
     928             : 
     929             :     // Check that alice's video is muted
     930           2 :     auto aliceVideoMuted = [&]() {
     931           2 :         int result = 0;
     932           2 :         std::lock_guard lock(pInfosMtx_);
     933           8 :         for (auto i = 0u; i < pInfos_.size(); ++i) {
     934          18 :             if (pInfos_[i]["uri"].find(aliceUri) != std::string::npos
     935          18 :                 && pInfos_[i]["videoMuted"] == "true" && pInfos_[i]["sinkId"] == "host_video_0")
     936           1 :                 result += 1;
     937             :         }
     938           2 :         return result;
     939           2 :     };
     940           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceVideoMuted() == 1; }));
     941             : 
     942           1 :     libjami::unregisterSignalHandlers();
     943           1 : }
     944             : 
     945             : void
     946           1 : ConferenceTest::testParticipantAddRmSecondVideo()
     947             : {
     948           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     949           1 :     auto bobUri = bobAccount->getUsername();
     950           1 :     registerSignalHandlers();
     951             : 
     952           1 :     startConference();
     953             : 
     954             :     // Bob adds new media
     955           1 :     pInfos_.clear();
     956             :     std::vector<std::map<std::string, std::string>> mediaList
     957             :         = {{{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     958             :              libjami::Media::MediaAttributeValue::AUDIO},
     959             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     960             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     961             :             {libjami::Media::MediaAttributeKey::SOURCE, ""},
     962             :             {libjami::Media::MediaAttributeKey::LABEL, "audio_0"}},
     963             :            {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     964             :              libjami::Media::MediaAttributeValue::VIDEO},
     965             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     966             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     967             :             {libjami::Media::MediaAttributeKey::SOURCE, "bar"},
     968             :             {libjami::Media::MediaAttributeKey::LABEL, "video_0"}},
     969             :            {{libjami::Media::MediaAttributeKey::MEDIA_TYPE,
     970             :              libjami::Media::MediaAttributeValue::VIDEO},
     971             :             {libjami::Media::MediaAttributeKey::ENABLED, "true"},
     972             :             {libjami::Media::MediaAttributeKey::MUTED, "false"},
     973             :             {libjami::Media::MediaAttributeKey::SOURCE, "foo"},
     974          23 :             {libjami::Media::MediaAttributeKey::LABEL, "video_1"}}};
     975           1 :     libjami::requestMediaChange(bobId, bobCall.callId, mediaList);
     976             : 
     977             :     // Check that bob has two videos attached to the conference
     978          12 :     auto bobVideos = [&]() {
     979          12 :         std::lock_guard lock(pInfosMtx_);
     980          12 :         int result = 0;
     981          51 :         for (auto i = 0u; i < pInfos_.size(); ++i)
     982          39 :             if (pInfos_[i]["uri"].find(bobUri) != std::string::npos)
     983          15 :                 result += 1;
     984          12 :         return result;
     985          12 :     };
     986          10 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return bobVideos() == 2; }));
     987             : 
     988             :     // Bob removes his second video
     989             :     {
     990           1 :         std::lock_guard lock(pInfosMtx_);
     991           1 :         pInfos_.clear();
     992           1 :     }
     993           1 :     mediaList.pop_back();
     994           1 :     libjami::requestMediaChange(bobId, bobCall.callId, mediaList);
     995             : 
     996             :     // Check that bob has ont video attached to the conference
     997           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return bobVideos() == 1; }));
     998             : 
     999           1 :     hangupConference();
    1000             : 
    1001           1 :     libjami::unregisterSignalHandlers();
    1002           1 : }
    1003             : 
    1004             : void
    1005           1 : ConferenceTest::testPropagateRecording()
    1006             : {
    1007           1 :     registerSignalHandlers();
    1008             : 
    1009           1 :     startConference();
    1010             : 
    1011           1 :     JAMI_INFO("Play with recording state");
    1012           1 :     libjami::toggleRecording(bobId, bobCall.callId);
    1013           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.recording.load(); }));
    1014             : 
    1015           1 :     libjami::toggleRecording(bobId, bobCall.callId);
    1016           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !bobCall.recording.load(); }));
    1017             : 
    1018           1 :     libjami::toggleRecording(aliceId, confId);
    1019           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return hostRecording.load(); }));
    1020             : 
    1021           1 :     libjami::toggleRecording(aliceId, confId);
    1022           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !hostRecording.load(); }));
    1023             : 
    1024           1 :     hangupConference();
    1025             : 
    1026           1 :     libjami::unregisterSignalHandlers();
    1027           1 : }
    1028             : 
    1029             : void
    1030           1 : ConferenceTest::testBrokenParticipantAudioAndVideo()
    1031             : {
    1032           1 :     registerSignalHandlers();
    1033             : 
    1034             :     // Start conference with four participants
    1035           1 :     startConference(false, true);
    1036           1 :     auto expectedNumberOfParticipants = 4u;
    1037             : 
    1038             :     // Check participants number
    1039           2 :     CPPUNIT_ASSERT(
    1040             :         cv.wait_for(lk, 30s, [&] { return pInfos_.size() == expectedNumberOfParticipants; }));
    1041             : 
    1042             :     // Crash participant
    1043           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
    1044           1 :     auto call2Crash = std::dynamic_pointer_cast<SIPCall>(daviAccount->getCall(daviCall.callId));
    1045           1 :     pjsip_transport_shutdown(call2Crash->getTransport()->get());
    1046             : 
    1047             :     // Check participants number
    1048             :     // It should have one less participant than in the conference start
    1049           8 :     CPPUNIT_ASSERT(
    1050             :         cv.wait_for(lk, 30s, [&] { return expectedNumberOfParticipants - 1 == pInfos_.size(); }));
    1051             : 
    1052           1 :     hangupConference();
    1053             : 
    1054           1 :     libjami::unregisterSignalHandlers();
    1055           1 : }
    1056             : 
    1057             : void
    1058           1 : ConferenceTest::testBrokenParticipantAudioOnly()
    1059             : {
    1060           1 :     registerSignalHandlers();
    1061             : 
    1062             :     // Start conference with four participants
    1063           1 :     startConference(true, true);
    1064           1 :     auto expectedNumberOfParticipants = 4u;
    1065             : 
    1066             :     // Check participants number
    1067           2 :     CPPUNIT_ASSERT(
    1068             :         cv.wait_for(lk, 30s, [&] { return pInfos_.size() == expectedNumberOfParticipants; }));
    1069             : 
    1070             :     // Crash participant
    1071           1 :     auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
    1072           1 :     auto call2Crash = std::dynamic_pointer_cast<SIPCall>(daviAccount->getCall(daviCall.callId));
    1073           1 :     pjsip_transport_shutdown(call2Crash->getTransport()->get());
    1074             : 
    1075             :     // Check participants number
    1076             :     // It should have one less participant than in the conference start
    1077           7 :     CPPUNIT_ASSERT(
    1078             :         cv.wait_for(lk, 30s, [&] { return expectedNumberOfParticipants - 1 == pInfos_.size(); }));
    1079             : 
    1080           1 :     hangupConference();
    1081           1 :     libjami::unregisterSignalHandlers();
    1082           1 : }
    1083             : 
    1084             : void
    1085           1 : ConferenceTest::testAudioOnlyLeaveLayout()
    1086             : {
    1087           1 :     registerSignalHandlers();
    1088             : 
    1089             :     // Start conference with four participants
    1090           1 :     startConference(true, true);
    1091           1 :     auto expectedNumberOfParticipants = 4u;
    1092             : 
    1093             :     // Check participants number
    1094           2 :     CPPUNIT_ASSERT(
    1095             :         cv.wait_for(lk, 30s, [&] { return pInfos_.size() == expectedNumberOfParticipants; }));
    1096             : 
    1097             :     // Carla Leave
    1098           1 :     Manager::instance().hangupCall(carlaId, carlaCall.callId);
    1099             : 
    1100             :     // Check participants number
    1101             :     // It should have one less participant than in the conference start
    1102           4 :     CPPUNIT_ASSERT(
    1103             :         cv.wait_for(lk, 30s, [&] { return expectedNumberOfParticipants - 1 == pInfos_.size(); }));
    1104             : 
    1105           1 :     hangupConference();
    1106           1 :     libjami::unregisterSignalHandlers();
    1107           1 : }
    1108             : 
    1109             : void
    1110           1 : ConferenceTest::testRemoveConferenceInOneOne()
    1111             : {
    1112           1 :     registerSignalHandlers();
    1113           1 :     startConference();
    1114             :     // Here it's 1:1 calls we merged, so we can close the conference
    1115           1 :     JAMI_INFO("Hangup Bob");
    1116           1 :     Manager::instance().hangupCall(bobId, bobCall.callId);
    1117           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return confId.empty() && bobCall.state == "OVER"; }));
    1118           1 :     Manager::instance().hangupCall(carlaId, carlaCall.callId);
    1119           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaCall.state == "OVER"; }));
    1120           1 :     libjami::unregisterSignalHandlers();
    1121           1 : }
    1122             : 
    1123             : } // namespace test
    1124             : } // namespace jami
    1125             : 
    1126           1 : RING_TEST_RUNNER(jami::test::ConferenceTest::name())

Generated by: LCOV version 1.14