LCOV - code coverage report
Current view: top level - test/unitTest/media_negotiation - media_negotiation.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 456 580 78.6 %
Date: 2024-04-19 08:05:40 Functions: 44 55 80.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 "manager.h"
      21             : #include "jamidht/jamiaccount.h"
      22             : #include "sip/sipaccount.h"
      23             : #include "../../test_runner.h"
      24             : #include "jami.h"
      25             : #include "jami/media_const.h"
      26             : #include "call_const.h"
      27             : #include "account_const.h"
      28             : #include "sip/sipcall.h"
      29             : #include "sip/sdp.h"
      30             : 
      31             : #include "common.h"
      32             : 
      33             : #include <dhtnet/connectionmanager.h>
      34             : 
      35             : #include <cppunit/TestAssert.h>
      36             : #include <cppunit/TestFixture.h>
      37             : #include <cppunit/extensions/HelperMacros.h>
      38             : 
      39             : #include <condition_variable>
      40             : #include <string>
      41             : 
      42             : using namespace libjami::Account;
      43             : using namespace libjami::Call;
      44             : 
      45             : namespace jami {
      46             : namespace test {
      47             : 
      48             : struct TestScenario
      49             : {
      50             :     TestScenario(const std::vector<MediaAttribute>& offer,
      51             :                  const std::vector<MediaAttribute>& answer,
      52             :                  const std::vector<MediaAttribute>& offerUpdate,
      53             :                  const std::vector<MediaAttribute>& answerUpdate)
      54             :         : offer_(std::move(offer))
      55             :         , answer_(std::move(answer))
      56             :         , offerUpdate_(std::move(offerUpdate))
      57             :         , answerUpdate_(std::move(answerUpdate))
      58             :     {}
      59             : 
      60           6 :     TestScenario() {};
      61             : 
      62             :     std::vector<MediaAttribute> offer_;
      63             :     std::vector<MediaAttribute> answer_;
      64             :     std::vector<MediaAttribute> offerUpdate_;
      65             :     std::vector<MediaAttribute> answerUpdate_;
      66             :     // Determine if we should expect the MediaNegotiationStatus signal.
      67             :     bool expectMediaRenegotiation_ {false};
      68             :     // Determine if we should expect the MediaChangeRequested signal.
      69             :     bool expectMediaChangeRequest_ {false};
      70             : };
      71             : 
      72             : struct CallData
      73             : {
      74             :     struct Signal
      75             :     {
      76          95 :         Signal(const std::string& name, const std::string& event = {})
      77          95 :             : name_(std::move(name))
      78          95 :             , event_(std::move(event)) {};
      79             : 
      80             :         std::string name_ {};
      81             :         std::string event_ {};
      82             :     };
      83             : 
      84          12 :     CallData() = default;
      85             :     CallData(CallData&& other) = delete;
      86           0 :     CallData(const CallData& other)
      87           0 :     {
      88           0 :         accountId_ = std::move(other.accountId_);
      89           0 :         listeningPort_ = other.listeningPort_;
      90           0 :         userName_ = std::move(other.userName_);
      91           0 :         alias_ = std::move(other.alias_);
      92           0 :         callId_ = std::move(other.callId_);
      93           0 :         signals_ = std::move(other.signals_);
      94           0 :     };
      95             : 
      96             :     std::string accountId_ {};
      97             :     std::string userName_ {};
      98             :     std::string alias_ {};
      99             :     uint16_t listeningPort_ {0};
     100             :     std::string toUri_ {};
     101             :     std::string callId_ {};
     102             :     std::vector<Signal> signals_;
     103             :     std::condition_variable cv_ {};
     104             :     std::mutex mtx_;
     105             : };
     106             : 
     107             : /**
     108             :  * Basic tests for media negotiation.
     109             :  */
     110             : class MediaNegotiationTest
     111             : {
     112             : public:
     113           6 :     MediaNegotiationTest()
     114           6 :     {
     115             :         // Init daemon
     116           6 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
     117           6 :         if (not Manager::instance().initialized)
     118           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
     119           6 :     }
     120           6 :     ~MediaNegotiationTest() { libjami::fini(); }
     121             : 
     122             :     static std::string name() { return "MediaNegotiationTest"; }
     123             : 
     124             : protected:
     125             :     // Test cases.
     126             :     void audio_and_video_then_caller_mute_video();
     127             :     void audio_only_then_caller_add_video();
     128             :     void audio_and_video_then_caller_mute_audio();
     129             :     void audio_and_video_answer_muted_video_then_mute_video();
     130             :     void audio_and_video_then_change_video_source();
     131             :     void negotiate_2_videos_1_audio();
     132             : 
     133             :     // Event/Signal handlers
     134             :     static void onCallStateChange(const std::string& accountId,
     135             :                                   const std::string& callId,
     136             :                                   const std::string& state,
     137             :                                   CallData& callData);
     138             :     static void onIncomingCallWithMedia(const std::string& accountId,
     139             :                                         const std::string& callId,
     140             :                                         const std::vector<libjami::MediaMap> mediaList,
     141             :                                         CallData& callData);
     142             :     // For backward compatibility test cases.
     143             :     // TODO. Do we still need this?
     144             :     static void onIncomingCall(const std::string& accountId,
     145             :                                const std::string& callId,
     146             :                                CallData& callData);
     147             :     static void onMediaChangeRequested(const std::string& accountId,
     148             :                                        const std::string& callId,
     149             :                                        const std::vector<libjami::MediaMap> mediaList,
     150             :                                        CallData& callData);
     151             :     static void onVideoMuted(const std::string& callId, bool muted, CallData& callData);
     152             :     static void onMediaNegotiationStatus(const std::string& callId,
     153             :                                          const std::string& event,
     154             :                                          CallData& callData);
     155             : 
     156             :     // Helpers
     157             :     void configureScenario();
     158             :     void testWithScenario(CallData& aliceData, CallData& bobData, const TestScenario& scenario);
     159             :     std::string getAccountId(const std::string& callId);
     160             :     std::string getUserAlias(const std::string& callId);
     161             :     // Infer media direction of an offer.
     162             :     static uint8_t directionToBitset(MediaDirection direction, bool isLocal);
     163             :     static MediaDirection bitsetToDirection(uint8_t val);
     164             :     static MediaDirection inferInitialDirection(const MediaAttribute& offer);
     165             :     // Infer media direction of an answer.
     166             :     static MediaDirection inferNegotiatedDirection(MediaDirection local, MediaDirection answer);
     167             :     // Wait for a signal from the callbacks. Some signals also report the event that
     168             :     // triggered the signal a like the StateChange signal.
     169             :     static bool validateMuteState(std::vector<MediaAttribute> expected,
     170             :                                   std::vector<MediaAttribute> actual);
     171             :     static bool validateMediaDirection(std::vector<MediaDescription> descrList,
     172             :                                        std::vector<MediaAttribute> listInOffer,
     173             :                                        std::vector<MediaAttribute> listInAnswer);
     174             :     static bool waitForSignal(CallData& callData,
     175             :                               const std::string& signal,
     176             :                               const std::string& expectedEvent = {});
     177             : 
     178             :     bool isSipAccount_ {false};
     179             :     std::map<std::string, CallData> callDataMap_;
     180             :     std::set<std::string> testAccounts_;
     181             : };
     182             : 
     183             : // Specialized test case for Jami accounts
     184             : class MediaNegotiationTestJami : public MediaNegotiationTest, public CppUnit::TestFixture
     185             : {
     186             : public:
     187           6 :     MediaNegotiationTestJami() { isSipAccount_ = false; }
     188             : 
     189           2 :     static std::string name() { return "MediaNegotiationTestJami"; }
     190             : 
     191           6 :     void setUp() override
     192             :     {
     193           6 :         auto actors = load_actors("actors/alice-bob-no-upnp.yml");
     194           6 :         callDataMap_["ALICE"].accountId_ = actors["alice"];
     195           6 :         callDataMap_["BOB"].accountId_ = actors["bob"];
     196             : 
     197           6 :         JAMI_INFO("Initialize account...");
     198           6 :         auto aliceAccount = Manager::instance().getAccount<JamiAccount>(
     199          12 :             callDataMap_["ALICE"].accountId_);
     200           6 :         auto bobAccount = Manager::instance().getAccount<JamiAccount>(
     201          12 :             callDataMap_["BOB"].accountId_);
     202             : 
     203          18 :         wait_for_announcement_of({aliceAccount->getAccountID(), bobAccount->getAccountID()});
     204           6 :     }
     205             : 
     206           6 :     void tearDown() override
     207             :     {
     208          18 :         wait_for_removal_of({callDataMap_["ALICE"].accountId_, callDataMap_["BOB"].accountId_});
     209           6 :     }
     210             : 
     211             : private:
     212           2 :     CPPUNIT_TEST_SUITE(MediaNegotiationTestJami);
     213           1 :     CPPUNIT_TEST(audio_and_video_then_caller_mute_video);
     214           1 :     CPPUNIT_TEST(audio_only_then_caller_add_video);
     215           1 :     CPPUNIT_TEST(audio_and_video_then_caller_mute_audio);
     216           1 :     CPPUNIT_TEST(audio_and_video_answer_muted_video_then_mute_video);
     217           1 :     CPPUNIT_TEST(audio_and_video_then_change_video_source);
     218           1 :     CPPUNIT_TEST(negotiate_2_videos_1_audio);
     219           4 :     CPPUNIT_TEST_SUITE_END();
     220             : };
     221             : 
     222             : // Specialized test case for SIP accounts
     223             : class MediaNegotiationTestSip : public MediaNegotiationTest, public CppUnit::TestFixture
     224             : {
     225             : public:
     226           0 :     MediaNegotiationTestSip() { isSipAccount_ = true; }
     227             : 
     228           1 :     static std::string name() { return "MediaNegotiationTestSip"; }
     229             : 
     230           0 :     bool addTestAccount(const std::string& alias, uint16_t port)
     231             :     {
     232           0 :         CallData callData;
     233           0 :         callData.alias_ = alias;
     234           0 :         callData.userName_ = alias;
     235           0 :         callData.listeningPort_ = port;
     236           0 :         std::map<std::string, std::string> details = libjami::getAccountTemplate("SIP");
     237           0 :         details[ConfProperties::TYPE] = "SIP";
     238           0 :         details[ConfProperties::USERNAME] = alias;
     239           0 :         details[ConfProperties::DISPLAYNAME] = alias;
     240           0 :         details[ConfProperties::ALIAS] = alias;
     241           0 :         details[ConfProperties::LOCAL_PORT] = std::to_string(port);
     242           0 :         details[ConfProperties::UPNP_ENABLED] = "false";
     243           0 :         callData.accountId_ = Manager::instance().addAccount(details);
     244           0 :         testAccounts_.insert(callData.accountId_);
     245           0 :         callDataMap_.emplace(alias, std::move(callData));
     246           0 :         return (not callDataMap_[alias].accountId_.empty());
     247           0 :     }
     248             : 
     249           0 :     void setUp() override
     250             :     {
     251           0 :         CPPUNIT_ASSERT(addTestAccount("ALICE", 5080));
     252           0 :         CPPUNIT_ASSERT(addTestAccount("BOB", 5082));
     253           0 :     }
     254             : 
     255           0 :     void tearDown() override
     256             :     {
     257           0 :         std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     258           0 :         std::mutex mtx;
     259           0 :         std::unique_lock lk {mtx};
     260           0 :         std::condition_variable cv;
     261           0 :         std::atomic_bool accountsRemoved {false};
     262           0 :         confHandlers.insert(
     263           0 :             libjami::exportable_callback<libjami::ConfigurationSignal::AccountsChanged>([&]() {
     264           0 :                 auto currAccounts = Manager::instance().getAccountList();
     265           0 :                 for (auto iter = testAccounts_.begin(); iter != testAccounts_.end();) {
     266           0 :                     auto item = std::find(currAccounts.begin(), currAccounts.end(), *iter);
     267           0 :                     if (item == currAccounts.end()) {
     268           0 :                         JAMI_INFO("Removing account %s", (*iter).c_str());
     269           0 :                         iter = testAccounts_.erase(iter);
     270             :                     } else {
     271           0 :                         iter++;
     272             :                     }
     273             :                 }
     274             : 
     275           0 :                 if (testAccounts_.empty()) {
     276           0 :                     accountsRemoved = true;
     277           0 :                     JAMI_INFO("All accounts removed...");
     278           0 :                     cv.notify_one();
     279             :                 }
     280           0 :             }));
     281             : 
     282           0 :         libjami::registerSignalHandlers(confHandlers);
     283             : 
     284           0 :         Manager::instance().removeAccount(callDataMap_["ALICE"].accountId_, true);
     285           0 :         Manager::instance().removeAccount(callDataMap_["BOB"].accountId_, true);
     286           0 :         CPPUNIT_ASSERT(
     287             :             cv.wait_for(lk, std::chrono::seconds(30), [&] { return accountsRemoved.load(); }));
     288             : 
     289           0 :         libjami::unregisterSignalHandlers();
     290           0 :     }
     291             : 
     292             : private:
     293           0 :     CPPUNIT_TEST_SUITE(MediaNegotiationTestSip);
     294           0 :     CPPUNIT_TEST(audio_and_video_then_caller_mute_video);
     295           0 :     CPPUNIT_TEST(audio_only_then_caller_add_video);
     296           0 :     CPPUNIT_TEST(audio_and_video_then_caller_mute_audio);
     297           0 :     CPPUNIT_TEST(audio_and_video_answer_muted_video_then_mute_video);
     298           0 :     CPPUNIT_TEST(audio_and_video_then_change_video_source);
     299           0 :     CPPUNIT_TEST(negotiate_2_videos_1_audio);
     300           0 :     CPPUNIT_TEST_SUITE_END();
     301             : };
     302             : 
     303             : std::string
     304          26 : MediaNegotiationTest::getAccountId(const std::string& callId)
     305             : {
     306          26 :     auto call = Manager::instance().getCallFromCallID(callId);
     307             : 
     308          26 :     if (not call) {
     309           0 :         JAMI_WARN("Call [%s] does not exist anymore!", callId.c_str());
     310           0 :         return {};
     311             :     }
     312             : 
     313          26 :     auto const& account = call->getAccount().lock();
     314             : 
     315          26 :     if (account) {
     316          26 :         return account->getAccountID();
     317             :     }
     318             : 
     319           0 :     JAMI_WARN("Account owning the call [%s] does not exist anymore!", callId.c_str());
     320           0 :     return {};
     321          26 : }
     322             : 
     323             : std::string
     324          95 : MediaNegotiationTest::getUserAlias(const std::string& accountId)
     325             : {
     326          95 :     if (accountId.empty()) {
     327           0 :         JAMI_WARN("No account ID is empty");
     328           0 :         return {};
     329             :     }
     330             : 
     331          95 :     auto ret = std::find_if(callDataMap_.begin(), callDataMap_.end(), [accountId](auto const& item) {
     332         149 :         return item.second.accountId_ == accountId;
     333             :     });
     334             : 
     335          95 :     if (ret != callDataMap_.end())
     336          95 :         return ret->first;
     337             : 
     338           0 :     JAMI_WARN("No matching test account %s", accountId.c_str());
     339           0 :     return {};
     340             : }
     341             : 
     342             : MediaDirection
     343          88 : MediaNegotiationTest::inferInitialDirection(const MediaAttribute& mediaAttr)
     344             : {
     345          88 :     if (not mediaAttr.enabled_)
     346           0 :         return MediaDirection::INACTIVE;
     347             : 
     348          88 :     if (mediaAttr.muted_) {
     349          10 :         if (mediaAttr.onHold_)
     350           0 :             return MediaDirection::INACTIVE;
     351          10 :         return MediaDirection::RECVONLY;
     352             :     }
     353             : 
     354          78 :     if (mediaAttr.onHold_)
     355           0 :         return MediaDirection::SENDONLY;
     356             : 
     357          78 :     return MediaDirection::SENDRECV;
     358             : }
     359             : 
     360             : uint8_t
     361          88 : MediaNegotiationTest::directionToBitset(MediaDirection direction, bool isLocal)
     362             : {
     363          88 :     if (direction == MediaDirection::SENDRECV)
     364          78 :         return 3;
     365          10 :     if (direction == MediaDirection::RECVONLY)
     366          10 :         return isLocal ? 2 : 1;
     367           0 :     if (direction == MediaDirection::SENDONLY)
     368           0 :         return isLocal ? 1 : 2;
     369           0 :     return 0;
     370             : }
     371             : 
     372             : MediaDirection
     373          44 : MediaNegotiationTest::bitsetToDirection(uint8_t val)
     374             : {
     375          44 :     if (val == 3)
     376          36 :         return MediaDirection::SENDRECV;
     377           8 :     if (val == 2)
     378           3 :         return MediaDirection::RECVONLY;
     379           5 :     if (val == 1)
     380           3 :         return MediaDirection::SENDONLY;
     381           2 :     return MediaDirection::INACTIVE;
     382             : }
     383             : 
     384             : MediaDirection
     385          44 : MediaNegotiationTest::inferNegotiatedDirection(MediaDirection local, MediaDirection remote)
     386             : {
     387          44 :     uint8_t val = directionToBitset(local, true) & directionToBitset(remote, false);
     388          44 :     return bitsetToDirection(val);
     389             : }
     390             : 
     391             : bool
     392          22 : MediaNegotiationTest::validateMuteState(std::vector<MediaAttribute> expected,
     393             :                                         std::vector<MediaAttribute> actual)
     394             : {
     395          22 :     CPPUNIT_ASSERT_EQUAL(expected.size(), actual.size());
     396             : 
     397          22 :     return std::equal(expected.begin(),
     398             :                       expected.end(),
     399             :                       actual.begin(),
     400             :                       actual.end(),
     401          44 :                       [](auto const& expAttr, auto const& actAttr) {
     402          44 :                           return expAttr.muted_ == actAttr.muted_;
     403          22 :                       });
     404             : }
     405             : 
     406             : bool
     407          22 : MediaNegotiationTest::validateMediaDirection(std::vector<MediaDescription> descrList,
     408             :                                              std::vector<MediaAttribute> localMediaList,
     409             :                                              std::vector<MediaAttribute> remoteMediaList)
     410             : {
     411          22 :     CPPUNIT_ASSERT_EQUAL(descrList.size(), localMediaList.size());
     412          22 :     CPPUNIT_ASSERT_EQUAL(descrList.size(), remoteMediaList.size());
     413             : 
     414          66 :     for (size_t idx = 0; idx < descrList.size(); idx++) {
     415          44 :         auto local = inferInitialDirection(localMediaList[idx]);
     416          44 :         auto remote = inferInitialDirection(remoteMediaList[idx]);
     417          44 :         auto negotiated = inferNegotiatedDirection(local, remote);
     418             : 
     419          44 :         if (descrList[idx].direction_ != negotiated) {
     420           0 :             JAMI_WARN("Media [%lu] direction mismatch: expected %i - found %i",
     421             :                       idx,
     422             :                       static_cast<int>(negotiated),
     423             :                       static_cast<int>(descrList[idx].direction_));
     424           0 :             return false;
     425             :         }
     426             :     }
     427             : 
     428          22 :     return true;
     429             : }
     430             : 
     431             : void
     432           6 : MediaNegotiationTest::onIncomingCallWithMedia(const std::string& accountId,
     433             :                                               const std::string& callId,
     434             :                                               const std::vector<libjami::MediaMap> mediaList,
     435             :                                               CallData& callData)
     436             : {
     437           6 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     438             : 
     439           6 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     440             :               libjami::CallSignal::IncomingCallWithMedia::name,
     441             :               callData.alias_.c_str(),
     442             :               callId.c_str(),
     443             :               mediaList.size());
     444             : 
     445             :     // NOTE.
     446             :     // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
     447             :     // the client, and the client have no access to this type. But here, we only
     448             :     // needed to check if the call exists. This is the most straightforward and
     449             :     // reliable way to do it until we add a new API (like hasCall(id)).
     450           6 :     if (not Manager::instance().getCallFromCallID(callId)) {
     451           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     452           0 :         callData.callId_ = {};
     453           0 :         return;
     454             :     }
     455             : 
     456           6 :     std::unique_lock lock {callData.mtx_};
     457           6 :     callData.callId_ = callId;
     458           6 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCallWithMedia::name));
     459             : 
     460           6 :     callData.cv_.notify_one();
     461           6 : }
     462             : 
     463             : void
     464           0 : MediaNegotiationTest::onIncomingCall(const std::string& accountId,
     465             :                                      const std::string& callId,
     466             :                                      CallData& callData)
     467             : {
     468           0 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     469             : 
     470           0 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s]",
     471             :               libjami::CallSignal::IncomingCall::name,
     472             :               callData.alias_.c_str(),
     473             :               callId.c_str());
     474             : 
     475             :     // NOTE.
     476             :     // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
     477             :     // the client, and the client have no access to this type. But here, we only
     478             :     // needed to check if the call exists. This is the most straightforward and
     479             :     // reliable way to do it until we add a new API (like hasCall(id)).
     480           0 :     if (not Manager::instance().getCallFromCallID(callId)) {
     481           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     482           0 :         callData.callId_ = {};
     483           0 :         return;
     484             :     }
     485             : 
     486           0 :     std::unique_lock lock {callData.mtx_};
     487           0 :     callData.callId_ = callId;
     488           0 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCall::name));
     489             : 
     490           0 :     callData.cv_.notify_one();
     491           0 : }
     492             : 
     493             : void
     494           2 : MediaNegotiationTest::onMediaChangeRequested(const std::string& accountId,
     495             :                                              const std::string& callId,
     496             :                                              const std::vector<libjami::MediaMap> mediaList,
     497             :                                              CallData& callData)
     498             : {
     499           2 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     500             : 
     501           2 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     502             :               libjami::CallSignal::MediaChangeRequested::name,
     503             :               callData.alias_.c_str(),
     504             :               callId.c_str(),
     505             :               mediaList.size());
     506             : 
     507             :     // TODO
     508             :     // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
     509             :     // the client, and the client have no access to this type. But here, we only
     510             :     // needed to check if the call exists. This is the most straightforward and
     511             :     // reliable way to do it until we add a new API (like hasCall(id)).
     512           2 :     if (not Manager::instance().getCallFromCallID(callId)) {
     513           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     514           0 :         callData.callId_ = {};
     515           0 :         return;
     516             :     }
     517             : 
     518           2 :     std::unique_lock lock {callData.mtx_};
     519           2 :     callData.callId_ = callId;
     520           2 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::MediaChangeRequested::name));
     521             : 
     522           2 :     callData.cv_.notify_one();
     523           2 : }
     524             : 
     525             : void
     526          61 : MediaNegotiationTest::onCallStateChange(const std::string& accountId,
     527             :                                         const std::string& callId,
     528             :                                         const std::string& state,
     529             :                                         CallData& callData)
     530             : {
     531          61 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     532             :               libjami::CallSignal::StateChange::name,
     533             :               callData.alias_.c_str(),
     534             :               callId.c_str(),
     535             :               state.c_str());
     536             : 
     537          61 :     CPPUNIT_ASSERT(accountId == callData.accountId_);
     538             : 
     539             :     {
     540          61 :         std::unique_lock lock {callData.mtx_};
     541          61 :         callData.signals_.emplace_back(
     542         122 :             CallData::Signal(libjami::CallSignal::StateChange::name, state));
     543          61 :     }
     544             :     // NOTE. Only states that we are interested in will notify the CV.
     545             :     // If this unit test is modified to process other states, they must
     546             :     // be added here.
     547          61 :     if (state == "CURRENT" or state == "OVER" or state == "HUNGUP" or state == "RINGING") {
     548          37 :         callData.cv_.notify_one();
     549             :     }
     550          61 : }
     551             : 
     552             : void
     553           3 : MediaNegotiationTest::onVideoMuted(const std::string& callId, bool muted, CallData& callData)
     554             : {
     555           3 :     auto call = Manager::instance().getCallFromCallID(callId);
     556             : 
     557           3 :     if (not call) {
     558           0 :         JAMI_WARN("Call with ID [%s] does not exist anymore!", callId.c_str());
     559           0 :         return;
     560             :     }
     561             : 
     562           3 :     auto account = call->getAccount().lock();
     563           3 :     if (not account) {
     564           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     565           0 :         return;
     566             :     }
     567             : 
     568           3 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     569             :               libjami::CallSignal::VideoMuted::name,
     570             :               account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
     571             :               call->getCallId().c_str(),
     572             :               muted ? "Mute" : "Un-mute");
     573             : 
     574           3 :     if (account->getAccountID() != callData.accountId_)
     575           0 :         return;
     576             : 
     577             :     {
     578           3 :         std::unique_lock lock {callData.mtx_};
     579           3 :         callData.signals_.emplace_back(
     580           6 :             CallData::Signal(libjami::CallSignal::VideoMuted::name, muted ? "muted" : "un-muted"));
     581           3 :     }
     582             : 
     583           3 :     callData.cv_.notify_one();
     584           3 : }
     585             : 
     586             : void
     587          23 : MediaNegotiationTest::onMediaNegotiationStatus(const std::string& callId,
     588             :                                                const std::string& event,
     589             :                                                CallData& callData)
     590             : {
     591          23 :     auto call = Manager::instance().getCallFromCallID(callId);
     592          23 :     if (not call) {
     593           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     594           0 :         return;
     595             :     }
     596             : 
     597          23 :     auto account = call->getAccount().lock();
     598          23 :     if (not account) {
     599           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     600           0 :         return;
     601             :     }
     602             : 
     603          23 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     604             :               libjami::CallSignal::MediaNegotiationStatus::name,
     605             :               account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
     606             :               call->getCallId().c_str(),
     607             :               event.c_str());
     608             : 
     609          23 :     if (account->getAccountID() != callData.accountId_)
     610           0 :         return;
     611             : 
     612             :     {
     613          23 :         std::unique_lock lock {callData.mtx_};
     614          23 :         callData.signals_.emplace_back(
     615          46 :             CallData::Signal(libjami::CallSignal::MediaNegotiationStatus::name, event));
     616          23 :     }
     617             : 
     618          23 :     callData.cv_.notify_one();
     619          23 : }
     620             : 
     621             : bool
     622          37 : MediaNegotiationTest::waitForSignal(CallData& callData,
     623             :                                     const std::string& expectedSignal,
     624             :                                     const std::string& expectedEvent)
     625             : {
     626          37 :     const std::chrono::seconds TIME_OUT {30};
     627          37 :     std::unique_lock lock {callData.mtx_};
     628             : 
     629             :     // Combined signal + event (if any).
     630          37 :     std::string sigEvent(expectedSignal);
     631          37 :     if (not expectedEvent.empty())
     632          29 :         sigEvent += "::" + expectedEvent;
     633             : 
     634          37 :     JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
     635             : 
     636          37 :     auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
     637             :         // Search for the expected signal in list of received signals.
     638          63 :         bool pred = false;
     639         257 :         for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
     640             :             // The predicate is true if the signal names match, and if the
     641             :             // expectedEvent is not empty, the events must also match.
     642         231 :             if (it->name_ == expectedSignal
     643         231 :                 and (expectedEvent.empty() or it->event_ == expectedEvent)) {
     644          37 :                 pred = true;
     645             :                 // Done with this signal.
     646          37 :                 callData.signals_.erase(it);
     647          37 :                 break;
     648             :             }
     649             :         }
     650             : 
     651          63 :         return pred;
     652             :     });
     653             : 
     654          37 :     if (not res) {
     655           0 :         JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
     656             :                  callData.alias_.c_str(),
     657             :                  sigEvent.c_str());
     658             : 
     659           0 :         JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
     660             : 
     661           0 :         for (auto const& sig : callData.signals_) {
     662           0 :             JAMI_INFO() << "\tSignal [" << sig.name_
     663           0 :                         << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
     664             :         }
     665             :     }
     666             : 
     667          37 :     return res;
     668          37 : }
     669             : 
     670             : void
     671           6 : MediaNegotiationTest::configureScenario()
     672             : {
     673             :     // Configure Alice
     674             :     {
     675           6 :         CPPUNIT_ASSERT(not callDataMap_["ALICE"].accountId_.empty());
     676           6 :         auto const& account = Manager::instance().getAccount<Account>(
     677           6 :             callDataMap_["ALICE"].accountId_);
     678           6 :         callDataMap_["ALICE"].userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     679           6 :         callDataMap_["ALICE"].alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     680           6 :         if (isSipAccount_) {
     681           0 :             auto sipAccount = std::dynamic_pointer_cast<SIPAccount>(account);
     682           0 :             CPPUNIT_ASSERT(sipAccount);
     683           0 :             sipAccount->setLocalPort(callDataMap_["ALICE"].listeningPort_);
     684           0 :         }
     685           6 :     }
     686             : 
     687             :     // Configure Bob
     688             :     {
     689           6 :         CPPUNIT_ASSERT(not callDataMap_["BOB"].accountId_.empty());
     690           6 :         auto const& account = Manager::instance().getAccount<Account>(
     691           6 :             callDataMap_["BOB"].accountId_);
     692           6 :         callDataMap_["BOB"].userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     693           6 :         callDataMap_["BOB"].alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     694             : 
     695           6 :         if (isSipAccount_) {
     696           0 :             auto sipAccount = std::dynamic_pointer_cast<SIPAccount>(account);
     697           0 :             CPPUNIT_ASSERT(sipAccount);
     698           0 :             sipAccount->setLocalPort(callDataMap_["BOB"].listeningPort_);
     699           0 :             callDataMap_["BOB"].toUri_ = fmt::format("127.0.0.1:{}",
     700           0 :                                                      callDataMap_["BOB"].listeningPort_);
     701           0 :         }
     702           6 :     }
     703             : 
     704           6 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers;
     705             : 
     706             :     // Insert needed signal handlers.
     707           6 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     708           6 :         [&](const std::string& accountId,
     709             :             const std::string& callId,
     710             :             const std::string&,
     711             :             const std::vector<libjami::MediaMap> mediaList) {
     712           6 :             auto user = getUserAlias(accountId);
     713           6 :             if (not user.empty())
     714           6 :                 onIncomingCallWithMedia(accountId, callId, mediaList, callDataMap_[user]);
     715           6 :         }));
     716             : 
     717           6 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaChangeRequested>(
     718           2 :         [&](const std::string& accountId,
     719             :             const std::string& callId,
     720             :             const std::vector<libjami::MediaMap> mediaList) {
     721           2 :             auto user = getUserAlias(accountId);
     722           2 :             if (not user.empty())
     723           2 :                 onMediaChangeRequested(accountId, callId, mediaList, callDataMap_[user]);
     724           2 :         }));
     725             : 
     726           6 :     signalHandlers.insert(
     727          12 :         libjami::exportable_callback<libjami::CallSignal::StateChange>([&](const std::string& accountId,
     728             :                                                                        const std::string& callId,
     729             :                                                                        const std::string& state,
     730             :                                                                        signed) {
     731          61 :             auto user = getUserAlias(accountId);
     732          61 :             if (not user.empty())
     733          61 :                 onCallStateChange(accountId, callId, state, callDataMap_[user]);
     734          61 :         }));
     735             : 
     736           6 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::VideoMuted>(
     737           3 :         [&](const std::string& callId, bool muted) {
     738           3 :             auto user = getUserAlias(getAccountId(callId));
     739           3 :             if (not user.empty())
     740           3 :                 onVideoMuted(callId, muted, callDataMap_[user]);
     741           3 :         }));
     742             : 
     743           6 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
     744          23 :         [&](const std::string& callId,
     745             :             const std::string& event,
     746             :             const std::vector<std::map<std::string, std::string>>&) {
     747          23 :             auto user = getUserAlias(getAccountId(callId));
     748          23 :             if (not user.empty())
     749          23 :                 onMediaNegotiationStatus(callId, event, callDataMap_[user]);
     750          23 :         }));
     751             : 
     752           6 :     libjami::registerSignalHandlers(signalHandlers);
     753           6 : }
     754             : 
     755             : void
     756           6 : MediaNegotiationTest::testWithScenario(CallData& aliceData,
     757             :                                        CallData& bobData,
     758             :                                        const TestScenario& scenario)
     759             : {
     760           6 :     JAMI_INFO("=== Start a call and validate ===");
     761             : 
     762             :     // The media count of the offer and answer must match (RFC-3264).
     763           6 :     auto mediaCount = scenario.offer_.size();
     764           6 :     CPPUNIT_ASSERT_EQUAL(mediaCount, scenario.answer_.size());
     765             : 
     766          12 :     aliceData.callId_ = libjami::placeCallWithMedia(aliceData.accountId_,
     767           6 :                                                   isSipAccount_ ? bobData.toUri_
     768          12 :                                                                 : callDataMap_["BOB"].userName_,
     769          12 :                                                   MediaAttribute::mediaAttributesToMediaMaps(
     770          12 :                                                       scenario.offer_));
     771           6 :     CPPUNIT_ASSERT(not aliceData.callId_.empty());
     772             : 
     773             :     auto aliceCall = std::static_pointer_cast<SIPCall>(
     774           6 :         Manager::instance().getCallFromCallID(aliceData.callId_));
     775           6 :     CPPUNIT_ASSERT(aliceCall);
     776             : 
     777           6 :     aliceData.callId_ = aliceCall->getCallId();
     778             : 
     779           6 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     780             :               aliceData.accountId_.c_str(),
     781             :               bobData.accountId_.c_str());
     782             : 
     783             :     // Wait for incoming call signal.
     784           6 :     CPPUNIT_ASSERT(waitForSignal(bobData, libjami::CallSignal::IncomingCallWithMedia::name));
     785             : 
     786             :     // Answer the call.
     787             :     {
     788           6 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.answer_);
     789           6 :         libjami::acceptWithMedia(bobData.accountId_, bobData.callId_, mediaList);
     790           6 :     }
     791             : 
     792             :     // Wait for media negotiation complete signal.
     793           6 :     CPPUNIT_ASSERT_EQUAL(
     794             :         true,
     795             :         waitForSignal(bobData,
     796             :                       libjami::CallSignal::MediaNegotiationStatus::name,
     797             :                       libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     798             :     // Wait for the StateChange signal.
     799           6 :     CPPUNIT_ASSERT_EQUAL(true,
     800             :                          waitForSignal(bobData,
     801             :                                        libjami::CallSignal::StateChange::name,
     802             :                                        StateEvent::CURRENT));
     803             : 
     804           6 :     JAMI_INFO("BOB answered the call [%s]", bobData.callId_.c_str());
     805             : 
     806             :     // Wait for media negotiation complete signal.
     807           6 :     CPPUNIT_ASSERT_EQUAL(
     808             :         true,
     809             :         waitForSignal(aliceData,
     810             :                       libjami::CallSignal::MediaNegotiationStatus::name,
     811             :                       libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     812             : 
     813             :     // Validate Alice's media
     814             :     {
     815           6 :         auto mediaList = aliceCall->getMediaAttributeList();
     816           6 :         CPPUNIT_ASSERT_EQUAL(mediaCount, mediaList.size());
     817             : 
     818             :         // Validate mute state
     819           6 :         CPPUNIT_ASSERT(validateMuteState(scenario.offer_, mediaList));
     820             : 
     821           6 :         auto& sdp = aliceCall->getSDP();
     822             : 
     823             :         // Validate local media direction
     824           6 :         auto descrList = sdp.getActiveMediaDescription(false);
     825           6 :         CPPUNIT_ASSERT_EQUAL(mediaCount, descrList.size());
     826             :         // For Alice, local is the offer and remote is the answer.
     827           6 :         CPPUNIT_ASSERT(validateMediaDirection(descrList, scenario.offer_, scenario.answer_));
     828           6 :     }
     829             : 
     830             :     // Validate Bob's media
     831             :     {
     832             :         auto const& bobCall = std::dynamic_pointer_cast<SIPCall>(
     833           6 :             Manager::instance().getCallFromCallID(bobData.callId_));
     834           6 :         auto mediaList = bobCall->getMediaAttributeList();
     835           6 :         CPPUNIT_ASSERT_EQUAL(mediaCount, mediaList.size());
     836             : 
     837             :         // Validate mute state
     838           6 :         CPPUNIT_ASSERT(validateMuteState(scenario.answer_, mediaList));
     839             : 
     840           6 :         auto& sdp = bobCall->getSDP();
     841             : 
     842             :         // Validate local media direction
     843           6 :         auto descrList = sdp.getActiveMediaDescription(false);
     844           6 :         CPPUNIT_ASSERT_EQUAL(mediaCount, descrList.size());
     845             :         // For Bob, local is the answer and remote is the offer.
     846           6 :         CPPUNIT_ASSERT(validateMediaDirection(descrList, scenario.answer_, scenario.offer_));
     847           6 :     }
     848             : 
     849           6 :     std::this_thread::sleep_for(std::chrono::seconds(3));
     850             : 
     851           6 :     JAMI_INFO("=== Request Media Change and validate ===");
     852             :     {
     853           6 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.offerUpdate_);
     854           6 :         libjami::requestMediaChange(aliceData.accountId_, aliceData.callId_, mediaList);
     855           6 :     }
     856             : 
     857             :     // Update and validate media count.
     858           6 :     mediaCount = scenario.offerUpdate_.size();
     859           6 :     CPPUNIT_ASSERT_EQUAL(mediaCount, scenario.answerUpdate_.size());
     860             : 
     861             :     // Not all media change requests requires validation from client.
     862           6 :     if (scenario.expectMediaChangeRequest_) {
     863             :         // Wait for media change request signal.
     864           2 :         CPPUNIT_ASSERT_EQUAL(true,
     865             :                              waitForSignal(bobData, libjami::CallSignal::MediaChangeRequested::name));
     866             : 
     867             :         // Answer the change request.
     868           2 :         auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(scenario.answerUpdate_);
     869           2 :         libjami::answerMediaChangeRequest(bobData.accountId_, bobData.callId_, mediaList);
     870           2 :     }
     871             : 
     872           6 :     if (scenario.expectMediaRenegotiation_) {
     873             :         // Wait for media negotiation complete signal.
     874           5 :         CPPUNIT_ASSERT_EQUAL(
     875             :             true,
     876             :             waitForSignal(aliceData,
     877             :                           libjami::CallSignal::MediaNegotiationStatus::name,
     878             :                           libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     879             : 
     880             :         // Validate Alice's media
     881             :         {
     882           5 :             auto mediaList = aliceCall->getMediaAttributeList();
     883           5 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaList.size());
     884             : 
     885             :             // Validate mute state
     886           5 :             CPPUNIT_ASSERT(validateMuteState(scenario.offerUpdate_, mediaList));
     887             : 
     888           5 :             auto& sdp = aliceCall->getSDP();
     889             : 
     890             :             // Validate local media direction
     891           5 :             auto descrList = sdp.getActiveMediaDescription(false);
     892           5 :             CPPUNIT_ASSERT_EQUAL(mediaCount, descrList.size());
     893           5 :             CPPUNIT_ASSERT(
     894             :                 validateMediaDirection(descrList, scenario.offerUpdate_, scenario.answerUpdate_));
     895             :             // Validate remote media direction
     896           5 :             descrList = sdp.getActiveMediaDescription(true);
     897           5 :             CPPUNIT_ASSERT_EQUAL(mediaCount, descrList.size());
     898           5 :             CPPUNIT_ASSERT(
     899             :                 validateMediaDirection(descrList, scenario.answerUpdate_, scenario.offerUpdate_));
     900           5 :         }
     901             : 
     902             :         // Validate Bob's media
     903             :         {
     904             :             auto const& bobCall = std::dynamic_pointer_cast<SIPCall>(
     905           5 :                 Manager::instance().getCallFromCallID(bobData.callId_));
     906           5 :             auto mediaList = bobCall->getMediaAttributeList();
     907           5 :             CPPUNIT_ASSERT_EQUAL(mediaCount, mediaList.size());
     908             : 
     909             :             // Validate mute state
     910           5 :             CPPUNIT_ASSERT(validateMuteState(scenario.answerUpdate_, mediaList));
     911             : 
     912             :             // NOTE:
     913             :             // It should be enough to validate media direction on Alice's side
     914           5 :         }
     915             :     }
     916             : 
     917           6 :     std::this_thread::sleep_for(std::chrono::seconds(3));
     918             : 
     919             :     // Bob hang-up.
     920           6 :     JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     921           6 :     libjami::hangUp(bobData.accountId_, bobData.callId_);
     922             : 
     923           6 :     CPPUNIT_ASSERT_EQUAL(true,
     924             :                          waitForSignal(aliceData,
     925             :                                        libjami::CallSignal::StateChange::name,
     926             :                                        StateEvent::HUNGUP));
     927             : 
     928           6 :     JAMI_INFO("Call terminated on both sides");
     929           6 : }
     930             : 
     931             : void
     932           1 : MediaNegotiationTest::audio_and_video_then_caller_mute_video()
     933             : {
     934           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     935             : 
     936           1 :     configureScenario();
     937             : 
     938           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     939           1 :     defaultAudio.label_ = "audio_0";
     940           1 :     defaultAudio.enabled_ = true;
     941             : 
     942           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
     943           1 :     defaultVideo.label_ = "video_0";
     944           1 :     defaultVideo.enabled_ = true;
     945             : 
     946           1 :     MediaAttribute audio(defaultAudio);
     947           1 :     MediaAttribute video(defaultVideo);
     948             : 
     949           1 :     TestScenario scenario;
     950             :     // First offer/answer
     951           1 :     scenario.offer_.emplace_back(audio);
     952           1 :     scenario.offer_.emplace_back(video);
     953           1 :     scenario.answer_.emplace_back(audio);
     954           1 :     scenario.answer_.emplace_back(video);
     955             : 
     956             :     // Updated offer/answer
     957           1 :     scenario.offerUpdate_.emplace_back(audio);
     958           1 :     video.muted_ = true;
     959           1 :     scenario.offerUpdate_.emplace_back(video);
     960             : 
     961           1 :     scenario.answerUpdate_.emplace_back(audio);
     962           1 :     video.muted_ = false;
     963           1 :     scenario.answerUpdate_.emplace_back(video);
     964           1 :     scenario.expectMediaRenegotiation_ = true;
     965           1 :     scenario.expectMediaChangeRequest_ = false;
     966             : 
     967           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
     968             : 
     969           1 :     libjami::unregisterSignalHandlers();
     970             : 
     971           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
     972           1 : }
     973             : 
     974             : void
     975           1 : MediaNegotiationTest::audio_only_then_caller_add_video()
     976             : {
     977           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     978             : 
     979           1 :     configureScenario();
     980             : 
     981           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     982           1 :     defaultAudio.label_ = "audio_0";
     983           1 :     defaultAudio.enabled_ = true;
     984             : 
     985           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
     986           1 :     defaultVideo.label_ = "video_0";
     987           1 :     defaultVideo.enabled_ = true;
     988             : 
     989           1 :     MediaAttribute audio(defaultAudio);
     990           1 :     MediaAttribute video(defaultVideo);
     991             : 
     992           1 :     TestScenario scenario;
     993             :     // First offer/answer
     994           1 :     scenario.offer_.emplace_back(audio);
     995           1 :     scenario.answer_.emplace_back(audio);
     996             : 
     997             :     // Updated offer/answer
     998           1 :     scenario.offerUpdate_.emplace_back(audio);
     999           1 :     scenario.offerUpdate_.emplace_back(video);
    1000           1 :     scenario.answerUpdate_.emplace_back(audio);
    1001           1 :     video.muted_ = true;
    1002           1 :     scenario.answerUpdate_.emplace_back(video);
    1003           1 :     scenario.expectMediaRenegotiation_ = true;
    1004           1 :     scenario.expectMediaChangeRequest_ = true;
    1005             : 
    1006           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
    1007             : 
    1008           1 :     libjami::unregisterSignalHandlers();
    1009             : 
    1010           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
    1011           1 : }
    1012             : 
    1013             : void
    1014           1 : MediaNegotiationTest::audio_and_video_then_caller_mute_audio()
    1015             : {
    1016           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
    1017             : 
    1018           1 :     configureScenario();
    1019             : 
    1020           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
    1021           1 :     defaultAudio.label_ = "audio_0";
    1022           1 :     defaultAudio.enabled_ = true;
    1023             : 
    1024           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
    1025           1 :     defaultVideo.label_ = "video_0";
    1026           1 :     defaultVideo.enabled_ = true;
    1027             : 
    1028           1 :     MediaAttribute audio(defaultAudio);
    1029           1 :     MediaAttribute video(defaultVideo);
    1030             : 
    1031           1 :     TestScenario scenario;
    1032             :     // First offer/answer
    1033           1 :     scenario.offer_.emplace_back(audio);
    1034           1 :     scenario.offer_.emplace_back(video);
    1035           1 :     scenario.answer_.emplace_back(audio);
    1036           1 :     scenario.answer_.emplace_back(video);
    1037             : 
    1038             :     // Updated offer/answer
    1039           1 :     audio.muted_ = true;
    1040           1 :     scenario.offerUpdate_.emplace_back(audio);
    1041           1 :     scenario.offerUpdate_.emplace_back(video);
    1042             : 
    1043           1 :     audio.muted_ = false;
    1044           1 :     scenario.answerUpdate_.emplace_back(audio);
    1045           1 :     scenario.answerUpdate_.emplace_back(video);
    1046             : 
    1047           1 :     scenario.expectMediaRenegotiation_ = false;
    1048           1 :     scenario.expectMediaChangeRequest_ = false;
    1049             : 
    1050           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
    1051             : 
    1052           1 :     libjami::unregisterSignalHandlers();
    1053             : 
    1054           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
    1055           1 : }
    1056             : 
    1057             : void
    1058           1 : MediaNegotiationTest::audio_and_video_answer_muted_video_then_mute_video()
    1059             : {
    1060           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
    1061             : 
    1062           1 :     configureScenario();
    1063             : 
    1064           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
    1065           1 :     defaultAudio.label_ = "audio_0";
    1066           1 :     defaultAudio.enabled_ = true;
    1067             : 
    1068           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
    1069           1 :     defaultVideo.label_ = "video_0";
    1070           1 :     defaultVideo.enabled_ = true;
    1071             : 
    1072           1 :     MediaAttribute audio(defaultAudio);
    1073           1 :     MediaAttribute video(defaultVideo);
    1074             : 
    1075           1 :     TestScenario scenario;
    1076             :     // First offer/answer
    1077           1 :     scenario.offer_.emplace_back(audio);
    1078           1 :     scenario.offer_.emplace_back(video);
    1079           1 :     video.muted_ = true;
    1080           1 :     scenario.answer_.emplace_back(audio);
    1081           1 :     scenario.answer_.emplace_back(video);
    1082             : 
    1083             :     // Updated offer/answer
    1084           1 :     video.muted_ = true;
    1085           1 :     scenario.offerUpdate_.emplace_back(audio);
    1086           1 :     scenario.offerUpdate_.emplace_back(video);
    1087             : 
    1088           1 :     video.muted_ = true;
    1089           1 :     scenario.answerUpdate_.emplace_back(audio);
    1090           1 :     scenario.answerUpdate_.emplace_back(video);
    1091             : 
    1092           1 :     scenario.expectMediaChangeRequest_ = false;
    1093           1 :     scenario.expectMediaRenegotiation_ = true;
    1094             : 
    1095           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
    1096             : 
    1097           1 :     libjami::unregisterSignalHandlers();
    1098             : 
    1099           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
    1100           1 : }
    1101             : 
    1102             : void
    1103           1 : MediaNegotiationTest::audio_and_video_then_change_video_source()
    1104             : {
    1105           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
    1106             : 
    1107           1 :     configureScenario();
    1108             : 
    1109           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
    1110           1 :     defaultAudio.label_ = "audio_0";
    1111           1 :     defaultAudio.enabled_ = true;
    1112             : 
    1113           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
    1114           1 :     defaultVideo.label_ = "video_0";
    1115           1 :     defaultVideo.enabled_ = true;
    1116             : 
    1117           1 :     MediaAttribute audio(defaultAudio);
    1118           1 :     MediaAttribute video(defaultVideo);
    1119             : 
    1120           1 :     TestScenario scenario;
    1121             :     // First offer/answer
    1122           1 :     scenario.offer_.emplace_back(audio);
    1123           1 :     scenario.offer_.emplace_back(video);
    1124           1 :     scenario.answer_.emplace_back(audio);
    1125           1 :     scenario.answer_.emplace_back(video);
    1126             : 
    1127             :     // Updated offer/answer
    1128           1 :     scenario.offerUpdate_.emplace_back(audio);
    1129             :     // Just change the media source to validate that a new
    1130             :     // media negotiation (re-invite) will be triggered.
    1131           1 :     video.sourceUri_ = "Fake source";
    1132           1 :     scenario.offerUpdate_.emplace_back(video);
    1133             : 
    1134           1 :     scenario.answerUpdate_.emplace_back(audio);
    1135           1 :     scenario.answerUpdate_.emplace_back(video);
    1136             : 
    1137           1 :     scenario.expectMediaRenegotiation_ = true;
    1138           1 :     scenario.expectMediaChangeRequest_ = false;
    1139             : 
    1140           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
    1141             : 
    1142           1 :     libjami::unregisterSignalHandlers();
    1143             : 
    1144           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
    1145           1 : }
    1146             : 
    1147             : void
    1148           1 : MediaNegotiationTest::negotiate_2_videos_1_audio()
    1149             : {
    1150           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
    1151             : 
    1152           1 :     configureScenario();
    1153             : 
    1154           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
    1155           1 :     defaultAudio.label_ = "audio_0";
    1156           1 :     defaultAudio.enabled_ = true;
    1157             : 
    1158           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
    1159           1 :     defaultVideo.label_ = "video_0";
    1160           1 :     defaultVideo.sourceUri_ = "foo";
    1161           1 :     defaultVideo.enabled_ = true;
    1162             : 
    1163           1 :     MediaAttribute defaultVideo2(MediaType::MEDIA_VIDEO);
    1164           1 :     defaultVideo2.label_ = "video_1";
    1165           1 :     defaultVideo2.sourceUri_ = "bar";
    1166           1 :     defaultVideo2.enabled_ = true;
    1167             : 
    1168           1 :     MediaAttribute audio(defaultAudio);
    1169           1 :     MediaAttribute video(defaultVideo);
    1170           1 :     MediaAttribute video2(defaultVideo2);
    1171             : 
    1172           1 :     TestScenario scenario;
    1173             :     // First offer/answer
    1174           1 :     scenario.offer_.emplace_back(audio);
    1175           1 :     scenario.offer_.emplace_back(video);
    1176           1 :     scenario.answer_.emplace_back(audio);
    1177           1 :     scenario.answer_.emplace_back(video);
    1178             : 
    1179             :     // Update offer/answer with 2 videos
    1180           1 :     scenario.offerUpdate_.emplace_back(audio);
    1181           1 :     scenario.offerUpdate_.emplace_back(video);
    1182           1 :     scenario.offerUpdate_.emplace_back(video2);
    1183           1 :     scenario.answerUpdate_.emplace_back(audio);
    1184           1 :     scenario.answerUpdate_.emplace_back(video);
    1185           1 :     scenario.answerUpdate_.emplace_back(video2);
    1186             : 
    1187           1 :     scenario.expectMediaRenegotiation_ = true;
    1188           1 :     scenario.expectMediaChangeRequest_ = true;
    1189             : 
    1190           1 :     testWithScenario(callDataMap_["ALICE"], callDataMap_["BOB"], scenario);
    1191             : 
    1192           1 :     libjami::unregisterSignalHandlers();
    1193             : 
    1194           1 :     JAMI_INFO("=== End test %s ===", __FUNCTION__);
    1195           1 : }
    1196             : 
    1197             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(MediaNegotiationTestJami, MediaNegotiationTestJami::name());
    1198             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(MediaNegotiationTestSip, MediaNegotiationTestSip::name());
    1199             : 
    1200             : } // namespace test
    1201             : } // namespace jami
    1202             : 
    1203           5 : JAMI_TEST_RUNNER(jami::test::MediaNegotiationTestJami::name())

Generated by: LCOV version 1.14