LCOV - code coverage report
Current view: top level - test/unitTest/media_negotiation - hold_resume.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 264 318 83.0 %
Date: 2024-12-21 08:56:24 Functions: 25 29 86.2 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "manager.h"
      19             : #include "jamidht/jamiaccount.h"
      20             : #include "../../test_runner.h"
      21             : #include "jami.h"
      22             : #include "jami/media_const.h"
      23             : #include "call_const.h"
      24             : #include "account_const.h"
      25             : #include "sip/sipcall.h"
      26             : #include "sip/sdp.h"
      27             : #include "common.h"
      28             : 
      29             : #include <dhtnet/connectionmanager.h>
      30             : 
      31             : #include <cppunit/TestAssert.h>
      32             : #include <cppunit/TestFixture.h>
      33             : #include <cppunit/extensions/HelperMacros.h>
      34             : 
      35             : #include <condition_variable>
      36             : #include <string>
      37             : 
      38             : using namespace libjami::Account;
      39             : using namespace libjami::Call;
      40             : 
      41             : namespace jami {
      42             : namespace test {
      43             : 
      44             : struct TestScenario
      45             : {
      46             :     TestScenario(const std::vector<MediaAttribute>& offer,
      47             :                  const std::vector<MediaAttribute>& answer,
      48             :                  const std::vector<MediaAttribute>& offerUpdate,
      49             :                  const std::vector<MediaAttribute>& answerUpdate)
      50             :         : offer_(std::move(offer))
      51             :         , answer_(std::move(answer))
      52             :         , offerUpdate_(std::move(offerUpdate))
      53             :         , answerUpdate_(std::move(answerUpdate))
      54             :     {}
      55             : 
      56           2 :     TestScenario() {};
      57             : 
      58             :     std::vector<MediaAttribute> offer_;
      59             :     std::vector<MediaAttribute> answer_;
      60             :     std::vector<MediaAttribute> offerUpdate_;
      61             :     std::vector<MediaAttribute> answerUpdate_;
      62             :     // Determine if we should expect the MediaNegotiationStatus signal.
      63             :     bool expectMediaRenegotiation_ {false};
      64             : };
      65             : 
      66             : struct CallData
      67             : {
      68             :     struct Signal
      69             :     {
      70          36 :         Signal(const std::string& name, const std::string& event = {})
      71          36 :             : name_(std::move(name))
      72          36 :             , event_(std::move(event)) {};
      73             : 
      74             :         std::string name_ {};
      75             :         std::string event_ {};
      76             :     };
      77             : 
      78             :     std::string accountId_ {};
      79             :     std::string userName_ {};
      80             :     std::string alias_ {};
      81             :     std::string callId_ {};
      82             :     std::vector<Signal> signals_;
      83             :     std::condition_variable cv_ {};
      84             :     std::mutex mtx_;
      85             : };
      86             : 
      87             : /**
      88             :  * Basic tests for media negotiation.
      89             :  */
      90             : class HoldResumeTest : public CppUnit::TestFixture
      91             : {
      92             : public:
      93           2 :     HoldResumeTest()
      94           2 :     {
      95             :         // Init daemon
      96           2 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      97           2 :         if (not Manager::instance().initialized)
      98           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      99           2 :     }
     100           4 :     ~HoldResumeTest() { libjami::fini(); }
     101             : 
     102           2 :     static std::string name() { return "HoldResumeTest"; }
     103             :     void setUp();
     104             :     void tearDown();
     105             : 
     106             : private:
     107             :     // Test cases.
     108             :     void audio_and_video_then_hold_resume();
     109             :     void audio_only_then_hold_resume();
     110             : 
     111           2 :     CPPUNIT_TEST_SUITE(HoldResumeTest);
     112           1 :     CPPUNIT_TEST(audio_and_video_then_hold_resume);
     113           1 :     CPPUNIT_TEST(audio_only_then_hold_resume);
     114           4 :     CPPUNIT_TEST_SUITE_END();
     115             : 
     116             :     // Event/Signal handlers
     117             :     static void onCallStateChange(const std::string& callId,
     118             :                                   const std::string& state,
     119             :                                   CallData& callData);
     120             :     static void onIncomingCallWithMedia(const std::string& accountId,
     121             :                                         const std::string& callId,
     122             :                                         const std::vector<libjami::MediaMap> mediaList,
     123             :                                         CallData& callData);
     124             :     // For backward compatibility test cases
     125             :     static void onIncomingCall(const std::string& accountId,
     126             :                                const std::string& callId,
     127             :                                CallData& callData);
     128             :     static void onMediaChangeRequested(const std::string& accountId,
     129             :                                        const std::string& callId,
     130             :                                        const std::vector<libjami::MediaMap> mediaList,
     131             :                                        CallData& callData);
     132             :     static void onMediaNegotiationStatus(const std::string& callId,
     133             :                                          const std::string& event,
     134             :                                          CallData& callData);
     135             : 
     136             :     // Helpers
     137             :     static void configureScenario(CallData& bob, CallData& alice);
     138             :     void testWithScenario(CallData& aliceData, CallData& bobData, const TestScenario& scenario);
     139             :     static std::string getUserAlias(const std::string& callId);
     140             :     // Wait for a signal from the callbacks. Some signals also report the event that
     141             :     // triggered the signal a like the StateChange signal.
     142             :     static bool waitForSignal(CallData& callData,
     143             :                               const std::string& signal,
     144             :                               const std::string& expectedEvent = {});
     145             : 
     146             : private:
     147             :     CallData aliceData_;
     148             :     CallData bobData_;
     149             : };
     150             : 
     151             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(HoldResumeTest, HoldResumeTest::name());
     152             : 
     153             : void
     154           2 : HoldResumeTest::setUp()
     155             : {
     156           2 :     auto actors = load_actors("actors/alice-bob-no-upnp.yml");
     157             : 
     158           2 :     aliceData_.accountId_ = actors["alice"];
     159           2 :     bobData_.accountId_ = actors["bob"];
     160             : 
     161           2 :     JAMI_INFO("Initialize account...");
     162           2 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceData_.accountId_);
     163           2 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobData_.accountId_);
     164             : 
     165           6 :     wait_for_announcement_of({aliceAccount->getAccountID(), bobAccount->getAccountID()});
     166           2 : }
     167             : 
     168             : void
     169           2 : HoldResumeTest::tearDown()
     170             : {
     171           6 :     wait_for_removal_of({aliceData_.accountId_, bobData_.accountId_});
     172           2 : }
     173             : 
     174             : std::string
     175          38 : HoldResumeTest::getUserAlias(const std::string& callId)
     176             : {
     177          38 :     auto call = Manager::instance().getCallFromCallID(callId);
     178             : 
     179          38 :     if (not call) {
     180           2 :         JAMI_WARN("Call with ID [%s] does not exist anymore!", callId.c_str());
     181           2 :         return {};
     182             :     }
     183             : 
     184          36 :     auto const& account = call->getAccount().lock();
     185          36 :     if (not account) {
     186           0 :         return {};
     187             :     }
     188             : 
     189          36 :     return account->getAccountDetails()[ConfProperties::ALIAS];
     190          38 : }
     191             : 
     192             : void
     193           2 : HoldResumeTest::onIncomingCallWithMedia(const std::string& accountId,
     194             :                                         const std::string& callId,
     195             :                                         const std::vector<libjami::MediaMap> mediaList,
     196             :                                         CallData& callData)
     197             : {
     198           2 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     199             : 
     200           2 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     201             :               libjami::CallSignal::IncomingCallWithMedia::name,
     202             :               callData.alias_.c_str(),
     203             :               callId.c_str(),
     204             :               mediaList.size());
     205             : 
     206           2 :     if (not Manager::instance().getCallFromCallID(callId)) {
     207           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     208           0 :         callData.callId_ = {};
     209           0 :         return;
     210             :     }
     211             : 
     212           2 :     std::unique_lock lock {callData.mtx_};
     213           2 :     callData.callId_ = callId;
     214           2 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCallWithMedia::name));
     215             : 
     216           2 :     callData.cv_.notify_one();
     217           2 : }
     218             : 
     219             : void
     220           0 : HoldResumeTest::onIncomingCall(const std::string& accountId,
     221             :                                const std::string& callId,
     222             :                                CallData& callData)
     223             : {
     224           0 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     225             : 
     226           0 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s]",
     227             :               libjami::CallSignal::IncomingCall::name,
     228             :               callData.alias_.c_str(),
     229             :               callId.c_str());
     230             : 
     231           0 :     if (not Manager::instance().getCallFromCallID(callId)) {
     232           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     233           0 :         callData.callId_ = {};
     234           0 :         return;
     235             :     }
     236             : 
     237           0 :     std::unique_lock lock {callData.mtx_};
     238           0 :     callData.callId_ = callId;
     239           0 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCall::name));
     240             : 
     241           0 :     callData.cv_.notify_one();
     242           0 : }
     243             : 
     244             : void
     245           0 : HoldResumeTest::onMediaChangeRequested(const std::string& accountId,
     246             :                                        const std::string& callId,
     247             :                                        const std::vector<libjami::MediaMap> mediaList,
     248             :                                        CallData& callData)
     249             : {
     250           0 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     251             : 
     252           0 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     253             :               libjami::CallSignal::MediaChangeRequested::name,
     254             :               callData.alias_.c_str(),
     255             :               callId.c_str(),
     256             :               mediaList.size());
     257             : 
     258           0 :     if (not Manager::instance().getCallFromCallID(callId)) {
     259           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     260           0 :         callData.callId_ = {};
     261           0 :         return;
     262             :     }
     263             : 
     264           0 :     std::unique_lock lock {callData.mtx_};
     265           0 :     callData.callId_ = callId;
     266           0 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::MediaChangeRequested::name));
     267             : 
     268           0 :     callData.cv_.notify_one();
     269           0 : }
     270             : 
     271             : void
     272          22 : HoldResumeTest::onCallStateChange(const std::string& callId,
     273             :                                   const std::string& state,
     274             :                                   CallData& callData)
     275             : {
     276          22 :     auto call = Manager::instance().getCallFromCallID(callId);
     277          22 :     if (not call) {
     278           0 :         JAMI_WARN("Call with ID [%s] does not exist anymore!", callId.c_str());
     279           0 :         return;
     280             :     }
     281             : 
     282          22 :     auto account = call->getAccount().lock();
     283          22 :     if (not account) {
     284           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     285           0 :         return;
     286             :     }
     287             : 
     288          22 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     289             :               libjami::CallSignal::StateChange::name,
     290             :               callData.alias_.c_str(),
     291             :               callId.c_str(),
     292             :               state.c_str());
     293             : 
     294          22 :     if (account->getAccountID() != callData.accountId_)
     295           0 :         return;
     296             : 
     297             :     {
     298          22 :         std::unique_lock lock {callData.mtx_};
     299          22 :         callData.signals_.emplace_back(
     300          44 :             CallData::Signal(libjami::CallSignal::StateChange::name, state));
     301          22 :     }
     302             : 
     303          22 :     if (state == "CURRENT" or state == "OVER" or state == "HUNGUP") {
     304          10 :         callData.cv_.notify_one();
     305             :     }
     306          22 : }
     307             : 
     308             : void
     309          12 : HoldResumeTest::onMediaNegotiationStatus(const std::string& callId,
     310             :                                          const std::string& event,
     311             :                                          CallData& callData)
     312             : {
     313          12 :     auto call = Manager::instance().getCallFromCallID(callId);
     314          12 :     if (not call) {
     315           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     316           0 :         return;
     317             :     }
     318             : 
     319          12 :     auto account = call->getAccount().lock();
     320          12 :     if (not account) {
     321           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     322           0 :         return;
     323             :     }
     324             : 
     325          12 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     326             :               libjami::CallSignal::MediaNegotiationStatus::name,
     327             :               account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
     328             :               call->getCallId().c_str(),
     329             :               event.c_str());
     330             : 
     331          12 :     if (account->getAccountID() != callData.accountId_)
     332           0 :         return;
     333             : 
     334             :     {
     335          12 :         std::unique_lock lock {callData.mtx_};
     336          12 :         callData.signals_.emplace_back(
     337          24 :             CallData::Signal(libjami::CallSignal::MediaNegotiationStatus::name, event));
     338          12 :     }
     339             : 
     340          12 :     callData.cv_.notify_one();
     341          12 : }
     342             : 
     343             : bool
     344          14 : HoldResumeTest::waitForSignal(CallData& callData,
     345             :                               const std::string& expectedSignal,
     346             :                               const std::string& expectedEvent)
     347             : {
     348          14 :     const std::chrono::seconds TIME_OUT {30};
     349          14 :     std::unique_lock lock {callData.mtx_};
     350             : 
     351             :     // Combined signal + event (if any).
     352          14 :     std::string sigEvent(expectedSignal);
     353          14 :     if (not expectedEvent.empty())
     354          12 :         sigEvent += "::" + expectedEvent;
     355             : 
     356          14 :     JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
     357             : 
     358          14 :     auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
     359             :         // Search for the expected signal in list of received signals.
     360          26 :         bool pred = false;
     361         122 :         for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
     362             :             // The predicate is true if the signal names match, and if the
     363             :             // expectedEvent is not empty, the events must also match.
     364         110 :             if (it->name_ == expectedSignal
     365         110 :                 and (expectedEvent.empty() or it->event_ == expectedEvent)) {
     366          14 :                 pred = true;
     367             :                 // Done with this signal.
     368          14 :                 callData.signals_.erase(it);
     369          14 :                 break;
     370             :             }
     371             :         }
     372             : 
     373          26 :         return pred;
     374             :     });
     375             : 
     376          14 :     if (not res) {
     377           0 :         JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
     378             :                  callData.alias_.c_str(),
     379             :                  sigEvent.c_str());
     380             : 
     381           0 :         JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
     382             : 
     383           0 :         for (auto const& sig : callData.signals_) {
     384           0 :             JAMI_INFO() << "Signal [" << sig.name_
     385           0 :                         << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
     386             :         }
     387             :     }
     388             : 
     389          14 :     return res;
     390          14 : }
     391             : 
     392             : void
     393           2 : HoldResumeTest::configureScenario(CallData& aliceData, CallData& bobData)
     394             : {
     395             :     {
     396           2 :         CPPUNIT_ASSERT(not aliceData.accountId_.empty());
     397           2 :         auto const& account = Manager::instance().getAccount<JamiAccount>(aliceData.accountId_);
     398           2 :         aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     399           2 :         aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     400           2 :     }
     401             : 
     402             :     {
     403           2 :         CPPUNIT_ASSERT(not bobData.accountId_.empty());
     404           2 :         auto const& account = Manager::instance().getAccount<JamiAccount>(bobData.accountId_);
     405           2 :         bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     406           2 :         bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     407           2 :     }
     408             : 
     409           2 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers;
     410             : 
     411             :     // Insert needed signal handlers.
     412           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     413           2 :         [&](const std::string& accountId,
     414             :             const std::string& callId,
     415             :             const std::string&,
     416             :             const std::vector<libjami::MediaMap> mediaList) {
     417           2 :             auto user = getUserAlias(callId);
     418           2 :             if (not user.empty())
     419           2 :                 onIncomingCallWithMedia(accountId,
     420             :                                         callId,
     421             :                                         mediaList,
     422           2 :                                         user == aliceData.alias_ ? aliceData : bobData);
     423           2 :         }));
     424             : 
     425           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCall>(
     426           0 :         [&](const std::string& accountId, const std::string& callId, const std::string&) {
     427           0 :             auto user = getUserAlias(callId);
     428           0 :             if (not user.empty()) {
     429           0 :                 onIncomingCall(accountId, callId, user == aliceData.alias_ ? aliceData : bobData);
     430             :             }
     431           0 :         }));
     432             : 
     433           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaChangeRequested>(
     434           0 :         [&](const std::string& accountId,
     435             :             const std::string& callId,
     436             :             const std::vector<libjami::MediaMap> mediaList) {
     437           0 :             auto user = getUserAlias(callId);
     438           0 :             if (not user.empty())
     439           0 :                 onMediaChangeRequested(accountId,
     440             :                                        callId,
     441             :                                        mediaList,
     442           0 :                                        user == aliceData.alias_ ? aliceData : bobData);
     443           0 :         }));
     444             : 
     445           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::StateChange>(
     446          24 :         [&](const std::string&, const std::string& callId, const std::string& state, signed) {
     447          24 :             auto user = getUserAlias(callId);
     448          24 :             if (not user.empty())
     449          22 :                 onCallStateChange(callId, state, user == aliceData.alias_ ? aliceData : bobData);
     450          24 :         }));
     451             : 
     452           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
     453          12 :         [&](const std::string& callId,
     454             :             const std::string& event,
     455             :             const std::vector<std::map<std::string, std::string>>&) {
     456          12 :             auto user = getUserAlias(callId);
     457          12 :             if (not user.empty())
     458          12 :                 onMediaNegotiationStatus(callId,
     459             :                                          event,
     460          12 :                                          user == aliceData.alias_ ? aliceData : bobData);
     461          12 :         }));
     462             : 
     463           2 :     libjami::registerSignalHandlers(signalHandlers);
     464           2 : }
     465             : 
     466             : void
     467           2 : HoldResumeTest::testWithScenario(CallData& aliceData,
     468             :                                  CallData& bobData,
     469             :                                  const TestScenario& scenario)
     470             : {
     471           2 :     JAMI_INFO("=== Start a call and validate ===");
     472             : 
     473             :     // The media count of the offer and answer must match (RFC-3264).
     474           2 :     auto mediaCount = scenario.offer_.size();
     475           2 :     CPPUNIT_ASSERT_EQUAL(mediaCount, scenario.answer_.size());
     476             : 
     477             :     auto const& aliceCall = std::dynamic_pointer_cast<SIPCall>(
     478           2 :         (Manager::instance().getAccount<JamiAccount>(aliceData.accountId_))
     479           4 :             ->newOutgoingCall(bobData.userName_,
     480           4 :                               MediaAttribute::mediaAttributesToMediaMaps(scenario.offer_)));
     481           2 :     CPPUNIT_ASSERT(aliceCall);
     482           2 :     aliceData.callId_ = aliceCall->getCallId();
     483             : 
     484           2 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     485             :               aliceData.accountId_.c_str(),
     486             :               bobData.accountId_.c_str());
     487             : 
     488             :     // Wait for incoming call signal.
     489           2 :     CPPUNIT_ASSERT(waitForSignal(bobData, libjami::CallSignal::IncomingCallWithMedia::name));
     490             : 
     491             :     // Answer the call.
     492             :     {
     493           2 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.answer_);
     494           2 :         Manager::instance().answerCall(bobData.accountId_, bobData.callId_, mediaList);
     495           2 :     }
     496             : 
     497             :     // Wait for media negotiation complete signal.
     498           2 :     CPPUNIT_ASSERT_EQUAL(
     499             :         true,
     500             :         waitForSignal(bobData,
     501             :                       libjami::CallSignal::MediaNegotiationStatus::name,
     502             :                       libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     503             :     // Wait for the StateChange signal.
     504           2 :     CPPUNIT_ASSERT_EQUAL(true,
     505             :                          waitForSignal(bobData,
     506             :                                        libjami::CallSignal::StateChange::name,
     507             :                                        StateEvent::CURRENT));
     508             : 
     509           2 :     JAMI_INFO("BOB answered the call [%s]", bobData.callId_.c_str());
     510             : 
     511             :     // Wait for media negotiation complete signal.
     512           2 :     CPPUNIT_ASSERT_EQUAL(
     513             :         true,
     514             :         waitForSignal(aliceData,
     515             :                       libjami::CallSignal::MediaNegotiationStatus::name,
     516             :                       libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     517             : 
     518             :     // Validate Alice's media and SDP
     519             :     {
     520           2 :         auto mediaAttr = aliceCall->getMediaAttributeList();
     521           2 :         CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     522           5 :         for (size_t idx = 0; idx < mediaCount; idx++) {
     523           3 :             CPPUNIT_ASSERT_EQUAL(scenario.offer_[idx].muted_, mediaAttr[idx].muted_);
     524             :         }
     525             :         // Check media direction
     526           2 :         auto& sdp = aliceCall->getSDP();
     527           2 :         auto mediaStreams = sdp.getMediaSlots();
     528           5 :         for (auto const& media : mediaStreams) {
     529           3 :             CPPUNIT_ASSERT_EQUAL(media.first.direction_, MediaDirection::SENDRECV);
     530           3 :             CPPUNIT_ASSERT_EQUAL(media.second.direction_, MediaDirection::SENDRECV);
     531             :         }
     532           2 :     }
     533             : 
     534             :     // Validate Bob's media
     535             :     {
     536             :         auto const& bobCall = std::dynamic_pointer_cast<SIPCall>(
     537           2 :             Manager::instance().getCallFromCallID(bobData.callId_));
     538           2 :         auto mediaAttr = bobCall->getMediaAttributeList();
     539           2 :         CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     540           5 :         for (size_t idx = 0; idx < mediaCount; idx++) {
     541           3 :             CPPUNIT_ASSERT_EQUAL(scenario.answer_[idx].muted_, mediaAttr[idx].muted_);
     542             :         }
     543             :         // Check media direction
     544           2 :         auto& sdp = bobCall->getSDP();
     545           2 :         auto mediaStreams = sdp.getMediaSlots();
     546           5 :         for (auto const& media : mediaStreams) {
     547           3 :             CPPUNIT_ASSERT_EQUAL(media.first.direction_, MediaDirection::SENDRECV);
     548           3 :             CPPUNIT_ASSERT_EQUAL(media.second.direction_, MediaDirection::SENDRECV);
     549             :         }
     550           2 :     }
     551             : 
     552           2 :     std::this_thread::sleep_for(std::chrono::seconds(3));
     553             : 
     554           2 :     JAMI_INFO("=== Hold the call and validate ===");
     555             :     {
     556           2 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.offerUpdate_);
     557           2 :         libjami::hold(aliceData.accountId_, aliceData.callId_);
     558           2 :     }
     559             : 
     560             :     // Update and validate media count.
     561           2 :     mediaCount = scenario.offerUpdate_.size();
     562           2 :     CPPUNIT_ASSERT_EQUAL(mediaCount, scenario.answerUpdate_.size());
     563             : 
     564           2 :     if (scenario.expectMediaRenegotiation_) {
     565             :         // Wait for media negotiation complete signal.
     566           2 :         CPPUNIT_ASSERT_EQUAL(
     567             :             true,
     568             :             waitForSignal(aliceData,
     569             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     570             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     571             : 
     572             :         // Validate Alice's media
     573             :         {
     574           2 :             auto mediaAttr = aliceCall->getMediaAttributeList();
     575           2 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     576           5 :             for (size_t idx = 0; idx < mediaCount; idx++) {
     577           3 :                 CPPUNIT_ASSERT(mediaAttr[idx].onHold_);
     578           3 :                 CPPUNIT_ASSERT_EQUAL(scenario.offerUpdate_[idx].muted_, mediaAttr[idx].muted_);
     579             :                 // Check isCaptureDeviceMuted API
     580           3 :                 CPPUNIT_ASSERT_EQUAL(mediaAttr[idx].muted_,
     581             :                                      aliceCall->isCaptureDeviceMuted(mediaAttr[idx].type_));
     582             :             }
     583           2 :         }
     584             : 
     585             :         // Validate Bob's media
     586             :         {
     587             :             auto const& bobCall = std::dynamic_pointer_cast<SIPCall>(
     588           2 :                 Manager::instance().getCallFromCallID(bobData.callId_));
     589           2 :             auto mediaAttr = bobCall->getMediaAttributeList();
     590           2 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     591           5 :             for (size_t idx = 0; idx < mediaCount; idx++) {
     592           3 :                 CPPUNIT_ASSERT(not mediaAttr[idx].onHold_);
     593           3 :                 CPPUNIT_ASSERT_EQUAL(scenario.answerUpdate_[idx].muted_, mediaAttr[idx].muted_);
     594             :                 // Check isCaptureDeviceMuted API
     595           3 :                 CPPUNIT_ASSERT_EQUAL(mediaAttr[idx].muted_,
     596             :                                      bobCall->isCaptureDeviceMuted(mediaAttr[idx].type_));
     597             :             }
     598           2 :         }
     599             :     }
     600             : 
     601           2 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     602             : 
     603           2 :     JAMI_INFO("=== Resume the call and validate ===");
     604             :     {
     605           2 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.offerUpdate_);
     606           2 :         libjami::unhold(aliceData.accountId_, aliceData.callId_);
     607           2 :     }
     608             : 
     609             :     // Update and validate media count.
     610           2 :     mediaCount = scenario.offerUpdate_.size();
     611           2 :     CPPUNIT_ASSERT_EQUAL(mediaCount, scenario.answerUpdate_.size());
     612             : 
     613           2 :     if (scenario.expectMediaRenegotiation_) {
     614             :         // Wait for media negotiation complete signal.
     615           2 :         CPPUNIT_ASSERT_EQUAL(
     616             :             true,
     617             :             waitForSignal(aliceData,
     618             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     619             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     620             :         // Validate Alice's media
     621             :         {
     622           2 :             auto mediaAttr = aliceCall->getMediaAttributeList();
     623           2 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     624           5 :             for (size_t idx = 0; idx < mediaCount; idx++) {
     625           3 :                 CPPUNIT_ASSERT(not mediaAttr[idx].onHold_);
     626           3 :                 CPPUNIT_ASSERT_EQUAL(scenario.offerUpdate_[idx].muted_, mediaAttr[idx].muted_);
     627             :                 // Check isCaptureDeviceMuted API
     628           3 :                 CPPUNIT_ASSERT_EQUAL(mediaAttr[idx].muted_,
     629             :                                      aliceCall->isCaptureDeviceMuted(mediaAttr[idx].type_));
     630             :             }
     631           2 :         }
     632             : 
     633             :         // Validate Bob's media
     634             :         {
     635             :             auto const& bobCall = std::dynamic_pointer_cast<SIPCall>(
     636           2 :                 Manager::instance().getCallFromCallID(bobData.callId_));
     637           2 :             auto mediaAttr = bobCall->getMediaAttributeList();
     638           2 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaAttr.size());
     639           5 :             for (size_t idx = 0; idx < mediaCount; idx++) {
     640           3 :                 CPPUNIT_ASSERT(not mediaAttr[idx].onHold_);
     641           3 :                 CPPUNIT_ASSERT_EQUAL(scenario.answerUpdate_[idx].muted_, mediaAttr[idx].muted_);
     642             :                 // Check isCaptureDeviceMuted API
     643           3 :                 CPPUNIT_ASSERT_EQUAL(mediaAttr[idx].muted_,
     644             :                                      bobCall->isCaptureDeviceMuted(mediaAttr[idx].type_));
     645             :             }
     646           2 :         }
     647             :     }
     648             : 
     649           2 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     650             : 
     651             :     // Bob hang-up.
     652           2 :     JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     653           2 :     Manager::instance().hangupCall(bobData.accountId_, bobData.callId_);
     654             : 
     655           2 :     CPPUNIT_ASSERT_EQUAL(true,
     656             :                          waitForSignal(aliceData,
     657             :                                        libjami::CallSignal::StateChange::name,
     658             :                                        StateEvent::HUNGUP));
     659             : 
     660           2 :     JAMI_INFO("Call terminated on both sides");
     661           2 : }
     662             : 
     663             : void
     664           1 : HoldResumeTest::audio_and_video_then_hold_resume()
     665             : {
     666           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     667             : 
     668           1 :     configureScenario(aliceData_, bobData_);
     669             : 
     670           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     671           1 :     defaultAudio.label_ = "audio_0";
     672           1 :     defaultAudio.enabled_ = true;
     673             : 
     674           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
     675           1 :     defaultVideo.label_ = "video_0";
     676           1 :     defaultVideo.enabled_ = true;
     677             : 
     678           1 :     MediaAttribute audio(defaultAudio);
     679           1 :     MediaAttribute video(defaultVideo);
     680             : 
     681           1 :     TestScenario scenario;
     682             :     // First offer/answer
     683           1 :     scenario.offer_.emplace_back(audio);
     684           1 :     scenario.offer_.emplace_back(video);
     685           1 :     scenario.answer_.emplace_back(audio);
     686           1 :     scenario.answer_.emplace_back(video);
     687             : 
     688             :     // Updated offer/answer
     689           1 :     scenario.offerUpdate_.emplace_back(audio);
     690           1 :     scenario.offerUpdate_.emplace_back(video);
     691             : 
     692           1 :     scenario.answerUpdate_.emplace_back(audio);
     693           1 :     scenario.answerUpdate_.emplace_back(video);
     694           1 :     scenario.expectMediaRenegotiation_ = true;
     695             : 
     696           1 :     testWithScenario(aliceData_, bobData_, scenario);
     697             : 
     698           1 :     libjami::unregisterSignalHandlers();
     699             : 
     700           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
     701           1 : }
     702             : 
     703             : void
     704           1 : HoldResumeTest::audio_only_then_hold_resume()
     705             : {
     706           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     707             : 
     708           1 :     configureScenario(aliceData_, bobData_);
     709             : 
     710           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     711           1 :     defaultAudio.label_ = "audio_0";
     712           1 :     defaultAudio.enabled_ = true;
     713             : 
     714           1 :     MediaAttribute audio(defaultAudio);
     715             : 
     716           1 :     TestScenario scenario;
     717             :     // First offer/answer
     718           1 :     scenario.offer_.emplace_back(audio);
     719           1 :     scenario.answer_.emplace_back(audio);
     720             : 
     721             :     // Updated offer/answer
     722           1 :     scenario.offerUpdate_.emplace_back(audio);
     723           1 :     scenario.answerUpdate_.emplace_back(audio);
     724           1 :     scenario.expectMediaRenegotiation_ = true;
     725             : 
     726           1 :     testWithScenario(aliceData_, bobData_, scenario);
     727             : 
     728           1 :     libjami::unregisterSignalHandlers();
     729             : 
     730           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
     731           1 : }
     732             : 
     733             : } // namespace test
     734             : } // namespace jami
     735             : 
     736           1 : RING_TEST_RUNNER(jami::test::HoldResumeTest::name())

Generated by: LCOV version 1.14