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