Line data Source code
1 : /*
2 : * Copyright (C) 2020-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 "manager.h"
20 : #include "jamidht/jamiaccount.h"
21 : #include "sip/sipcall.h"
22 : #include "sip/siptransport.h"
23 : #include "../../test_runner.h"
24 : #include "jami.h"
25 : #include "account_const.h"
26 : #include "account_schema.h"
27 : #include "media_const.h"
28 : #include "call_const.h"
29 : #include "common.h"
30 :
31 : #include <dhtnet/connectionmanager.h>
32 :
33 : #include <cppunit/TestAssert.h>
34 : #include <cppunit/TestFixture.h>
35 : #include <cppunit/extensions/HelperMacros.h>
36 :
37 : #include <condition_variable>
38 : #include <filesystem>
39 : #include <string>
40 :
41 : using namespace libjami::Account;
42 : using namespace libjami::Call::Details;
43 : using namespace std::literals::chrono_literals;
44 : namespace jami {
45 : namespace test {
46 :
47 : class CallTest : public CppUnit::TestFixture
48 : {
49 : public:
50 9 : CallTest()
51 9 : {
52 : // Init daemon
53 9 : libjami::init(
54 : libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
55 9 : if (not Manager::instance().initialized)
56 1 : CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
57 9 : }
58 18 : ~CallTest() { libjami::fini(); }
59 2 : static std::string name() { return "Call"; }
60 : void setUp();
61 : void tearDown();
62 :
63 : std::string aliceId;
64 : std::string bobId;
65 : std::string bob2Id;
66 : std::string carlaId;
67 :
68 : private:
69 : void testCall();
70 : void testCachedCall();
71 : void testStopSearching();
72 : void testDeclineMultiDevice();
73 : void testTlsInfosPeerCertificate();
74 : void testSocketInfos();
75 : void testInvalidTurn();
76 : void testTransfer();
77 : void testDhtPublicInCall();
78 :
79 2 : CPPUNIT_TEST_SUITE(CallTest);
80 1 : CPPUNIT_TEST(testCall);
81 1 : CPPUNIT_TEST(testCachedCall);
82 1 : CPPUNIT_TEST(testStopSearching);
83 1 : CPPUNIT_TEST(testDeclineMultiDevice);
84 1 : CPPUNIT_TEST(testTlsInfosPeerCertificate);
85 1 : CPPUNIT_TEST(testSocketInfos);
86 1 : CPPUNIT_TEST(testInvalidTurn);
87 1 : CPPUNIT_TEST(testTransfer);
88 1 : CPPUNIT_TEST(testDhtPublicInCall);
89 4 : CPPUNIT_TEST_SUITE_END();
90 : };
91 :
92 : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CallTest, CallTest::name());
93 :
94 : void
95 9 : CallTest::setUp()
96 : {
97 18 : auto actors = load_actors_and_wait_for_announcement("actors/alice-bob-carla.yml");
98 9 : aliceId = actors["alice"];
99 9 : bobId = actors["bob"];
100 9 : carlaId = actors["carla"];
101 9 : }
102 :
103 : void
104 9 : CallTest::tearDown()
105 : {
106 18 : auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
107 9 : std::remove(bobArchive.c_str());
108 :
109 9 : if (bob2Id.empty()) {
110 32 : wait_for_removal_of({aliceId, bobId, carlaId});
111 : } else {
112 5 : wait_for_removal_of({aliceId, bobId, carlaId, bob2Id});
113 : }
114 9 : }
115 :
116 : void
117 1 : CallTest::testCall()
118 : {
119 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
120 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
121 1 : auto bobUri = bobAccount->getUsername();
122 1 : auto aliceUri = aliceAccount->getUsername();
123 :
124 1 : std::mutex mtx;
125 1 : std::unique_lock lk {mtx};
126 1 : std::condition_variable cv;
127 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
128 1 : std::atomic_bool callReceived {false};
129 1 : std::atomic<int> callStopped {0};
130 : // Watch signals
131 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
132 1 : [&](const std::string&,
133 : const std::string&,
134 : const std::string&,
135 : const std::vector<std::map<std::string, std::string>>&) {
136 1 : callReceived = true;
137 1 : cv.notify_one();
138 1 : }));
139 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
140 8 : [&](const std::string&, const std::string&, const std::string& state, signed) {
141 8 : if (state == "OVER") {
142 2 : callStopped += 1;
143 2 : if (callStopped == 2)
144 1 : cv.notify_one();
145 : }
146 8 : }));
147 1 : libjami::registerSignalHandlers(confHandlers);
148 :
149 1 : JAMI_INFO("Start call between alice and Bob");
150 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
151 :
152 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callReceived.load(); }));
153 :
154 1 : JAMI_INFO("Stop call between alice and Bob");
155 1 : callStopped = 0;
156 1 : Manager::instance().hangupCall(aliceId, call);
157 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callStopped == 2; }));
158 1 : }
159 :
160 : void
161 1 : CallTest::testCachedCall()
162 : {
163 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
164 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
165 1 : auto bobUri = bobAccount->getUsername();
166 1 : auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
167 1 : auto aliceUri = aliceAccount->getUsername();
168 :
169 : // Pin certificate from one to another certstore (because we do not perform any DHT operation in this test)
170 1 : bobAccount->certStore().pinCertificate(aliceAccount->identity().second);
171 1 : aliceAccount->certStore().pinCertificate(bobAccount->identity().second);
172 :
173 1 : std::mutex mtx;
174 1 : std::unique_lock lk {mtx};
175 1 : std::condition_variable cv;
176 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
177 1 : std::atomic_bool callReceived {false}, successfullyConnected {false};
178 1 : std::atomic<int> callStopped {0};
179 : // Watch signals
180 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
181 1 : [&](const std::string&,
182 : const std::string&,
183 : const std::string&,
184 : const std::vector<std::map<std::string, std::string>>&) {
185 1 : callReceived = true;
186 1 : cv.notify_one();
187 1 : }));
188 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
189 8 : [&](const std::string&, const std::string&, const std::string& state, signed) {
190 8 : if (state == "OVER") {
191 2 : callStopped += 1;
192 2 : if (callStopped == 2)
193 1 : cv.notify_one();
194 : }
195 8 : }));
196 1 : libjami::registerSignalHandlers(confHandlers);
197 :
198 1 : JAMI_INFO("Connect Alice's device and Bob's device");
199 1 : aliceAccount->connectionManager()
200 1 : .connectDevice(bobDeviceId,
201 : "sip",
202 1 : [&cv, &successfullyConnected](std::shared_ptr<dhtnet::ChannelSocket> socket,
203 2 : const DeviceId&) {
204 1 : if (socket)
205 1 : successfullyConnected = true;
206 1 : cv.notify_one();
207 1 : });
208 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected.load(); }));
209 :
210 1 : JAMI_INFO("Start call between alice and Bob");
211 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
212 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callReceived.load(); }));
213 :
214 1 : callStopped = 0;
215 1 : JAMI_INFO("Stop call between alice and Bob");
216 1 : Manager::instance().hangupCall(aliceId, call);
217 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callStopped == 2; }));
218 1 : }
219 :
220 : void
221 1 : CallTest::testStopSearching()
222 : {
223 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
224 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
225 1 : auto bobUri = bobAccount->getUsername();
226 1 : auto aliceUri = aliceAccount->getUsername();
227 :
228 1 : Manager::instance().sendRegister(bobId, false);
229 :
230 1 : std::mutex mtx;
231 1 : std::unique_lock lk {mtx};
232 1 : std::condition_variable cv;
233 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
234 1 : std::atomic_bool callStopped {false};
235 : // Watch signals
236 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
237 2 : [&](const std::string&, const std::string&, const std::string& state, signed) {
238 2 : if (state == "OVER") {
239 1 : callStopped = true;
240 1 : cv.notify_one();
241 : }
242 2 : }));
243 1 : libjami::registerSignalHandlers(confHandlers);
244 :
245 1 : JAMI_INFO("Start call between alice and Bob");
246 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
247 :
248 : // Bob not there, so we should get a SEARCHING STATUS
249 1 : JAMI_INFO("Wait OVER state");
250 : // Then wait for the DHT no answer. this can take some times
251 3 : CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&] { return callStopped.load(); }));
252 1 : }
253 :
254 : void
255 1 : CallTest::testDeclineMultiDevice()
256 : {
257 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
258 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
259 1 : auto bobUri = bobAccount->getUsername();
260 1 : auto aliceUri = aliceAccount->getUsername();
261 1 : std::mutex mtx;
262 1 : std::unique_lock lk {mtx};
263 1 : std::condition_variable cv;
264 :
265 : // Add second device for Bob
266 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
267 2 : auto bobArchive = std::filesystem::current_path().string() + "/bob.gz";
268 1 : std::remove(bobArchive.c_str());
269 1 : bobAccount->exportArchive(bobArchive);
270 :
271 2 : std::map<std::string, std::string> details = libjami::getAccountTemplate("RING");
272 1 : details[ConfProperties::TYPE] = "RING";
273 1 : details[ConfProperties::DISPLAYNAME] = "BOB2";
274 1 : details[ConfProperties::ALIAS] = "BOB2";
275 1 : details[ConfProperties::UPNP_ENABLED] = "true";
276 1 : details[ConfProperties::ARCHIVE_PASSWORD] = "";
277 1 : details[ConfProperties::ARCHIVE_PIN] = "";
278 1 : details[ConfProperties::ARCHIVE_PATH] = bobArchive;
279 :
280 1 : bob2Id = Manager::instance().addAccount(details);
281 :
282 1 : wait_for_announcement_of(bob2Id);
283 :
284 1 : std::atomic<int> callReceived {0};
285 1 : std::atomic<int> callStopped {0};
286 1 : std::string callIdBob;
287 : // Watch signals
288 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
289 2 : [&](const std::string& accountId,
290 : const std::string& callId,
291 : const std::string&,
292 : const std::vector<std::map<std::string, std::string>>&) {
293 2 : if (accountId == bobId)
294 1 : callIdBob = callId;
295 2 : callReceived += 1;
296 2 : cv.notify_one();
297 2 : }));
298 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
299 14 : [&](const std::string&, const std::string&, const std::string& state, signed) {
300 14 : if (state == "OVER")
301 3 : callStopped++;
302 14 : cv.notify_one();
303 14 : }));
304 1 : libjami::registerSignalHandlers(confHandlers);
305 :
306 1 : JAMI_INFO("Start call between alice and Bob");
307 1 : auto bobAccount2 = Manager::instance().getAccount<JamiAccount>(bob2Id);
308 :
309 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
310 :
311 12 : CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return callReceived == 2 && !callIdBob.empty(); }));
312 :
313 1 : JAMI_INFO("Stop call between alice and Bob");
314 1 : callStopped = 0;
315 1 : Manager::instance().refuseCall(bobId, callIdBob);
316 11 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
317 : return callStopped.load() >= 3; /* >= because there is subcalls */
318 : }));
319 1 : }
320 :
321 : void
322 1 : CallTest::testTlsInfosPeerCertificate()
323 : {
324 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
325 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
326 1 : auto bobUri = bobAccount->getUsername();
327 1 : auto aliceUri = aliceAccount->getUsername();
328 :
329 1 : std::mutex mtx;
330 1 : std::unique_lock lk {mtx};
331 1 : std::condition_variable cv;
332 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
333 1 : std::atomic<int> callStopped {0};
334 1 : std::string bobCallId;
335 1 : std::string aliceCallState;
336 : // Watch signals
337 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
338 1 : [&](const std::string& accountId,
339 : const std::string& callId,
340 : const std::string&,
341 : const std::vector<std::map<std::string, std::string>>&) {
342 1 : if (accountId == bobId)
343 1 : bobCallId = callId;
344 1 : cv.notify_one();
345 1 : }));
346 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
347 11 : [&](const std::string& accountId, const std::string&, const std::string& state, signed) {
348 11 : if (accountId == aliceId)
349 5 : aliceCallState = state;
350 11 : if (state == "OVER") {
351 2 : callStopped += 1;
352 2 : if (callStopped == 2)
353 1 : cv.notify_one();
354 : }
355 11 : }));
356 1 : libjami::registerSignalHandlers(confHandlers);
357 :
358 1 : JAMI_INFO("Start call between alice and Bob");
359 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, {});
360 :
361 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !bobCallId.empty(); }));
362 :
363 1 : Manager::instance().answerCall(bobId, bobCallId);
364 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceCallState == "CURRENT"; }));
365 :
366 1 : auto call = std::dynamic_pointer_cast<SIPCall>(aliceAccount->getCall(callId));
367 1 : auto* transport = call->getTransport();
368 1 : CPPUNIT_ASSERT(transport);
369 1 : auto cert = transport->getTlsInfos().peerCert;
370 1 : CPPUNIT_ASSERT(cert && cert->issuer);
371 1 : CPPUNIT_ASSERT(cert->issuer->getId().toString() == bobAccount->getUsername());
372 :
373 1 : JAMI_INFO("Stop call between alice and Bob");
374 1 : callStopped = 0;
375 1 : Manager::instance().hangupCall(aliceId, callId);
376 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callStopped == 2; }));
377 1 : }
378 :
379 : void
380 1 : CallTest::testSocketInfos()
381 : {
382 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
383 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
384 1 : auto bobUri = bobAccount->getUsername();
385 1 : auto aliceUri = aliceAccount->getUsername();
386 :
387 1 : std::mutex mtx;
388 1 : std::unique_lock lk {mtx};
389 1 : std::condition_variable cv;
390 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
391 1 : std::atomic<int> callStopped {0};
392 1 : std::string bobCallId;
393 1 : std::string aliceCallState;
394 : // Watch signals
395 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
396 1 : [&](const std::string& accountId,
397 : const std::string& callId,
398 : const std::string&,
399 : const std::vector<std::map<std::string, std::string>>&) {
400 1 : if (accountId == bobId)
401 1 : bobCallId = callId;
402 1 : cv.notify_one();
403 1 : }));
404 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
405 11 : [&](const std::string& accountId, const std::string&, const std::string& state, signed) {
406 11 : if (accountId == aliceId)
407 5 : aliceCallState = state;
408 11 : if (state == "OVER") {
409 2 : callStopped += 1;
410 2 : if (callStopped == 2)
411 1 : cv.notify_one();
412 : }
413 11 : }));
414 1 : auto mediaReady = false;
415 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
416 2 : [&](const std::string& callId,
417 : const std::string& event,
418 : const std::vector<std::map<std::string, std::string>>&) {
419 2 : if (event == libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS) {
420 2 : mediaReady = true;
421 2 : cv.notify_one();
422 : }
423 2 : }));
424 1 : libjami::registerSignalHandlers(confHandlers);
425 :
426 1 : JAMI_INFO("Start call between alice and Bob");
427 1 : auto callId = libjami::placeCallWithMedia(aliceId, bobUri, {});
428 :
429 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !bobCallId.empty(); }));
430 :
431 1 : Manager::instance().answerCall(bobId, bobCallId);
432 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceCallState == "CURRENT" && mediaReady; }));
433 :
434 1 : JAMI_INFO("Detail debug");
435 1 : auto details = libjami::getCallDetails(aliceId, callId);
436 21 : for (auto i = details.begin(); i != details.end(); i++) {
437 20 : JAMI_INFO("%s : %s", i->first.c_str(), i->second.c_str());
438 : }
439 1 : auto call = std::dynamic_pointer_cast<SIPCall>(aliceAccount->getCall(callId));
440 1 : auto transport = call->getIceMedia();
441 1 : CPPUNIT_ASSERT(transport);
442 1 : CPPUNIT_ASSERT(transport->isRunning());
443 1 : CPPUNIT_ASSERT(transport->link().c_str() == details[libjami::Call::Details::SOCKETS]);
444 :
445 1 : JAMI_INFO("Stop call between alice and Bob");
446 1 : callStopped = 0;
447 1 : Manager::instance().hangupCall(aliceId, callId);
448 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callStopped == 2; }));
449 1 : }
450 :
451 : void
452 1 : CallTest::testInvalidTurn()
453 : {
454 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
455 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
456 1 : auto bobUri = bobAccount->getUsername();
457 1 : auto aliceUri = aliceAccount->getUsername();
458 :
459 1 : std::mutex mtx;
460 1 : std::unique_lock lk {mtx};
461 1 : std::condition_variable cv;
462 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
463 1 : std::atomic_bool callReceived {false};
464 1 : std::atomic<int> callStopped {0};
465 1 : bool aliceReady = false;
466 : // Watch signals
467 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
468 1 : [&](const std::string&,
469 : const std::string&,
470 : const std::string&,
471 : const std::vector<std::map<std::string, std::string>>&) {
472 1 : callReceived = true;
473 1 : cv.notify_one();
474 1 : }));
475 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
476 8 : [&](const std::string&, const std::string&, const std::string& state, signed) {
477 8 : if (state == "OVER") {
478 2 : callStopped += 1;
479 2 : if (callStopped == 2)
480 1 : cv.notify_one();
481 : }
482 8 : }));
483 1 : confHandlers.insert(
484 2 : libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>(
485 4 : [&](const std::string& accountId,
486 : const std::map<std::string, std::string>& details) {
487 4 : if (accountId != aliceId) {
488 0 : return;
489 : }
490 : try {
491 8 : aliceReady |= accountId == aliceId
492 8 : && details.at(jami::Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS) == "REGISTERED"
493 8 : && details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) == "true";
494 0 : } catch (const std::out_of_range&) {}
495 4 : cv.notify_one();
496 : }));
497 1 : libjami::registerSignalHandlers(confHandlers);
498 :
499 1 : std::map<std::string, std::string> details;
500 1 : details[ConfProperties::TURN::SERVER] = "1.1.1.1";
501 1 : aliceReady = false;
502 1 : libjami::setAccountDetails(aliceId, details);
503 4 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceReady; }));
504 :
505 1 : JAMI_INFO("Start call between alice and Bob");
506 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
507 :
508 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return callReceived.load(); }));
509 :
510 1 : JAMI_INFO("Stop call between alice and Bob");
511 1 : callStopped = 0;
512 1 : Manager::instance().hangupCall(aliceId, call);
513 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callStopped == 2; }));
514 1 : }
515 :
516 : void
517 1 : CallTest::testTransfer()
518 : {
519 : // Alice call Bob
520 : // Bob transfer to Carla
521 :
522 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
523 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
524 1 : auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
525 1 : auto carlaUri = carlaAccount->getUsername();
526 1 : auto bobUri = bobAccount->getUsername();
527 1 : auto aliceUri = aliceAccount->getUsername();
528 :
529 1 : std::mutex mtx;
530 1 : std::unique_lock lk {mtx};
531 1 : std::condition_variable cv;
532 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
533 1 : std::atomic_bool bobCallReceived {false};
534 1 : std::atomic_bool carlaCallReceived {false};
535 1 : std::atomic<int> bobCallStopped {0};
536 1 : std::atomic<int> aliceCallStopped {0};
537 1 : std::string bobCallId;
538 1 : std::string carlaCallId;
539 1 : std::string carlaCallPeer;
540 : // Watch signals
541 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
542 2 : [&](const std::string& accountId,
543 : const std::string& callId,
544 : const std::string& peerId,
545 : const std::vector<std::map<std::string, std::string>>&) {
546 2 : if (accountId == bobId) {
547 1 : bobCallReceived = true;
548 1 : bobCallId = callId;
549 1 : } else if (accountId == carlaId) {
550 1 : carlaCallReceived = true;
551 1 : carlaCallId = callId;
552 1 : carlaCallPeer = peerId;
553 1 : string_replace(carlaCallPeer, "@ring.dht", "");
554 : }
555 2 : cv.notify_one();
556 2 : }));
557 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
558 18 : [&](const std::string& accountId, const std::string&, const std::string& state, signed) {
559 18 : if (state == "OVER") {
560 4 : if (accountId == bobId) {
561 1 : bobCallStopped += 1;
562 1 : cv.notify_one();
563 : }
564 4 : if (accountId == aliceId) {
565 2 : aliceCallStopped += 1;
566 2 : cv.notify_one();
567 : }
568 : }
569 18 : }));
570 1 : libjami::registerSignalHandlers(confHandlers);
571 :
572 1 : JAMI_INFO("Start call between alice and Bob");
573 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
574 :
575 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return bobCallReceived.load(); }));
576 :
577 1 : JAMI_INFO("Bob transfer to Carla");
578 1 : libjami::transfer(bobId, bobCallId, carlaUri);
579 :
580 7 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return bobCallStopped.load(); }));
581 3 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaCallReceived.load(); }));
582 1 : CPPUNIT_ASSERT(carlaCallPeer == aliceUri);
583 :
584 1 : JAMI_INFO("Stop call between alice and carla");
585 1 : aliceCallStopped = 0;
586 1 : Manager::instance().hangupCall(carlaId, carlaCallId);
587 5 : CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceCallStopped.load(); }));
588 1 : }
589 :
590 : void
591 1 : CallTest::testDhtPublicInCall()
592 : {
593 1 : auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
594 1 : auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
595 1 : auto bobUri = bobAccount->getUsername();
596 1 : auto aliceUri = aliceAccount->getUsername();
597 :
598 1 : std::mutex mtx;
599 1 : std::unique_lock lk {mtx};
600 1 : std::condition_variable cv;
601 1 : std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
602 1 : std::atomic_bool callReceived {false};
603 1 : std::atomic<int> callStopped {0};
604 : // Watch signals
605 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
606 0 : [&](const std::string&,
607 : const std::string&,
608 : const std::string&,
609 : const std::vector<std::map<std::string, std::string>>&) {
610 0 : callReceived = true;
611 0 : cv.notify_one();
612 0 : }));
613 1 : confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
614 0 : [&](const std::string&, const std::string&, const std::string& state, signed) {
615 0 : if (state == "OVER") {
616 0 : callStopped += 1;
617 0 : if (callStopped == 2)
618 0 : cv.notify_one();
619 : }
620 0 : }));
621 1 : libjami::registerSignalHandlers(confHandlers);
622 :
623 1 : std::map<std::string, std::string> details;
624 1 : details["DHT.PublicInCalls"] = "FALSE";
625 1 : libjami::setAccountDetails(bobId, details);
626 :
627 1 : JAMI_INFO("Start call between alice and Bob");
628 1 : auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
629 :
630 3 : CPPUNIT_ASSERT(!cv.wait_for(lk, 15s, [&] { return callReceived.load(); }));
631 1 : }
632 :
633 : } // namespace test
634 : } // namespace jami
635 :
636 1 : RING_TEST_RUNNER(jami::test::CallTest::name())
|