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-11-15 09:04:49 Functions: 44 55 80.0 %

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

Generated by: LCOV version 1.14