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-05-03 08:04:07 Functions: 34 34 100.0 %

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

Generated by: LCOV version 1.14