Line data Source code
1 : /*
2 : * Copyright (C) 2022-2024 Savoir-faire Linux Inc.
3 : * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
4 : *
5 : * This program is free software; you can redistribute it and/or modify
6 : * it under the terms of the GNU General Public License as published by
7 : * the Free Software Foundation; either version 3 of the License, or
8 : * (at your option) any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 : */
18 :
19 : #include <cppunit/TestAssert.h>
20 : #include <cppunit/TestFixture.h>
21 : #include <cppunit/extensions/HelperMacros.h>
22 :
23 : #include <condition_variable>
24 : #include <filesystem>
25 : #include <string>
26 :
27 : #include "../../test_runner.h"
28 : #include "account_const.h"
29 : #include "fileutils.h"
30 : #include "jami.h"
31 : #include "jamidht/jamiaccount.h"
32 : #include "manager.h"
33 : #include "media_const.h"
34 : #include "client/videomanager.h"
35 :
36 : #include "common.h"
37 :
38 : using namespace libjami::Account;
39 : using namespace std::literals::chrono_literals;
40 :
41 : namespace jami {
42 : namespace test {
43 :
44 : struct CallData
45 : {
46 : std::string callId {};
47 : std::string state {};
48 : std::string mediaStatus {};
49 : std::string device {};
50 : std::string hostState {};
51 : bool changeRequested = false;
52 :
53 5 : void reset()
54 : {
55 5 : callId = "";
56 5 : state = "";
57 5 : mediaStatus = "";
58 5 : device = "";
59 5 : hostState = "";
60 5 : }
61 : };
62 :
63 : class RecorderTest : public CppUnit::TestFixture
64 : {
65 : public:
66 5 : RecorderTest()
67 5 : {
68 : // Init daemon
69 5 : libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
70 5 : if (not Manager::instance().initialized)
71 1 : CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
72 5 : }
73 10 : ~RecorderTest() { libjami::fini(); }
74 2 : static std::string name() { return "Recorder"; }
75 : void setUp();
76 : void tearDown();
77 :
78 : std::string aliceId {};
79 : std::string bobId {};
80 : std::string recordDir {};
81 : std::string recordedFile {};
82 : std::string playerId {};
83 : std::shared_ptr<MediaPlayer> player {};
84 : CallData bobCall {};
85 :
86 : std::mutex mtx;
87 : std::unique_lock<std::mutex> lk {mtx};
88 : std::condition_variable cv;
89 :
90 : std::string videoPath = "file://" + std::filesystem::absolute("media/test_video_file.mp4").string();
91 :
92 : private:
93 : void registerSignalHandlers();
94 : void testRecordCall();
95 : void testRecordAudioOnlyCall();
96 : void testRecordCallOnePersonRdv();
97 : void testStopCallWhileRecording();
98 : void testDaemonPreference();
99 :
100 2 : CPPUNIT_TEST_SUITE(RecorderTest);
101 1 : CPPUNIT_TEST(testRecordCall);
102 1 : CPPUNIT_TEST(testRecordAudioOnlyCall);
103 1 : CPPUNIT_TEST(testRecordCallOnePersonRdv);
104 1 : CPPUNIT_TEST(testStopCallWhileRecording);
105 1 : CPPUNIT_TEST(testDaemonPreference);
106 4 : CPPUNIT_TEST_SUITE_END();
107 : };
108 :
109 : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RecorderTest, RecorderTest::name());
110 :
111 : void
112 5 : RecorderTest::setUp()
113 : {
114 : // Generate a temporary directory with a file inside
115 5 : recordDir = "records";
116 5 : dhtnet::fileutils::recursive_mkdir(recordDir.c_str());
117 5 : CPPUNIT_ASSERT(std::filesystem::is_directory(recordDir));
118 :
119 10 : auto actors = load_actors_and_wait_for_announcement("actors/alice-bob.yml");
120 5 : aliceId = actors["alice"];
121 5 : bobId = actors["bob"];
122 5 : bobCall.reset();
123 5 : playerId = jami::createMediaPlayer(videoPath);
124 5 : player = jami::getMediaPlayer(playerId);
125 5 : player->setAutoRestart(true);
126 5 : player->pause(false);
127 :
128 5 : libjami::setRecordPath(recordDir);
129 5 : }
130 :
131 : void
132 5 : RecorderTest::tearDown()
133 : {
134 5 : player.reset();
135 5 : jami::closeMediaPlayer(playerId);
136 5 : libjami::setIsAlwaysRecording(false);
137 5 : dhtnet::fileutils::removeAll(recordDir);
138 :
139 15 : wait_for_removal_of({aliceId, bobId});
140 5 : }
141 :
142 : void
143 5 : RecorderTest::registerSignalHandlers()
144 : {
145 5 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
146 5 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
147 5 : auto bobUri = bobAccount->getUsername();
148 :
149 5 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
150 : // Watch signals
151 5 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
152 5 : [=](const std::string& accountId,
153 : const std::string& callId,
154 : const std::string&,
155 : const std::vector<std::map<std::string, std::string>>&) {
156 5 : if (accountId == bobId) {
157 5 : bobCall.callId = callId;
158 : }
159 5 : cv.notify_one();
160 5 : }));
161 5 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaChangeRequested>(
162 1 : [=](const std::string& accountId,
163 : const std::string& callId,
164 : const std::vector<std::map<std::string, std::string>>&) {
165 1 : if (accountId == bobId && bobCall.callId == callId) {
166 1 : bobCall.changeRequested = true;
167 : }
168 1 : }));
169 5 : confHandlers.insert(
170 10 : libjami::exportable_callback<libjami::CallSignal::StateChange>([=](const std::string& accountId,
171 : const std::string& callId,
172 : const std::string& state,
173 : signed) {
174 55 : if (accountId == aliceId) {
175 25 : auto details = libjami::getCallDetails(aliceId, callId);
176 25 : if (details["PEER_NUMBER"].find(bobUri) != std::string::npos)
177 25 : bobCall.hostState = state;
178 55 : } else if (bobCall.callId == callId)
179 15 : bobCall.state = state;
180 55 : cv.notify_one();
181 55 : }));
182 :
183 5 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
184 19 : [&](const std::string& callId,
185 : const std::string& event,
186 : const std::vector<std::map<std::string, std::string>>&) {
187 19 : if (callId == bobCall.callId)
188 9 : bobCall.mediaStatus = event;
189 19 : cv.notify_one();
190 19 : }));
191 5 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::RecordPlaybackStopped>(
192 9 : [&](const std::string& path) {
193 9 : recordedFile = path;
194 9 : cv.notify_one();
195 9 : }));
196 5 : libjami::registerSignalHandlers(confHandlers);
197 5 : }
198 :
199 : void
200 1 : RecorderTest::testRecordCall()
201 : {
202 1 : JAMI_INFO("Start testRecordCall");
203 1 : registerSignalHandlers();
204 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
205 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
206 1 : auto bobUri = bobAccount->getUsername();
207 :
208 1 : JAMI_INFO("Start call between Alice and Bob");
209 1 : std::vector<std::map<std::string, std::string>> mediaList;
210 : std::map<std::string, std::string> mediaAttributeA
211 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::AUDIO},
212 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
213 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
214 : {libjami::Media::MediaAttributeKey::LABEL, "audio_0"},
215 7 : {libjami::Media::MediaAttributeKey::SOURCE, ""}};
216 : std::map<std::string, std::string> mediaAttributeV
217 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::VIDEO},
218 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
219 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
220 : {libjami::Media::MediaAttributeKey::LABEL, "video_0"},
221 7 : {libjami::Media::MediaAttributeKey::SOURCE, videoPath}};
222 1 : mediaList.emplace_back(mediaAttributeA);
223 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
224 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
225 1 : libjami::acceptWithMedia(bobId, bobCall.callId, mediaList);
226 5 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
227 : return bobCall.mediaStatus
228 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
229 : }));
230 :
231 1 : std::this_thread::sleep_for(5s);
232 :
233 : // Start recorder
234 1 : recordedFile.clear();
235 1 : CPPUNIT_ASSERT(!libjami::getIsRecording(aliceId, callId));
236 1 : libjami::toggleRecording(aliceId, callId);
237 1 : std::this_thread::sleep_for(5s);
238 1 : CPPUNIT_ASSERT(libjami::getIsRecording(aliceId, callId));
239 :
240 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return recordedFile.empty(); }));
241 :
242 : // add local video
243 : {
244 1 : auto newMediaList = mediaList;
245 1 : newMediaList.emplace_back(mediaAttributeV);
246 :
247 : // Request Media Change
248 1 : libjami::requestMediaChange(aliceId, callId, newMediaList);
249 :
250 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return bobCall.changeRequested; }));
251 :
252 : // Answer the change request
253 1 : bobCall.mediaStatus = "";
254 1 : libjami::answerMediaChangeRequest(bobId, bobCall.callId, newMediaList);
255 1 : bobCall.changeRequested = false;
256 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
257 : return bobCall.mediaStatus
258 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
259 : }));
260 :
261 4 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !recordedFile.empty() && recordedFile.find(".ogg") != std::string::npos; }));
262 1 : recordedFile = "";
263 : // give time to start camera
264 1 : std::this_thread::sleep_for(10s);
265 :
266 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return recordedFile.empty(); }));
267 1 : }
268 :
269 : // mute local video
270 : {
271 1 : mediaAttributeV[libjami::Media::MediaAttributeKey::MUTED] = TRUE_STR;
272 1 : auto newMediaList = mediaList;
273 1 : newMediaList.emplace_back(mediaAttributeV);
274 :
275 : // Mute Bob video
276 1 : libjami::requestMediaChange(aliceId, callId, newMediaList);
277 1 : std::this_thread::sleep_for(5s);
278 1 : libjami::requestMediaChange(bobId, bobCall.callId, newMediaList);
279 :
280 4 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !recordedFile.empty() && recordedFile.find(".webm") != std::string::npos; }));
281 1 : recordedFile = "";
282 1 : std::this_thread::sleep_for(10s);
283 1 : }
284 :
285 : // Stop recorder after a few seconds
286 1 : libjami::toggleRecording(aliceId, callId);
287 1 : CPPUNIT_ASSERT(!libjami::getIsRecording(aliceId, callId));
288 :
289 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !recordedFile.empty() && recordedFile.find(".ogg") != std::string::npos; }));
290 :
291 1 : Manager::instance().hangupCall(aliceId, callId);
292 5 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER"; }));
293 1 : JAMI_INFO("End testRecordCall");
294 1 : }
295 :
296 : void
297 1 : RecorderTest::testRecordAudioOnlyCall()
298 : {
299 1 : JAMI_INFO("Start testRecordAudioOnlyCall");
300 1 : registerSignalHandlers();
301 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
302 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
303 1 : auto bobUri = bobAccount->getUsername();
304 :
305 1 : JAMI_INFO("Start call between Alice and Bob");
306 : // Audio only call
307 1 : std::vector<std::map<std::string, std::string>> mediaList;
308 : std::map<std::string, std::string> mediaAttribute
309 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::AUDIO},
310 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
311 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
312 : {libjami::Media::MediaAttributeKey::LABEL, "audio_0"},
313 7 : {libjami::Media::MediaAttributeKey::SOURCE, ""}};
314 1 : mediaList.emplace_back(mediaAttribute);
315 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
316 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
317 1 : libjami::acceptWithMedia(bobId, bobCall.callId, mediaList);
318 4 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
319 : return bobCall.mediaStatus
320 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
321 : }));
322 :
323 : // Start recorder
324 1 : recordedFile.clear();
325 1 : libjami::toggleRecording(aliceId, callId);
326 1 : std::this_thread::sleep_for(5s);
327 1 : CPPUNIT_ASSERT(libjami::getIsRecording(aliceId, callId));
328 :
329 : // Toggle recording
330 1 : libjami::toggleRecording(aliceId, callId);
331 1 : CPPUNIT_ASSERT(!libjami::getIsRecording(aliceId, callId));
332 :
333 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
334 : return !recordedFile.empty() && recordedFile.find(".ogg") != std::string::npos;
335 : }));
336 :
337 1 : Manager::instance().hangupCall(aliceId, callId);
338 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER"; }));
339 1 : JAMI_INFO("End testRecordAudioOnlyCall");
340 1 : }
341 :
342 : void
343 1 : RecorderTest::testRecordCallOnePersonRdv()
344 : {
345 1 : JAMI_INFO("Start testRecordCallOnePersonRdv");
346 1 : registerSignalHandlers();
347 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
348 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
349 1 : auto bobUri = bobAccount->getUsername();
350 :
351 : try {
352 1 : bobAccount->editConfig(
353 1 : [&](AccountConfig& config) { config.isRendezVous = true; });
354 0 : } catch (...) {}
355 :
356 1 : CPPUNIT_ASSERT(bobAccount->config().isRendezVous);
357 :
358 1 : recordedFile.clear();
359 :
360 1 : JAMI_INFO("Start call between Alice and Bob");
361 : // Audio only call
362 1 : std::vector<std::map<std::string, std::string>> mediaList;
363 : std::map<std::string, std::string> mediaAttributeA
364 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::AUDIO},
365 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
366 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
367 : {libjami::Media::MediaAttributeKey::LABEL, "audio_0"},
368 7 : {libjami::Media::MediaAttributeKey::SOURCE, ""}};
369 1 : mediaList.emplace_back(mediaAttributeA);
370 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
371 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
372 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
373 : return bobCall.mediaStatus
374 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
375 : }));
376 :
377 1 : CPPUNIT_ASSERT(!libjami::getIsRecording(aliceId, callId));
378 1 : libjami::toggleRecording(aliceId, callId);
379 1 : std::this_thread::sleep_for(5s);
380 :
381 1 : CPPUNIT_ASSERT(libjami::getIsRecording(aliceId, callId));
382 :
383 : // Stop recorder
384 1 : libjami::toggleRecording(aliceId, callId);
385 1 : CPPUNIT_ASSERT(!libjami::getIsRecording(aliceId, callId));
386 :
387 2 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !recordedFile.empty() && recordedFile.find(".ogg") != std::string::npos; }));
388 :
389 1 : Manager::instance().hangupCall(aliceId, callId);
390 4 : CPPUNIT_ASSERT(
391 : cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER"; }));
392 1 : JAMI_INFO("End testRecordCallOnePersonRdv");
393 1 : }
394 :
395 : void
396 1 : RecorderTest::testStopCallWhileRecording()
397 : {
398 1 : JAMI_INFO("Start testStopCallWhileRecording");
399 1 : registerSignalHandlers();
400 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
401 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
402 1 : auto bobUri = bobAccount->getUsername();
403 :
404 1 : JAMI_INFO("Start call between Alice and Bob");
405 1 : std::vector<std::map<std::string, std::string>> mediaList;
406 : std::map<std::string, std::string> mediaAttributeA
407 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::AUDIO},
408 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
409 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
410 : {libjami::Media::MediaAttributeKey::LABEL, "audio_0"},
411 7 : {libjami::Media::MediaAttributeKey::SOURCE, ""}};
412 : std::map<std::string, std::string> mediaAttributeV
413 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::VIDEO},
414 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
415 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
416 : {libjami::Media::MediaAttributeKey::LABEL, "video_0"},
417 7 : {libjami::Media::MediaAttributeKey::SOURCE, videoPath}};
418 1 : mediaList.emplace_back(mediaAttributeA);
419 1 : mediaList.emplace_back(mediaAttributeV);
420 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
421 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
422 1 : libjami::acceptWithMedia(bobId, bobCall.callId, mediaList);
423 5 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
424 : return bobCall.mediaStatus
425 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
426 : }));
427 :
428 : // give time to start camera
429 1 : std::this_thread::sleep_for(5s);
430 :
431 : // Start recorder
432 1 : recordedFile.clear();
433 1 : libjami::toggleRecording(aliceId, callId);
434 1 : std::this_thread::sleep_for(10s);
435 1 : CPPUNIT_ASSERT(libjami::getIsRecording(aliceId, callId));
436 :
437 : // Hangup call
438 1 : Manager::instance().hangupCall(aliceId, callId);
439 6 : CPPUNIT_ASSERT(
440 : cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER" && !recordedFile.empty() && recordedFile.find(".webm") != std::string::npos; }));
441 1 : JAMI_INFO("End testStopCallWhileRecording");
442 1 : }
443 :
444 : void
445 1 : RecorderTest::testDaemonPreference()
446 : {
447 1 : JAMI_INFO("Start testDaemonPreference");
448 1 : registerSignalHandlers();
449 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
450 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
451 1 : auto bobUri = bobAccount->getUsername();
452 :
453 1 : libjami::setIsAlwaysRecording(true);
454 1 : recordedFile.clear();
455 :
456 1 : JAMI_INFO("Start call between Alice and Bob");
457 1 : std::vector<std::map<std::string, std::string>> mediaList;
458 : std::map<std::string, std::string> mediaAttributeA
459 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::AUDIO},
460 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
461 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
462 : {libjami::Media::MediaAttributeKey::LABEL, "audio_0"},
463 7 : {libjami::Media::MediaAttributeKey::SOURCE, ""}};
464 : std::map<std::string, std::string> mediaAttributeV
465 : = {{libjami::Media::MediaAttributeKey::MEDIA_TYPE, libjami::Media::MediaAttributeValue::VIDEO},
466 : {libjami::Media::MediaAttributeKey::ENABLED, TRUE_STR},
467 : {libjami::Media::MediaAttributeKey::MUTED, FALSE_STR},
468 : {libjami::Media::MediaAttributeKey::LABEL, "video_0"},
469 7 : {libjami::Media::MediaAttributeKey::SOURCE, videoPath}};
470 1 : mediaList.emplace_back(mediaAttributeA);
471 1 : mediaList.emplace_back(mediaAttributeV);
472 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, mediaList);
473 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return !bobCall.callId.empty(); }));
474 1 : libjami::acceptWithMedia(bobId, bobCall.callId, mediaList);
475 4 : CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] {
476 : return bobCall.mediaStatus
477 : == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS;
478 : }));
479 :
480 : // Let record some seconds
481 1 : std::this_thread::sleep_for(5s);
482 1 : CPPUNIT_ASSERT(libjami::getIsRecording(aliceId, callId));
483 1 : std::this_thread::sleep_for(10s);
484 :
485 1 : Manager::instance().hangupCall(aliceId, callId);
486 2 : CPPUNIT_ASSERT(
487 : cv.wait_for(lk, 20s, [&] { return bobCall.state == "OVER" && !recordedFile.empty() && recordedFile.find(".webm") != std::string::npos; }));
488 1 : JAMI_INFO("End testDaemonPreference");
489 1 : }
490 :
491 : } // namespace test
492 : } // namespace jami
493 :
494 1 : RING_TEST_RUNNER(jami::test::RecorderTest::name())
|