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-12-21 08:56:24 Functions: 133 135 98.5 %

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

Generated by: LCOV version 1.14