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 617 : [=](const std::string& accountId,
217 : const std::string& callId,
218 : const std::string& state,
219 : signed) {
220 617 : if (accountId == aliceId) {
221 275 : auto details = libjami::getCallDetails(aliceId, callId);
222 275 : if (details["PEER_NUMBER"].find(bobUri) != std::string::npos)
223 107 : bobCall.hostState = state;
224 168 : else if (details["PEER_NUMBER"].find(carlaUri) != std::string::npos)
225 108 : carlaCall.hostState = state;
226 60 : else if (details["PEER_NUMBER"].find(daviUri) != std::string::npos)
227 60 : daviCall.hostState = state;
228 617 : } 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 617 : cv.notify_one();
235 617 : }));
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 88 : [=](const std::string&, const std::string& conferenceId, const std::string&) {
249 88 : if (confId == conferenceId)
250 69 : confChanged = true;
251 88 : cv.notify_one();
252 88 : }));
253 23 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::OnConferenceInfosUpdated>(
254 268 : [=](const std::string&,
255 : const std::vector<std::map<std::string, std::string>> participantsInfos) {
256 268 : std::lock_guard lock(pInfosMtx_);
257 268 : pInfos_ = participantsInfos;
258 1070 : for (const auto& infos : participantsInfos) {
259 802 : if (infos.at("uri").find(bobUri) != std::string::npos) {
260 258 : bobCall.active = infos.at("active") == "true";
261 258 : bobCall.recording = infos.at("recording") == "true";
262 258 : bobCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
263 258 : bobCall.raisedHand = infos.at("handRaised") == "true";
264 258 : bobCall.device = infos.at("device");
265 258 : bobCall.streamId = infos.at("sinkId");
266 544 : } else if (infos.at("uri").find(carlaUri) != std::string::npos) {
267 198 : carlaCall.active = infos.at("active") == "true";
268 198 : carlaCall.recording = infos.at("recording") == "true";
269 198 : carlaCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
270 198 : carlaCall.raisedHand = infos.at("handRaised") == "true";
271 198 : carlaCall.device = infos.at("device");
272 198 : carlaCall.streamId = infos.at("sinkId");
273 346 : } else if (infos.at("uri").find(daviUri) != std::string::npos) {
274 76 : daviCall.active = infos.at("active") == "true";
275 76 : daviCall.recording = infos.at("recording") == "true";
276 76 : daviCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
277 76 : daviCall.raisedHand = infos.at("handRaised") == "true";
278 76 : daviCall.device = infos.at("device");
279 76 : daviCall.streamId = infos.at("sinkId");
280 270 : } else if (infos.at("uri").find(aliceUri) != std::string::npos) {
281 185 : hostRecording = infos.at("recording") == "true";
282 : }
283 : }
284 268 : cv.notify_one();
285 268 : }));
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 9 : 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 3 : 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 3 : 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 5 : 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 5 : CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.raisedHand.load(); }));
675 :
676 1 : libjami::raiseHand(bobId, bobCall.callId, bobUri, bobCall.device, false);
677 3 : 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 11 : 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 7 : 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 4 : 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 6 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !daviCall.callId.empty(); }));
707 1 : Manager::instance().answerCall(daviId, daviCall.callId);
708 10 : 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 3 : 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 9 : 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 5 : 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 4 : 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 6 : 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 8 : 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 9 : 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 6 : 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())
|