LCOV - code coverage report
Current view: top level - test/unitTest/sip_account - sip_basic_calls.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 462 481 96.0 %
Date: 2024-12-21 08:56:24 Functions: 34 34 100.0 %

          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 "callmanager_interface.h"
      26             : #include "manager.h"
      27             : #include "sip/sipaccount.h"
      28             : #include "../../test_runner.h"
      29             : #include "jami.h"
      30             : #include "jami/media_const.h"
      31             : #include "call_const.h"
      32             : #include "account_const.h"
      33             : #include "sip/sipcall.h"
      34             : #include "sip/sdp.h"
      35             : 
      36             : using namespace libjami::Account;
      37             : using namespace libjami::Call;
      38             : 
      39             : namespace jami {
      40             : namespace test {
      41             : 
      42             : struct CallData
      43             : {
      44             :     struct Signal
      45             :     {
      46          84 :         Signal(const std::string& name, const std::string& event = {})
      47          84 :             : name_(std::move(name))
      48          84 :             , event_(std::move(event)) {};
      49             : 
      50             :         std::string name_ {};
      51             :         std::string event_ {};
      52             :     };
      53             : 
      54          15 :     CallData() = default;
      55             :     CallData(CallData&& other) = delete;
      56          15 :     CallData(const CallData& other)
      57          15 :     {
      58          15 :         accountId_ = std::move(other.accountId_);
      59          15 :         listeningPort_ = other.listeningPort_;
      60          15 :         userName_ = std::move(other.userName_);
      61          15 :         alias_ = std::move(other.alias_);
      62          15 :         callId_ = std::move(other.callId_);
      63          15 :         signals_ = std::move(other.signals_);
      64          15 :     };
      65             : 
      66             :     std::string accountId_ {};
      67             :     uint16_t listeningPort_ {0};
      68             :     std::string userName_ {};
      69             :     std::string alias_ {};
      70             :     std::string callId_ {};
      71             :     std::vector<Signal> signals_;
      72             :     std::condition_variable cv_ {};
      73             :     std::mutex mtx_;
      74             : };
      75             : 
      76             : /**
      77             :  * Call tests for SIP accounts.
      78             :  */
      79             : class SipBasicCallTest : public CppUnit::TestFixture
      80             : {
      81             : public:
      82           5 :     SipBasicCallTest()
      83           5 :     {
      84             :         // Init daemon
      85           5 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      86           5 :         if (not Manager::instance().initialized)
      87           1 :             CPPUNIT_ASSERT(libjami::start("dring-sample.yml"));
      88           5 :     }
      89          10 :     ~SipBasicCallTest() { libjami::fini(); }
      90             : 
      91           2 :     static std::string name() { return "SipBasicCallTest"; }
      92             :     void setUp();
      93             :     void tearDown();
      94             : 
      95             : private:
      96             :     // Test cases.
      97             :     void audio_only_test();
      98             :     void audio_video_test();
      99             :     void peer_answer_with_all_media_disabled();
     100             :     void hold_resume_test();
     101             :     void blind_transfer_test();
     102             : 
     103           2 :     CPPUNIT_TEST_SUITE(SipBasicCallTest);
     104           1 :     CPPUNIT_TEST(audio_only_test);
     105           1 :     CPPUNIT_TEST(audio_video_test);
     106             :     // Test when the peer answers with all the media disabled (RTP port = 0)
     107           1 :     CPPUNIT_TEST(peer_answer_with_all_media_disabled);
     108           1 :     CPPUNIT_TEST(hold_resume_test);
     109           1 :     CPPUNIT_TEST(blind_transfer_test);
     110           4 :     CPPUNIT_TEST_SUITE_END();
     111             : 
     112             :     // Event/Signal handlers
     113             :     static void onCallStateChange(const std::string& accountId,
     114             :                                   const std::string& callId,
     115             :                                   const std::string& state,
     116             :                                   CallData& callData);
     117             :     static void onIncomingCallWithMedia(const std::string& accountId,
     118             :                                         const std::string& callId,
     119             :                                         const std::vector<libjami::MediaMap> mediaList,
     120             :                                         CallData& callData);
     121             :     static void onMediaNegotiationStatus(const std::string& callId,
     122             :                                          const std::string& event,
     123             :                                          CallData& callData);
     124             : 
     125             :     // Helpers
     126             :     bool addTestAccount(const std::string& alias, uint16_t port);
     127             :     void audio_video_call(std::vector<MediaAttribute> offer,
     128             :                           std::vector<MediaAttribute> answer,
     129             :                           bool expectedToSucceed = true,
     130             :                           bool validateMedia = true);
     131             :     void configureTest();
     132             :     std::string getAccountId(const std::string& callId);
     133             :     std::string getUserAlias(const std::string& accountId);
     134             :     CallData& getCallData(const std::string& account);
     135             :     // Wait for a signal from the callbacks. Some signals also report the event that
     136             :     // triggered the signal a like the StateChange signal.
     137             :     static bool waitForSignal(CallData& callData,
     138             :                               const std::string& signal,
     139             :                               const std::string& expectedEvent = {});
     140             : 
     141             :     std::map<std::string, CallData> callDataMap_;
     142             :     std::set<std::string> testAccounts_;
     143             : };
     144             : 
     145             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SipBasicCallTest, SipBasicCallTest::name());
     146             : 
     147             : bool
     148          15 : SipBasicCallTest::addTestAccount(const std::string& alias, uint16_t port)
     149             : {
     150          15 :     CallData callData;
     151          15 :     callData.alias_ = alias;
     152          15 :     callData.userName_ = alias;
     153          15 :     callData.listeningPort_ = port;
     154          30 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("SIP");
     155          15 :     details[ConfProperties::TYPE] = "SIP";
     156          15 :     details[ConfProperties::USERNAME] = alias;
     157          15 :     details[ConfProperties::DISPLAYNAME] = alias;
     158          15 :     details[ConfProperties::ALIAS] = alias;
     159          15 :     details[ConfProperties::LOCAL_PORT] = std::to_string(port);
     160          15 :     details[ConfProperties::UPNP_ENABLED] = "false";
     161          15 :     callData.accountId_ = Manager::instance().addAccount(details);
     162          15 :     testAccounts_.insert(callData.accountId_);
     163          15 :     callDataMap_.emplace(alias, std::move(callData));
     164          30 :     return (not callDataMap_[alias].accountId_.empty());
     165          15 : }
     166             : 
     167             : void
     168           5 : SipBasicCallTest::setUp()
     169             : {
     170           5 :     JAMI_INFO("Initialize accounts ...");
     171             : 
     172           5 :     CPPUNIT_ASSERT(addTestAccount("ALICE", 5080));
     173           5 :     CPPUNIT_ASSERT(addTestAccount("BOB", 5082));
     174           5 :     CPPUNIT_ASSERT(addTestAccount("CARLA", 5084));
     175           5 : }
     176             : 
     177             : void
     178           5 : SipBasicCallTest::tearDown()
     179             : {
     180           5 :     JAMI_INFO("Remove created accounts...");
     181             : 
     182           5 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     183           5 :     std::mutex mtx;
     184           5 :     std::unique_lock lk {mtx};
     185           5 :     std::condition_variable cv;
     186           5 :     std::atomic_bool accountsRemoved {false};
     187           5 :     confHandlers.insert(
     188          10 :         libjami::exportable_callback<libjami::ConfigurationSignal::AccountsChanged>([&]() {
     189          15 :             auto currAccounts = Manager::instance().getAccountList();
     190          45 :             for (auto iter = testAccounts_.begin(); iter != testAccounts_.end();) {
     191          30 :                 auto item = std::find(currAccounts.begin(), currAccounts.end(), *iter);
     192          30 :                 if (item == currAccounts.end()) {
     193          15 :                     JAMI_INFO("Removing account %s", (*iter).c_str());
     194          15 :                     iter = testAccounts_.erase(iter);
     195             :                 } else {
     196          15 :                     iter++;
     197             :                 }
     198             :             }
     199             : 
     200          15 :             if (testAccounts_.empty()) {
     201           5 :                 accountsRemoved = true;
     202           5 :                 JAMI_INFO("All accounts removed...");
     203           5 :                 cv.notify_one();
     204             :             }
     205          15 :         }));
     206             : 
     207           5 :     libjami::registerSignalHandlers(confHandlers);
     208             : 
     209           5 :     Manager::instance().removeAccount(callDataMap_["ALICE"].accountId_, true);
     210           5 :     Manager::instance().removeAccount(callDataMap_["BOB"].accountId_, true);
     211           5 :     Manager::instance().removeAccount(callDataMap_["CARLA"].accountId_, true);
     212             : 
     213          10 :     CPPUNIT_ASSERT(
     214             :         cv.wait_for(lk, std::chrono::seconds(30), [&] { return accountsRemoved.load(); }));
     215             : 
     216           5 :     libjami::unregisterSignalHandlers();
     217           5 : }
     218             : 
     219             : std::string
     220          14 : SipBasicCallTest::getAccountId(const std::string& callId)
     221             : {
     222          14 :     auto call = Manager::instance().getCallFromCallID(callId);
     223             : 
     224          14 :     if (not call) {
     225           0 :         JAMI_WARN("Call [%s] does not exist anymore!", callId.c_str());
     226           0 :         return {};
     227             :     }
     228             : 
     229          14 :     auto const& account = call->getAccount().lock();
     230             : 
     231          14 :     if (account) {
     232          14 :         return account->getAccountID();
     233             :     }
     234             : 
     235           0 :     JAMI_WARN("Account owning the call [%s] does not exist anymore!", callId.c_str());
     236           0 :     return {};
     237          14 : }
     238             : 
     239             : std::string
     240          87 : SipBasicCallTest::getUserAlias(const std::string& accountId)
     241             : {
     242          87 :     if (accountId.empty()) {
     243           3 :         JAMI_WARN("No account ID is empty");
     244           3 :         return {};
     245             :     }
     246             : 
     247          84 :     auto ret = std::find_if(callDataMap_.begin(), callDataMap_.end(), [accountId](auto const& item) {
     248         147 :         return item.second.accountId_ == accountId;
     249             :     });
     250             : 
     251          84 :     if (ret != callDataMap_.end())
     252          84 :         return ret->first;
     253             : 
     254           0 :     JAMI_WARN("No matching test account %s", accountId.c_str());
     255           0 :     return {};
     256             : }
     257             : 
     258             : void
     259           6 : SipBasicCallTest::onIncomingCallWithMedia(const std::string& accountId,
     260             :                                           const std::string& callId,
     261             :                                           const std::vector<libjami::MediaMap> mediaList,
     262             :                                           CallData& callData)
     263             : {
     264           6 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     265             : 
     266           6 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     267             :               libjami::CallSignal::IncomingCallWithMedia::name,
     268             :               callData.alias_.c_str(),
     269             :               callId.c_str(),
     270             :               mediaList.size());
     271             : 
     272           6 :     if (not Manager::instance().getCallFromCallID(callId)) {
     273           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     274           0 :         callData.callId_ = {};
     275           0 :         return;
     276             :     }
     277             : 
     278           6 :     std::unique_lock lock {callData.mtx_};
     279           6 :     callData.callId_ = callId;
     280           6 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCallWithMedia::name));
     281             : 
     282           6 :     callData.cv_.notify_one();
     283           6 : }
     284             : 
     285             : void
     286          64 : SipBasicCallTest::onCallStateChange(const std::string& accountId,
     287             :                                     const std::string& callId,
     288             :                                     const std::string& state,
     289             :                                     CallData& callData)
     290             : {
     291          64 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     292             :               libjami::CallSignal::StateChange::name,
     293             :               callData.alias_.c_str(),
     294             :               callId.c_str(),
     295             :               state.c_str());
     296             : 
     297          64 :     CPPUNIT_ASSERT(accountId == callData.accountId_);
     298             : 
     299             :     {
     300          64 :         std::unique_lock lock {callData.mtx_};
     301          64 :         callData.signals_.emplace_back(
     302         128 :             CallData::Signal(libjami::CallSignal::StateChange::name, state));
     303          64 :     }
     304             :     // NOTE. Only states that we are interested on will notify the CV. If this
     305             :     // unit test is modified to process other states, they must be added here.
     306          64 :     if (state == "CURRENT" or state == "OVER" or state == "HUNGUP" or state == "RINGING") {
     307          38 :         callData.cv_.notify_one();
     308             :     }
     309          64 : }
     310             : 
     311             : void
     312          14 : SipBasicCallTest::onMediaNegotiationStatus(const std::string& callId,
     313             :                                            const std::string& event,
     314             :                                            CallData& callData)
     315             : {
     316          14 :     auto call = Manager::instance().getCallFromCallID(callId);
     317          14 :     if (not call) {
     318           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     319           0 :         return;
     320             :     }
     321             : 
     322          14 :     auto account = call->getAccount().lock();
     323          14 :     if (not account) {
     324           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     325           0 :         return;
     326             :     }
     327             : 
     328          14 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     329             :               libjami::CallSignal::MediaNegotiationStatus::name,
     330             :               account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
     331             :               call->getCallId().c_str(),
     332             :               event.c_str());
     333             : 
     334          14 :     if (account->getAccountID() != callData.accountId_)
     335           0 :         return;
     336             : 
     337             :     {
     338          14 :         std::unique_lock lock {callData.mtx_};
     339          14 :         callData.signals_.emplace_back(
     340          28 :             CallData::Signal(libjami::CallSignal::MediaNegotiationStatus::name, event));
     341          14 :     }
     342             : 
     343          14 :     callData.cv_.notify_one();
     344          14 : }
     345             : 
     346             : bool
     347          39 : SipBasicCallTest::waitForSignal(CallData& callData,
     348             :                                 const std::string& expectedSignal,
     349             :                                 const std::string& expectedEvent)
     350             : {
     351          39 :     const std::chrono::seconds TIME_OUT {10};
     352          39 :     std::unique_lock lock {callData.mtx_};
     353             : 
     354             :     // Combined signal + event (if any).
     355          39 :     std::string sigEvent(expectedSignal);
     356          39 :     if (not expectedEvent.empty())
     357          33 :         sigEvent += "::" + expectedEvent;
     358             : 
     359          39 :     JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
     360             : 
     361          39 :     auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
     362             :         // Search for the expected signal in list of received signals.
     363          54 :         bool pred = false;
     364         203 :         for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
     365             :             // The predicate is true if the signal names match, and if the
     366             :             // expectedEvent is not empty, the events must also match.
     367         188 :             if (it->name_ == expectedSignal
     368         188 :                 and (expectedEvent.empty() or it->event_ == expectedEvent)) {
     369          39 :                 pred = true;
     370             :                 // Done with this signal.
     371          39 :                 callData.signals_.erase(it);
     372          39 :                 break;
     373             :             }
     374             :         }
     375             : 
     376          54 :         return pred;
     377             :     });
     378             : 
     379          39 :     if (not res) {
     380           0 :         JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
     381             :                  callData.alias_.c_str(),
     382             :                  sigEvent.c_str());
     383             : 
     384           0 :         JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
     385             : 
     386           0 :         for (auto const& sig : callData.signals_) {
     387           0 :             JAMI_INFO() << "\tSignal [" << sig.name_
     388           0 :                         << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
     389             :         }
     390             :     }
     391             : 
     392          39 :     return res;
     393          39 : }
     394             : 
     395             : void
     396           5 : SipBasicCallTest::configureTest()
     397             : {
     398             :     {
     399           5 :         CPPUNIT_ASSERT(not callDataMap_["ALICE"].accountId_.empty());
     400           5 :         auto const& account = Manager::instance().getAccount<SIPAccount>(
     401           5 :             callDataMap_["ALICE"].accountId_);
     402           5 :         account->setLocalPort(callDataMap_["ALICE"].listeningPort_);
     403           5 :     }
     404             : 
     405             :     {
     406           5 :         CPPUNIT_ASSERT(not callDataMap_["BOB"].accountId_.empty());
     407           5 :         auto const& account = Manager::instance().getAccount<SIPAccount>(
     408           5 :             callDataMap_["BOB"].accountId_);
     409           5 :         account->setLocalPort(callDataMap_["BOB"].listeningPort_);
     410           5 :     }
     411             : 
     412             :     {
     413           5 :         CPPUNIT_ASSERT(not callDataMap_["CARLA"].accountId_.empty());
     414           5 :         auto const& account = Manager::instance().getAccount<SIPAccount>(
     415           5 :             callDataMap_["CARLA"].accountId_);
     416           5 :         account->setLocalPort(callDataMap_["CARLA"].listeningPort_);
     417           5 :     }
     418             : 
     419           5 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers;
     420             : 
     421             :     // Insert needed signal handlers.
     422           5 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     423           6 :         [&](const std::string& accountId,
     424             :             const std::string& callId,
     425             :             const std::string&,
     426             :             const std::vector<libjami::MediaMap> mediaList) {
     427           6 :             auto alias = getUserAlias(accountId);
     428           6 :             if (not alias.empty()) {
     429           6 :                 onIncomingCallWithMedia(accountId, callId, mediaList, callDataMap_[alias]);
     430             :             }
     431           6 :         }));
     432             : 
     433           5 :     signalHandlers.insert(
     434          10 :         libjami::exportable_callback<libjami::CallSignal::StateChange>([&](const std::string& accountId,
     435             :                                                                        const std::string& callId,
     436             :                                                                        const std::string& state,
     437             :                                                                        signed) {
     438          67 :             auto alias = getUserAlias(accountId);
     439          67 :             if (not alias.empty())
     440          64 :                 onCallStateChange(accountId, callId, state, callDataMap_[alias]);
     441          67 :         }));
     442             : 
     443           5 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
     444          14 :         [&](const std::string& callId,
     445             :             const std::string& event,
     446             :             const std::vector<std::map<std::string, std::string>>& /* mediaList */) {
     447          14 :             auto alias = getUserAlias(getAccountId(callId));
     448          14 :             if (not alias.empty())
     449          14 :                 onMediaNegotiationStatus(callId, event, callDataMap_[alias]);
     450          14 :         }));
     451             : 
     452           5 :     libjami::registerSignalHandlers(signalHandlers);
     453           5 : }
     454             : 
     455             : void
     456           3 : SipBasicCallTest::audio_video_call(std::vector<MediaAttribute> offer,
     457             :                                    std::vector<MediaAttribute> answer,
     458             :                                    bool expectedToSucceed,
     459             :                                    bool validateMedia)
     460             : {
     461           3 :     configureTest();
     462             : 
     463           3 :     JAMI_INFO("=== Start a call and validate ===");
     464             : 
     465           6 :     std::string bobUri = callDataMap_["BOB"].userName_
     466          12 :                          + "@127.0.0.1:" + std::to_string(callDataMap_["BOB"].listeningPort_);
     467             : 
     468           6 :     callDataMap_["ALICE"].callId_
     469           6 :         = libjami::placeCallWithMedia(callDataMap_["ALICE"].accountId_,
     470             :                                     bobUri,
     471           9 :                                     MediaAttribute::mediaAttributesToMediaMaps(offer));
     472             : 
     473           3 :     CPPUNIT_ASSERT(not callDataMap_["ALICE"].callId_.empty());
     474             : 
     475           3 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     476             :               callDataMap_["ALICE"].accountId_.c_str(),
     477             :               callDataMap_["BOB"].accountId_.c_str());
     478             : 
     479             :     // Give it some time to ring
     480           3 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     481             : 
     482             :     // Wait for call to be processed.
     483           3 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     484             :                                  libjami::CallSignal::StateChange::name,
     485             :                                  StateEvent::RINGING));
     486             : 
     487             :     // Wait for incoming call signal.
     488           3 :     CPPUNIT_ASSERT(
     489             :         waitForSignal(callDataMap_["BOB"], libjami::CallSignal::IncomingCallWithMedia::name));
     490             : 
     491             :     // Answer the call.
     492           3 :     libjami::acceptWithMedia(callDataMap_["BOB"].accountId_,
     493           6 :                            callDataMap_["BOB"].callId_,
     494           6 :                            MediaAttribute::mediaAttributesToMediaMaps(answer));
     495             : 
     496           3 :     if (expectedToSucceed) {
     497             :         // Wait for media negotiation complete signal.
     498           2 :         CPPUNIT_ASSERT(
     499             :             waitForSignal(callDataMap_["BOB"],
     500             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     501             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     502             : 
     503             :         // Wait for the StateChange signal.
     504           2 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
     505             :                                      libjami::CallSignal::StateChange::name,
     506             :                                      StateEvent::CURRENT));
     507             : 
     508           2 :         JAMI_INFO("BOB answered the call [%s]", callDataMap_["BOB"].callId_.c_str());
     509             : 
     510             :         // Wait for media negotiation complete signal.
     511           2 :         CPPUNIT_ASSERT(
     512             :             waitForSignal(callDataMap_["ALICE"],
     513             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     514             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     515             :         // Validate Alice's media
     516           2 :         if (validateMedia) {
     517           4 :             auto call = Manager::instance().getCallFromCallID(callDataMap_["ALICE"].callId_);
     518           2 :             auto activeMediaList = call->getMediaAttributeList();
     519           2 :             CPPUNIT_ASSERT_EQUAL(offer.size(), activeMediaList.size());
     520             :             // Audio
     521           2 :             CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_AUDIO, activeMediaList[0].type_);
     522           2 :             CPPUNIT_ASSERT_EQUAL(offer[0].enabled_, activeMediaList[0].enabled_);
     523             : 
     524             :             // Video
     525           2 :             if (offer.size() > 1) {
     526           2 :                 CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_VIDEO, activeMediaList[1].type_);
     527           2 :                 CPPUNIT_ASSERT_EQUAL(offer[1].enabled_, activeMediaList[1].enabled_);
     528             :             }
     529           2 :         }
     530             : 
     531             :         // Validate Bob's media
     532           2 :         if (validateMedia) {
     533           4 :             auto call = Manager::instance().getCallFromCallID(callDataMap_["BOB"].callId_);
     534           2 :             auto activeMediaList = call->getMediaAttributeList();
     535           2 :             CPPUNIT_ASSERT_EQUAL(answer.size(), activeMediaList.size());
     536             :             // Audio
     537           2 :             CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_AUDIO, activeMediaList[0].type_);
     538           2 :             CPPUNIT_ASSERT_EQUAL(answer[0].enabled_, activeMediaList[0].enabled_);
     539             : 
     540             :             // Video
     541           2 :             if (offer.size() > 1) {
     542           2 :                 CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_VIDEO, activeMediaList[1].type_);
     543           2 :                 CPPUNIT_ASSERT_EQUAL(answer[1].enabled_, activeMediaList[1].enabled_);
     544             :             }
     545           2 :         }
     546             : 
     547             :         // Give some time to media to start and flow
     548           2 :         std::this_thread::sleep_for(std::chrono::seconds(3));
     549             : 
     550             :         // Bob hang-up.
     551           2 :         JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     552           2 :         libjami::hangUp(callDataMap_["BOB"].accountId_, callDataMap_["BOB"].callId_);
     553             :     } else {
     554             :         // The media negotiation for the call is expected to fail, so we
     555             :         // should receive the signal.
     556           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
     557             :                                      libjami::CallSignal::StateChange::name,
     558             :                                      StateEvent::FAILURE));
     559             :     }
     560             : 
     561             :     // The hang-up signal will be emitted on caller's side (Alice) in both
     562             :     // success failure scenarios.
     563             : 
     564           3 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     565             :                                  libjami::CallSignal::StateChange::name,
     566             :                                  StateEvent::HUNGUP));
     567             : 
     568           3 :     JAMI_INFO("Call terminated on both sides");
     569           3 : }
     570             : 
     571             : void
     572           1 : SipBasicCallTest::audio_only_test()
     573             : {
     574             :     // Test with video enabled on Alice's side and disabled
     575             :     // on Bob's side.
     576             : 
     577           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     578             : 
     579           1 :     auto const aliceAcc = Manager::instance().getAccount<SIPAccount>(
     580           2 :         callDataMap_["ALICE"].accountId_);
     581           2 :     auto const bobAcc = Manager::instance().getAccount<SIPAccount>(callDataMap_["BOB"].accountId_);
     582             : 
     583           1 :     std::vector<MediaAttribute> offer;
     584           1 :     std::vector<MediaAttribute> answer;
     585             : 
     586           1 :     MediaAttribute audio(MediaType::MEDIA_AUDIO);
     587           1 :     MediaAttribute video(MediaType::MEDIA_VIDEO);
     588             : 
     589             :     // Configure Alice
     590           1 :     audio.enabled_ = true;
     591           1 :     audio.label_ = "audio_0";
     592           1 :     offer.emplace_back(audio);
     593             : 
     594           1 :     video.enabled_ = true;
     595           1 :     video.label_ = "video_0";
     596           1 :     aliceAcc->enableVideo(true);
     597           1 :     offer.emplace_back(video);
     598             : 
     599             :     // Configure Bob
     600           1 :     audio.enabled_ = true;
     601           1 :     audio.label_ = "audio_0";
     602           1 :     answer.emplace_back(audio);
     603             : 
     604           1 :     video.enabled_ = false;
     605           1 :     video.label_ = "video_0";
     606           1 :     bobAcc->enableVideo(false);
     607           1 :     answer.emplace_back(video);
     608             : 
     609             :     // Run the scenario
     610           1 :     audio_video_call(offer, answer);
     611           1 : }
     612             : 
     613             : void
     614           1 : SipBasicCallTest::audio_video_test()
     615             : {
     616           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     617             : 
     618           1 :     auto const aliceAcc = Manager::instance().getAccount<SIPAccount>(
     619           2 :         callDataMap_["ALICE"].accountId_);
     620           2 :     auto const bobAcc = Manager::instance().getAccount<SIPAccount>(callDataMap_["BOB"].accountId_);
     621             : 
     622           1 :     std::vector<MediaAttribute> offer;
     623           1 :     std::vector<MediaAttribute> answer;
     624             : 
     625           1 :     MediaAttribute audio(MediaType::MEDIA_AUDIO);
     626           1 :     MediaAttribute video(MediaType::MEDIA_VIDEO);
     627             : 
     628             :     // Configure Alice
     629           1 :     audio.enabled_ = true;
     630           1 :     audio.label_ = "audio_0";
     631           1 :     offer.emplace_back(audio);
     632             : 
     633           1 :     video.enabled_ = true;
     634           1 :     video.label_ = "video_0";
     635           1 :     aliceAcc->enableVideo(true);
     636           1 :     offer.emplace_back(video);
     637             : 
     638             :     // Configure Bob
     639           1 :     audio.enabled_ = true;
     640           1 :     audio.label_ = "audio_0";
     641           1 :     answer.emplace_back(audio);
     642             : 
     643           1 :     video.enabled_ = true;
     644           1 :     video.label_ = "video_0";
     645           1 :     bobAcc->enableVideo(true);
     646           1 :     answer.emplace_back(video);
     647             : 
     648             :     // Run the scenario
     649           1 :     audio_video_call(offer, answer);
     650           1 : }
     651             : 
     652             : void
     653           1 : SipBasicCallTest::peer_answer_with_all_media_disabled()
     654             : {
     655           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     656             : 
     657           2 :     auto const bobAcc = Manager::instance().getAccount<SIPAccount>(callDataMap_["BOB"].accountId_);
     658             : 
     659           1 :     std::vector<MediaAttribute> offer;
     660           1 :     std::vector<MediaAttribute> answer;
     661             : 
     662           1 :     MediaAttribute video(MediaType::MEDIA_VIDEO);
     663             : 
     664             :     // Configure Alice
     665           1 :     video.enabled_ = true;
     666           1 :     video.label_ = "video_0";
     667           1 :     video.secure_ = false;
     668           1 :     offer.emplace_back(video);
     669             : 
     670             :     // Configure Bob
     671           1 :     video.enabled_ = false;
     672           1 :     video.label_ = "video_0";
     673           1 :     video.secure_ = false;
     674           1 :     bobAcc->enableVideo(false);
     675           1 :     answer.emplace_back(video);
     676             : 
     677             :     // Run the scenario
     678           1 :     audio_video_call(offer, answer, false, false);
     679           1 : }
     680             : 
     681             : void
     682           1 : SipBasicCallTest::hold_resume_test()
     683             : {
     684           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     685             : 
     686           1 :     auto const aliceAcc = Manager::instance().getAccount<SIPAccount>(
     687           2 :         callDataMap_["ALICE"].accountId_);
     688           2 :     auto const bobAcc = Manager::instance().getAccount<SIPAccount>(callDataMap_["BOB"].accountId_);
     689             : 
     690           1 :     std::vector<MediaAttribute> offer;
     691           1 :     std::vector<MediaAttribute> answer;
     692             : 
     693           1 :     MediaAttribute audio(MediaType::MEDIA_AUDIO);
     694           1 :     MediaAttribute video(MediaType::MEDIA_VIDEO);
     695             : 
     696           1 :     audio.enabled_ = true;
     697           1 :     audio.label_ = "audio_0";
     698           1 :     video.enabled_ = true;
     699           1 :     video.label_ = "video_0";
     700             : 
     701             :     // Alice's media
     702           1 :     offer.emplace_back(audio);
     703           1 :     offer.emplace_back(video);
     704             : 
     705             :     // Bob's media
     706           1 :     answer.emplace_back(audio);
     707           1 :     answer.emplace_back(video);
     708             : 
     709             :     {
     710           1 :         configureTest();
     711             : 
     712           1 :         JAMI_INFO("=== Start a call and validate ===");
     713             : 
     714           2 :         std::string bobUri = callDataMap_["BOB"].userName_
     715           4 :                              + "@127.0.0.1:" + std::to_string(callDataMap_["BOB"].listeningPort_);
     716             : 
     717           2 :         callDataMap_["ALICE"].callId_
     718           2 :             = libjami::placeCallWithMedia(callDataMap_["ALICE"].accountId_,
     719             :                                         bobUri,
     720           3 :                                         MediaAttribute::mediaAttributesToMediaMaps(offer));
     721             : 
     722           1 :         CPPUNIT_ASSERT(not callDataMap_["ALICE"].callId_.empty());
     723             : 
     724           1 :         JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     725             :                   callDataMap_["ALICE"].accountId_.c_str(),
     726             :                   callDataMap_["BOB"].accountId_.c_str());
     727             : 
     728             :         // Give it some time to ring
     729           1 :         std::this_thread::sleep_for(std::chrono::seconds(2));
     730             : 
     731             :         // Wait for call to be processed.
     732           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     733             :                                      libjami::CallSignal::StateChange::name,
     734             :                                      StateEvent::RINGING));
     735             : 
     736             :         // Wait for incoming call signal.
     737           1 :         CPPUNIT_ASSERT(
     738             :             waitForSignal(callDataMap_["BOB"], libjami::CallSignal::IncomingCallWithMedia::name));
     739             : 
     740             :         // Answer the call.
     741           1 :         libjami::acceptWithMedia(callDataMap_["BOB"].accountId_,
     742           2 :                                callDataMap_["BOB"].callId_,
     743           2 :                                MediaAttribute::mediaAttributesToMediaMaps(answer));
     744             : 
     745             :         // Wait for media negotiation complete signal.
     746           1 :         CPPUNIT_ASSERT(
     747             :             waitForSignal(callDataMap_["BOB"],
     748             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     749             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     750             : 
     751             :         // Wait for the StateChange signal.
     752           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
     753             :                                      libjami::CallSignal::StateChange::name,
     754             :                                      StateEvent::CURRENT));
     755             : 
     756           1 :         JAMI_INFO("BOB answered the call [%s]", callDataMap_["BOB"].callId_.c_str());
     757             : 
     758             :         // Wait for media negotiation complete signal.
     759           1 :         CPPUNIT_ASSERT(
     760             :             waitForSignal(callDataMap_["ALICE"],
     761             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     762             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     763             : 
     764             :         // Validate Alice's side of media direction
     765             :         {
     766             :             auto call = std::static_pointer_cast<SIPCall>(
     767           2 :                 Manager::instance().getCallFromCallID(callDataMap_["ALICE"].callId_));
     768           1 :             auto& sdp = call->getSDP();
     769           1 :             auto mediaStreams = sdp.getMediaSlots();
     770           3 :             for (auto const& media : mediaStreams) {
     771           2 :                 CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.first.direction_);
     772           2 :                 CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.second.direction_);
     773             :             }
     774           1 :         }
     775             : 
     776             :         // Validate Bob's side of media direction
     777             :         {
     778             :             auto call = std::static_pointer_cast<SIPCall>(
     779           2 :                 Manager::instance().getCallFromCallID(callDataMap_["BOB"].callId_));
     780           1 :             auto& sdp = call->getSDP();
     781           1 :             auto mediaStreams = sdp.getMediaSlots();
     782           3 :             for (auto const& media : mediaStreams) {
     783           2 :                 CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.first.direction_);
     784           2 :                 CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.second.direction_);
     785             :             }
     786           1 :         }
     787             : 
     788             :         // Give some time to media to start and flow
     789           1 :         std::this_thread::sleep_for(std::chrono::seconds(2));
     790             : 
     791             :         // Hold/Resume the call
     792           1 :         JAMI_INFO("Hold Alice's call");
     793           1 :         libjami::hold(callDataMap_["ALICE"].accountId_, callDataMap_["ALICE"].callId_);
     794             :         // Wait for the StateChange signal.
     795           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     796             :                                      libjami::CallSignal::StateChange::name,
     797             :                                      StateEvent::HOLD));
     798             : 
     799             :         // Wait for media negotiation complete signal.
     800           1 :         CPPUNIT_ASSERT(
     801             :             waitForSignal(callDataMap_["ALICE"],
     802             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     803             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     804             :         {
     805             :             // Validate hold state.
     806           2 :             auto call = Manager::instance().getCallFromCallID(callDataMap_["ALICE"].callId_);
     807           1 :             auto activeMediaList = call->getMediaAttributeList();
     808           3 :             for (const auto& mediaAttr : activeMediaList) {
     809           2 :                 CPPUNIT_ASSERT(mediaAttr.onHold_);
     810             :             }
     811           1 :         }
     812             : 
     813             :         // Validate Alice's side of media direction
     814             :         {
     815             :             auto call = std::static_pointer_cast<SIPCall>(
     816           2 :                 Manager::instance().getCallFromCallID(callDataMap_["ALICE"].callId_));
     817           1 :             auto& sdp = call->getSDP();
     818           1 :             auto mediaStreams = sdp.getMediaSlots();
     819           3 :             for (auto const& media : mediaStreams) {
     820           2 :                 if (media.first.type == MediaType::MEDIA_AUDIO) {
     821           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.first.direction_);
     822           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.second.direction_);
     823             :                 } else {
     824           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDONLY, media.first.direction_);
     825           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::RECVONLY, media.second.direction_);
     826             :                 }
     827             :             }
     828           1 :         }
     829             : 
     830             :         // Validate Bob's side of media direction
     831             :         {
     832             :             auto call = std::static_pointer_cast<SIPCall>(
     833           2 :                 Manager::instance().getCallFromCallID(callDataMap_["BOB"].callId_));
     834           1 :             auto& sdp = call->getSDP();
     835           1 :             auto mediaStreams = sdp.getMediaSlots();
     836           3 :             for (auto const& media : mediaStreams) {
     837           2 :                 if (media.first.type == MediaType::MEDIA_AUDIO) {
     838           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.first.direction_);
     839           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.second.direction_);
     840             :                 } else {
     841           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::RECVONLY, media.first.direction_);
     842           1 :                     CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDONLY, media.second.direction_);
     843             :                 }
     844             :             }
     845           1 :         }
     846             : 
     847           1 :         std::this_thread::sleep_for(std::chrono::seconds(2));
     848             : 
     849           1 :         JAMI_INFO("Resume Alice's call");
     850           1 :         libjami::unhold(callDataMap_["ALICE"].accountId_, callDataMap_["ALICE"].callId_);
     851             : 
     852             :         // Wait for the StateChange signal.
     853           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     854             :                                      libjami::CallSignal::StateChange::name,
     855             :                                      StateEvent::CURRENT));
     856             : 
     857             :         // Wait for media negotiation complete signal.
     858           1 :         CPPUNIT_ASSERT(
     859             :             waitForSignal(callDataMap_["ALICE"],
     860             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     861             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     862           1 :         std::this_thread::sleep_for(std::chrono::seconds(2));
     863             : 
     864             :         {
     865             :             // Validate hold state.
     866           2 :             auto call = Manager::instance().getCallFromCallID(callDataMap_["ALICE"].callId_);
     867           1 :             auto activeMediaList = call->getMediaAttributeList();
     868           3 :             for (const auto& mediaAttr : activeMediaList) {
     869           2 :                 CPPUNIT_ASSERT(not mediaAttr.onHold_);
     870             :             }
     871           1 :         }
     872             : 
     873             :         // Bob hang-up.
     874           1 :         JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     875           1 :         libjami::hangUp(callDataMap_["BOB"].accountId_, callDataMap_["BOB"].callId_);
     876             : 
     877             :         // The hang-up signal will be emitted on caller's side (Alice) in both
     878             :         // success and failure scenarios.
     879             : 
     880           1 :         CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     881             :                                      libjami::CallSignal::StateChange::name,
     882             :                                      StateEvent::HUNGUP));
     883             : 
     884           1 :         JAMI_INFO("Call terminated on both sides");
     885           1 :     }
     886           1 : }
     887             : 
     888             : void
     889           1 : SipBasicCallTest::blind_transfer_test()
     890             : {
     891             :     // Test a "blind" (a.k.a. unattended) transfer as described in
     892             :     // https://datatracker.ietf.org/doc/html/rfc5589
     893             : 
     894             :     /** Call transfer scenario:
     895             :      *
     896             :      * Alice and Bob are in an active call
     897             :      * Alice performs a call transfer (SIP REFER method) to Carla
     898             :      * Bob automatically accepts the transfer request
     899             :      * Alice ends the call with Bob
     900             :      * Bob send a new call invite to Carla
     901             :      * Carla accepts the call
     902             :      * Carl ends the call
     903             :      *
     904             :      * Here is a simplified version of a call flow from
     905             :      * rfc5589
     906             :      *
     907             :      *
     908             :        Alice                     Bob                      Carla
     909             :          |      REFER             |                        |
     910             :          |----------------------->|                        |
     911             :          |      202 Accepted      |                        |
     912             :          |<-----------------------|                        |
     913             :          |      BYE               |                        |
     914             :          |<-----------------------|                        |
     915             :          |      200 OK            |                        |
     916             :          |----------------------->|                        |
     917             :          |                        |     INVITE             |
     918             :          |                        |----------------------->|
     919             :          |                        |     200 OK             |
     920             :          |                        |<-----------------------|
     921             :      */
     922             : 
     923           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     924             : 
     925           1 :     auto const aliceAcc = Manager::instance().getAccount<SIPAccount>(
     926           2 :         callDataMap_["ALICE"].accountId_);
     927           2 :     auto const bobAcc = Manager::instance().getAccount<SIPAccount>(callDataMap_["BOB"].accountId_);
     928           1 :     auto const carlaAcc = Manager::instance().getAccount<SIPAccount>(
     929           2 :         callDataMap_["CARLA"].accountId_);
     930             : 
     931           1 :     aliceAcc->enableIceForMedia(false);
     932           1 :     bobAcc->enableIceForMedia(false);
     933           1 :     carlaAcc->enableIceForMedia(false);
     934             : 
     935           1 :     std::vector<MediaAttribute> offer;
     936           1 :     std::vector<MediaAttribute> answer;
     937             : 
     938           1 :     MediaAttribute audio(MediaType::MEDIA_AUDIO);
     939           1 :     MediaAttribute video(MediaType::MEDIA_VIDEO);
     940             : 
     941           1 :     audio.enabled_ = true;
     942           1 :     audio.label_ = "audio_0";
     943             : 
     944             :     // Alice's media
     945           1 :     offer.emplace_back(audio);
     946             : 
     947             :     // Bob's media
     948           1 :     answer.emplace_back(audio);
     949             : 
     950           1 :     configureTest();
     951             : 
     952           1 :     JAMI_INFO("=== Start a call and validate ===");
     953             : 
     954           2 :     std::string bobUri = callDataMap_["BOB"].userName_
     955           4 :                          + "@127.0.0.1:" + std::to_string(callDataMap_["BOB"].listeningPort_);
     956             : 
     957           2 :     callDataMap_["ALICE"].callId_
     958           2 :         = libjami::placeCallWithMedia(callDataMap_["ALICE"].accountId_,
     959             :                                     bobUri,
     960           3 :                                     MediaAttribute::mediaAttributesToMediaMaps(offer));
     961             : 
     962           1 :     CPPUNIT_ASSERT(not callDataMap_["ALICE"].callId_.empty());
     963             : 
     964           1 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     965             :               callDataMap_["ALICE"].accountId_.c_str(),
     966             :               callDataMap_["BOB"].accountId_.c_str());
     967             : 
     968             :     // Give it some time to ring
     969           1 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     970             : 
     971             :     // Wait for call to be processed.
     972           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     973             :                                  libjami::CallSignal::StateChange::name,
     974             :                                  StateEvent::RINGING));
     975             : 
     976             :     // Wait for incoming call signal.
     977           1 :     CPPUNIT_ASSERT(
     978             :         waitForSignal(callDataMap_["BOB"], libjami::CallSignal::IncomingCallWithMedia::name));
     979             : 
     980             :     // Answer the call.
     981           1 :     libjami::acceptWithMedia(callDataMap_["BOB"].accountId_,
     982           2 :                            callDataMap_["BOB"].callId_,
     983           2 :                            MediaAttribute::mediaAttributesToMediaMaps(answer));
     984             : 
     985             :     // Wait for media negotiation complete signal.
     986           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
     987             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
     988             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     989             : 
     990             :     // Wait for the StateChange signal.
     991           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
     992             :                                  libjami::CallSignal::StateChange::name,
     993             :                                  StateEvent::CURRENT));
     994             : 
     995           1 :     JAMI_INFO("BOB answered the call [%s]", callDataMap_["BOB"].callId_.c_str());
     996             : 
     997             :     // Wait for media negotiation complete signal.
     998           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
     999             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
    1000             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
    1001             : 
    1002             :     // Give some time to media to start and flow
    1003           1 :     std::this_thread::sleep_for(std::chrono::seconds(2));
    1004             : 
    1005             :     // Transfer the call to Carla
    1006           1 :     std::string carlaUri = carlaAcc->getUsername()
    1007           3 :                            + "@127.0.0.1:" + std::to_string(callDataMap_["CARLA"].listeningPort_);
    1008             : 
    1009           1 :     libjami::transfer(callDataMap_["ALICE"].accountId_,
    1010           2 :                     callDataMap_["ALICE"].callId_,
    1011             :                     carlaUri); // TODO. Check trim
    1012             : 
    1013             :     // Expect Alice's call to end.
    1014           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["ALICE"],
    1015             :                                  libjami::CallSignal::StateChange::name,
    1016             :                                  StateEvent::HUNGUP));
    1017             : 
    1018             :     // Wait for the new call to be processed.
    1019           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
    1020             :                                  libjami::CallSignal::StateChange::name,
    1021             :                                  StateEvent::RINGING));
    1022             : 
    1023             :     // Wait for incoming call signal.
    1024           1 :     CPPUNIT_ASSERT(
    1025             :         waitForSignal(callDataMap_["CARLA"], libjami::CallSignal::IncomingCallWithMedia::name));
    1026             : 
    1027             :     // Let it ring
    1028           1 :     std::this_thread::sleep_for(std::chrono::seconds(2));
    1029             : 
    1030             :     // Carla answers the call.
    1031           1 :     libjami::acceptWithMedia(callDataMap_["CARLA"].accountId_,
    1032           2 :                            callDataMap_["CARLA"].callId_,
    1033           2 :                            MediaAttribute::mediaAttributesToMediaMaps(answer));
    1034             : 
    1035             :     // Wait for media negotiation complete signal.
    1036           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["CARLA"],
    1037             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
    1038             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
    1039             : 
    1040             :     // Wait for the StateChange signal.
    1041           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["CARLA"],
    1042             :                                  libjami::CallSignal::StateChange::name,
    1043             :                                  StateEvent::CURRENT));
    1044             : 
    1045           1 :     JAMI_INFO("CARLA answered the call [%s]", callDataMap_["BOB"].callId_.c_str());
    1046             : 
    1047             :     // Wait for media negotiation complete signal.
    1048           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
    1049             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
    1050             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
    1051             :     // Wait for the StateChange signal.
    1052           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["BOB"],
    1053             :                                  libjami::CallSignal::StateChange::name,
    1054             :                                  StateEvent::CURRENT));
    1055             : 
    1056             :     // Validate Carla's side of media direction
    1057             :     {
    1058             :         auto call = std::static_pointer_cast<SIPCall>(
    1059           2 :             Manager::instance().getCallFromCallID(callDataMap_["CARLA"].callId_));
    1060           1 :         auto& sdp = call->getSDP();
    1061           1 :         auto mediaStreams = sdp.getMediaSlots();
    1062           2 :         for (auto const& media : mediaStreams) {
    1063           1 :             CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.first.direction_);
    1064           1 :             CPPUNIT_ASSERT_EQUAL(MediaDirection::SENDRECV, media.second.direction_);
    1065             :         }
    1066           1 :     }
    1067             : 
    1068             :     // NOTE:
    1069             :     // For now, we dont validate Bob's media because currently
    1070             :     // test does not update BOB's call ID (callDataMap_["BOB"].callId_
    1071             :     // still point to the first call).
    1072             :     // It seems there is no easy way to get the ID of the new call
    1073             :     // made by Bob to Carla.
    1074             : 
    1075             :     // Give some time to media to start and flow
    1076           1 :     std::this_thread::sleep_for(std::chrono::seconds(2));
    1077             : 
    1078             :     // Bob hang-up.
    1079           1 :     JAMI_INFO("Hang up CARLA's call and wait for CARLA to hang up");
    1080           1 :     libjami::hangUp(callDataMap_["CARLA"].accountId_, callDataMap_["CARLA"].callId_);
    1081             : 
    1082             :     // Expect end call on Carla's side.
    1083           1 :     CPPUNIT_ASSERT(waitForSignal(callDataMap_["CARLA"],
    1084             :                                  libjami::CallSignal::StateChange::name,
    1085             :                                  StateEvent::HUNGUP));
    1086             : 
    1087           1 :     JAMI_INFO("Calls normally ended on both sides");
    1088           1 : }
    1089             : 
    1090             : } // namespace test
    1091             : } // namespace jami
    1092             : 
    1093           1 : RING_TEST_RUNNER(jami::test::SipBasicCallTest::name())

Generated by: LCOV version 1.14