LCOV - code coverage report
Current view: top level - test/unitTest/ice - ice_sdp_parser.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 231 274 84.3 %
Date: 2024-11-15 09:04:49 Functions: 28 32 87.5 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include <cppunit/TestAssert.h>
      19             : #include <cppunit/TestFixture.h>
      20             : #include <cppunit/extensions/HelperMacros.h>
      21             : 
      22             : #include <condition_variable>
      23             : #include <string>
      24             : 
      25             : #include "manager.h"
      26             : #include "sip/sipaccount.h"
      27             : #include "../../test_runner.h"
      28             : 
      29             : #include "jami.h"
      30             : #include "media_const.h"
      31             : #include "call_const.h"
      32             : #include "account_const.h"
      33             : #include "sip/sipcall.h"
      34             : #include "media/audio/audio_rtp_session.h"
      35             : #include "media/audio/audio_receive_thread.h"
      36             : #include "media/video/video_rtp_session.h"
      37             : #include "media/video/video_receive_thread.h"
      38             : 
      39             : #include "common.h"
      40             : 
      41             : using namespace libjami::Account;
      42             : using namespace libjami::Call;
      43             : 
      44             : namespace jami {
      45             : namespace test {
      46             : 
      47             : struct CallData
      48             : {
      49             :     struct Signal
      50             :     {
      51          24 :         Signal(const std::string& name, const std::string& event = {})
      52          24 :             : name_(std::move(name))
      53          24 :             , event_(std::move(event)) {};
      54             : 
      55             :         std::string name_ {};
      56             :         std::string event_ {};
      57             :     };
      58             : 
      59             :     std::string accountId_ {};
      60             :     std::string userName_ {};
      61             :     uint16_t listeningPort_ {0};
      62             :     std::string alias_ {};
      63             :     std::string callId_ {};
      64             :     std::vector<Signal> signals_;
      65             :     std::condition_variable cv_ {};
      66             :     std::mutex mtx_;
      67             :     bool compliancyEnabled_ {false};
      68             : };
      69             : 
      70             : // Used to register a MediaFrame observer to RTP session in order
      71             : // to validate the media stream.
      72             : class MediaReceiver : public Observer<std::shared_ptr<MediaFrame>>
      73             : {
      74             : public:
      75           4 :     MediaReceiver(MediaType type)
      76           8 :         : mediaType_(type)
      77           8 :         , mediaTypeStr_(type == MediaType::MEDIA_AUDIO ? "AUDIO" : "VIDEO") {};
      78             : 
      79           4 :     virtual ~MediaReceiver() {};
      80             :     void update(Observable<std::shared_ptr<jami::MediaFrame>>* observer,
      81             :                 const std::shared_ptr<jami::MediaFrame>& mediaframe) override;
      82             : 
      83             :     bool waitForMediaFlow();
      84             :     const MediaType mediaType_ {MediaType::MEDIA_NONE};
      85             :     const std::string mediaTypeStr_ {};
      86           4 :     const std::chrono::seconds TIME_OUT {10};
      87             :     const unsigned REQUIRED_FRAME_COUNT {100};
      88             : 
      89             : private:
      90             :     unsigned long frameCounter_ {0};
      91             :     std::condition_variable cv_ {};
      92             :     std::mutex mtx_;
      93             : };
      94             : 
      95             : void
      96           0 : MediaReceiver::update(Observable<std::shared_ptr<jami::MediaFrame>>*,
      97             :                       const std::shared_ptr<jami::MediaFrame>& frame)
      98             : {
      99           0 :     std::unique_lock lock {mtx_};
     100           0 :     if (frame and frame->getFrame())
     101           0 :         frameCounter_++;
     102             : 
     103           0 :     if (frameCounter_ % 10 == 1) {
     104           0 :         JAMI_INFO("[%s] Frame counter %lu", mediaTypeStr_.c_str(), frameCounter_);
     105             :     }
     106             : 
     107           0 :     if (frameCounter_ >= REQUIRED_FRAME_COUNT)
     108           0 :         cv_.notify_one();
     109           0 : }
     110             : 
     111             : bool
     112           0 : MediaReceiver::waitForMediaFlow()
     113             : {
     114           0 :     std::unique_lock lock {mtx_};
     115             : 
     116           0 :     return cv_.wait_for(lock, TIME_OUT, [this] { return frameCounter_ > 100; });
     117           0 : }
     118             : 
     119             : class IceSdpParsingTest : public CppUnit::TestFixture
     120             : {
     121             : public:
     122           2 :     IceSdpParsingTest()
     123           2 :     {
     124             :         // Init daemon
     125           2 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
     126           2 :         if (not Manager::instance().initialized)
     127           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
     128             : 
     129           6 :         for (size_t idx = 0; idx < MEDIA_COUNT; idx++) {
     130           4 :             mediaReceivers_.emplace_back(std::make_shared<MediaReceiver>(MediaType::MEDIA_AUDIO));
     131             :         }
     132           2 :     }
     133           4 :     ~IceSdpParsingTest() { libjami::fini(); }
     134             : 
     135           2 :     static std::string name() { return "IceSdpParsingTest"; }
     136             :     void setUp();
     137             :     void tearDown();
     138             : 
     139             : private:
     140             :     // Test cases.
     141             :     void call_with_rfc5245_compliancy_disabled();
     142             :     void call_with_rfc5245_compliancy_enabled();
     143             : 
     144           2 :     CPPUNIT_TEST_SUITE(IceSdpParsingTest);
     145           1 :     CPPUNIT_TEST(call_with_rfc5245_compliancy_disabled);
     146           1 :     CPPUNIT_TEST(call_with_rfc5245_compliancy_enabled);
     147           4 :     CPPUNIT_TEST_SUITE_END();
     148             : 
     149             :     // Event/Signal handlers
     150             :     static void onCallStateChange(const std::string& accountId,
     151             :                                   const std::string& callId,
     152             :                                   const std::string& state,
     153             :                                   CallData& callData);
     154             :     static void onIncomingCallWithMedia(const std::string& accountId,
     155             :                                         const std::string& callId,
     156             :                                         const std::vector<libjami::MediaMap> mediaList,
     157             :                                         CallData& callData);
     158             :     static void onMediaNegotiationStatus(const std::string& callId,
     159             :                                          const std::string& event,
     160             :                                          CallData& callData);
     161             : 
     162             :     // Helpers
     163             :     void test_call();
     164             :     static void configureTest(CallData& bob, CallData& alice);
     165             :     static std::string getUserAlias(const std::string& callId);
     166             :     // Wait for a signal from the callbacks. Some signals also report the event that
     167             :     // triggered the signal a like the StateChange signal.
     168             :     static bool waitForSignal(CallData& callData,
     169             :                               const std::string& signal,
     170             :                               const std::string& expectedEvent = {});
     171             :     static bool attachReceiver(std::shared_ptr<MediaReceiver> receiver,
     172             :                                std::shared_ptr<RtpSession> rtpStream);
     173             :     static bool detachReceiver(std::shared_ptr<MediaReceiver> receiver,
     174             :                                std::shared_ptr<RtpSession> rtpStream);
     175             : 
     176             : private:
     177             :     CallData aliceData_;
     178             :     CallData bobData_;
     179             :     const size_t MEDIA_COUNT {2};
     180             :     std::vector<std::shared_ptr<MediaReceiver>> mediaReceivers_;
     181             : };
     182             : 
     183             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(IceSdpParsingTest, IceSdpParsingTest::name());
     184             : 
     185             : void
     186           2 : IceSdpParsingTest::setUp()
     187             : {
     188           2 :     aliceData_.listeningPort_ = 5080;
     189           4 :     std::map<std::string, std::string> details = libjami::getAccountTemplate("SIP");
     190           2 :     details[ConfProperties::TYPE] = "SIP";
     191           2 :     details[ConfProperties::DISPLAYNAME] = "ALICE";
     192           2 :     details[ConfProperties::ALIAS] = "ALICE";
     193           2 :     details[ConfProperties::LOCAL_PORT] = std::to_string(aliceData_.listeningPort_);
     194           2 :     details[ConfProperties::UPNP_ENABLED] = "false";
     195           2 :     aliceData_.accountId_ = Manager::instance().addAccount(details);
     196             : 
     197           2 :     bobData_.listeningPort_ = 5082;
     198           2 :     details = libjami::getAccountTemplate("SIP");
     199           2 :     details[ConfProperties::TYPE] = "SIP";
     200           2 :     details[ConfProperties::DISPLAYNAME] = "BOB";
     201           2 :     details[ConfProperties::ALIAS] = "BOB";
     202           2 :     details[ConfProperties::LOCAL_PORT] = std::to_string(bobData_.listeningPort_);
     203           2 :     details[ConfProperties::UPNP_ENABLED] = "false";
     204           2 :     bobData_.accountId_ = Manager::instance().addAccount(details);
     205             : 
     206           2 :     JAMI_INFO("Initialize accounts ...");
     207           2 :     auto aliceAccount = Manager::instance().getAccount<SIPAccount>(aliceData_.accountId_);
     208           2 :     auto bobAccount = Manager::instance().getAccount<SIPAccount>(bobData_.accountId_);
     209           2 : }
     210             : 
     211             : void
     212           2 : IceSdpParsingTest::tearDown()
     213             : {
     214           2 :     JAMI_INFO("Remove created accounts...");
     215           6 :     wait_for_removal_of({aliceData_.accountId_, bobData_.accountId_});
     216           2 : }
     217             : 
     218             : std::string
     219          26 : IceSdpParsingTest::getUserAlias(const std::string& callId)
     220             : {
     221          26 :     auto call = Manager::instance().getCallFromCallID(callId);
     222             : 
     223          26 :     if (not call) {
     224           2 :         JAMI_WARN("Call [%s] does not exist!", callId.c_str());
     225           2 :         return {};
     226             :     }
     227             : 
     228          24 :     auto const& account = call->getAccount().lock();
     229          24 :     if (not account) {
     230           0 :         return {};
     231             :     }
     232             : 
     233          24 :     return account->getAccountDetails()[ConfProperties::ALIAS];
     234          26 : }
     235             : 
     236             : void
     237           2 : IceSdpParsingTest::onIncomingCallWithMedia(const std::string& accountId,
     238             :                                            const std::string& callId,
     239             :                                            const std::vector<libjami::MediaMap> mediaList,
     240             :                                            CallData& callData)
     241             : {
     242           2 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     243             : 
     244           2 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     245             :               libjami::CallSignal::IncomingCallWithMedia::name,
     246             :               callData.alias_.c_str(),
     247             :               callId.c_str(),
     248             :               mediaList.size());
     249             : 
     250             :     // NOTE.
     251             :     // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
     252             :     // the client, and the client have no access to this type. But here, we only
     253             :     // needed to check if the call exists. This is the most straightforward and
     254             :     // reliable way to do it until we add a new API (like hasCall(id)).
     255           2 :     if (not Manager::instance().getCallFromCallID(callId)) {
     256           0 :         JAMI_WARN("Call [%s] does not exist!", callId.c_str());
     257           0 :         callData.callId_ = {};
     258           0 :         return;
     259             :     }
     260             : 
     261           2 :     std::unique_lock lock {callData.mtx_};
     262           2 :     callData.callId_ = callId;
     263           2 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCallWithMedia::name));
     264             : 
     265           2 :     callData.cv_.notify_one();
     266           2 : }
     267             : 
     268             : void
     269          18 : IceSdpParsingTest::onCallStateChange(const std::string&,
     270             :                                      const std::string& callId,
     271             :                                      const std::string& state,
     272             :                                      CallData& callData)
     273             : {
     274          18 :     auto call = Manager::instance().getCallFromCallID(callId);
     275          18 :     if (not call) {
     276           0 :         JAMI_WARN("Call [%s] does not exist!", callId.c_str());
     277           0 :         return;
     278             :     }
     279             : 
     280          18 :     auto account = call->getAccount().lock();
     281          18 :     if (not account) {
     282           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     283           0 :         return;
     284             :     }
     285             : 
     286          18 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     287             :               libjami::CallSignal::StateChange::name,
     288             :               callData.alias_.c_str(),
     289             :               callId.c_str(),
     290             :               state.c_str());
     291             : 
     292          18 :     if (account->getAccountID() != callData.accountId_)
     293           0 :         return;
     294             : 
     295             :     {
     296          18 :         std::unique_lock lock {callData.mtx_};
     297          18 :         callData.signals_.emplace_back(
     298          36 :             CallData::Signal(libjami::CallSignal::StateChange::name, state));
     299          18 :     }
     300             : 
     301          18 :     if (state == "CURRENT" or state == "OVER" or state == "HUNGUP") {
     302           8 :         callData.cv_.notify_one();
     303             :     }
     304          18 : }
     305             : 
     306             : void
     307           4 : IceSdpParsingTest::onMediaNegotiationStatus(const std::string& callId,
     308             :                                             const std::string& event,
     309             :                                             CallData& callData)
     310             : {
     311           4 :     auto call = Manager::instance().getCallFromCallID(callId);
     312           4 :     if (not call) {
     313           0 :         JAMI_WARN("Call [%s] does not exist!", callId.c_str());
     314           0 :         return;
     315             :     }
     316             : 
     317           4 :     auto account = call->getAccount().lock();
     318           4 :     if (not account) {
     319           0 :         JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
     320           0 :         return;
     321             :     }
     322             : 
     323           4 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     324             :               libjami::CallSignal::MediaNegotiationStatus::name,
     325             :               account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
     326             :               call->getCallId().c_str(),
     327             :               event.c_str());
     328             : 
     329           4 :     if (account->getAccountID() != callData.accountId_)
     330           0 :         return;
     331             : 
     332             :     {
     333           4 :         std::unique_lock lock {callData.mtx_};
     334           4 :         callData.signals_.emplace_back(
     335           8 :             CallData::Signal(libjami::CallSignal::MediaNegotiationStatus::name, event));
     336           4 :     }
     337             : 
     338           4 :     callData.cv_.notify_one();
     339           4 : }
     340             : 
     341             : bool
     342          14 : IceSdpParsingTest::waitForSignal(CallData& callData,
     343             :                                  const std::string& expectedSignal,
     344             :                                  const std::string& expectedEvent)
     345             : {
     346          14 :     const std::chrono::seconds TIME_OUT {30};
     347          14 :     std::unique_lock lock {callData.mtx_};
     348             : 
     349             :     // Combined signal + event (if any).
     350          14 :     std::string sigEvent(expectedSignal);
     351          14 :     if (not expectedEvent.empty())
     352          12 :         sigEvent += "::" + expectedEvent;
     353             : 
     354          14 :     JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
     355             : 
     356          14 :     auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
     357             :         // Search for the expected signal in list of received signals.
     358          72 :         for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
     359             :             // The predicate is true if the signal names match, and if the
     360             :             // expectedEvent is not empty, the events must also match.
     361          66 :             if (it->name_ == expectedSignal
     362          66 :                 and (expectedEvent.empty() or it->event_ == expectedEvent)) {
     363             :                 // Done with this signal.
     364          14 :                 callData.signals_.erase(it);
     365          14 :                 return true;
     366             :             }
     367             :         }
     368             :         // Signal/event not found.
     369           6 :         return false;
     370             :     });
     371             : 
     372          14 :     if (not res) {
     373           0 :         JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
     374             :                  callData.alias_.c_str(),
     375             :                  sigEvent.c_str());
     376             : 
     377           0 :         JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
     378             : 
     379           0 :         for (auto const& sig : callData.signals_) {
     380           0 :             JAMI_INFO() << "Signal [" << sig.name_
     381           0 :                         << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
     382             :         }
     383             :     }
     384             : 
     385          14 :     return res;
     386          14 : }
     387             : 
     388             : bool
     389           4 : IceSdpParsingTest::attachReceiver(std::shared_ptr<MediaReceiver> mediaReceiver,
     390             :                                   std::shared_ptr<RtpSession> rtpSession)
     391             : {
     392           4 :     CPPUNIT_ASSERT(mediaReceiver);
     393           4 :     CPPUNIT_ASSERT(mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO
     394             :                    or mediaReceiver->mediaType_ == MediaType::MEDIA_VIDEO);
     395             : 
     396           4 :     if (mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO) {
     397           4 :         auto audioRtp = std::dynamic_pointer_cast<AudioRtpSession>(rtpSession);
     398           4 :         auto receiver = audioRtp->getAudioReceive().get();
     399           4 :         CPPUNIT_ASSERT(receiver != nullptr);
     400           4 :         if (receiver == nullptr)
     401           0 :             return false;
     402           4 :         return receiver->attach(mediaReceiver.get());
     403           4 :     }
     404             : 
     405           0 :     auto videoRtp = std::dynamic_pointer_cast<video::VideoRtpSession>(rtpSession);
     406           0 :     auto receiver = videoRtp->getVideoReceive().get();
     407           0 :     CPPUNIT_ASSERT(receiver != nullptr);
     408           0 :     return receiver->attach(mediaReceiver.get());
     409           0 : }
     410             : 
     411             : bool
     412           4 : IceSdpParsingTest::detachReceiver(std::shared_ptr<MediaReceiver> mediaReceiver,
     413             :                                   std::shared_ptr<RtpSession> rtpSession)
     414             : {
     415           4 :     CPPUNIT_ASSERT(mediaReceiver);
     416           4 :     CPPUNIT_ASSERT(mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO
     417             :                    or mediaReceiver->mediaType_ == MediaType::MEDIA_VIDEO);
     418             : 
     419           4 :     if (mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO) {
     420           4 :         auto audioRtp = std::dynamic_pointer_cast<AudioRtpSession>(rtpSession);
     421           4 :         auto receiver = audioRtp->getAudioReceive().get();
     422           4 :         CPPUNIT_ASSERT(receiver != nullptr);
     423           4 :         return receiver->detach(mediaReceiver.get());
     424           4 :     }
     425             : 
     426           0 :     auto videoRtp = std::dynamic_pointer_cast<video::VideoRtpSession>(rtpSession);
     427           0 :     auto receiver = videoRtp->getVideoReceive().get();
     428           0 :     CPPUNIT_ASSERT(receiver != nullptr);
     429           0 :     return receiver->detach(mediaReceiver.get());
     430           0 : }
     431             : 
     432             : void
     433           2 : IceSdpParsingTest::configureTest(CallData& aliceData, CallData& bobData)
     434             : {
     435             :     {
     436           2 :         CPPUNIT_ASSERT(not aliceData.accountId_.empty());
     437           2 :         auto const& account = Manager::instance().getAccount<SIPAccount>(aliceData.accountId_);
     438           2 :         aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     439           2 :         aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     440           2 :         account->setLocalPort(aliceData.listeningPort_);
     441           2 :         account->enableIceCompIdRfc5245Compliance(aliceData.compliancyEnabled_);
     442           2 :     }
     443             : 
     444             :     {
     445           2 :         CPPUNIT_ASSERT(not bobData.accountId_.empty());
     446           2 :         auto const& account = Manager::instance().getAccount<SIPAccount>(bobData.accountId_);
     447           2 :         bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     448           2 :         bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     449           2 :         account->setLocalPort(bobData.listeningPort_);
     450           2 :         account->enableIceCompIdRfc5245Compliance(bobData.compliancyEnabled_);
     451           2 :     }
     452             : 
     453           2 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers;
     454             : 
     455             :     // Insert needed signal handlers.
     456           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     457           2 :         [&](const std::string& accountId,
     458             :             const std::string& callId,
     459             :             const std::string&,
     460             :             const std::vector<libjami::MediaMap> mediaList) {
     461           2 :             auto user = getUserAlias(callId);
     462           2 :             if (not user.empty())
     463           2 :                 onIncomingCallWithMedia(accountId,
     464             :                                         callId,
     465             :                                         mediaList,
     466           2 :                                         user == aliceData.alias_ ? aliceData : bobData);
     467           2 :         }));
     468             : 
     469           2 :     signalHandlers.insert(
     470           4 :         libjami::exportable_callback<libjami::CallSignal::StateChange>([&](const std::string& accountId,
     471             :                                                                        const std::string& callId,
     472             :                                                                        const std::string& state,
     473             :                                                                        signed) {
     474          20 :             auto user = getUserAlias(callId);
     475          20 :             if (not user.empty())
     476          18 :                 onCallStateChange(accountId,
     477             :                                   callId,
     478             :                                   state,
     479          18 :                                   user == aliceData.alias_ ? aliceData : bobData);
     480          20 :         }));
     481             : 
     482           2 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::MediaNegotiationStatus>(
     483           4 :         [&](const std::string& callId,
     484             :             const std::string& event,
     485             :             const std::vector<std::map<std::string, std::string>>& /* mediaList */) {
     486           4 :             auto user = getUserAlias(callId);
     487           4 :             if (not user.empty())
     488           4 :                 onMediaNegotiationStatus(callId,
     489             :                                          event,
     490           4 :                                          user == aliceData.alias_ ? aliceData : bobData);
     491           4 :         }));
     492             : 
     493           2 :     libjami::registerSignalHandlers(signalHandlers);
     494           2 : }
     495             : 
     496             : void
     497           2 : IceSdpParsingTest::test_call()
     498             : {
     499           2 :     configureTest(aliceData_, bobData_);
     500             : 
     501           2 :     JAMI_INFO("=== Start a call and validate ===");
     502             : 
     503             :     // NOTE:
     504             :     // We use two audio media instead of one audio and one video media
     505             :     // to be able to run the test on machines that do not have access to
     506             :     // camera.
     507             :     // For this specific UT, testing with two audio media is valid, because
     508             :     // the main goal is to validate that the media sockets negotiated
     509             :     // through ICE can correctly exchange media (RTP packets).
     510             : 
     511           2 :     MediaAttribute media_0(MediaType::MEDIA_AUDIO);
     512           2 :     media_0.label_ = "audio_0";
     513           2 :     media_0.enabled_ = true;
     514           2 :     MediaAttribute media_1(MediaType::MEDIA_AUDIO);
     515           2 :     media_1.label_ = "audio_1";
     516           2 :     media_1.enabled_ = true;
     517             : 
     518           2 :     std::vector<MediaAttribute> offer;
     519           2 :     offer.emplace_back(media_0);
     520           2 :     offer.emplace_back(media_1);
     521             : 
     522           2 :     std::vector<MediaAttribute> answer;
     523           2 :     answer.emplace_back(media_0);
     524           2 :     answer.emplace_back(media_1);
     525             : 
     526           2 :     CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, offer.size());
     527           2 :     CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, answer.size());
     528           2 :     auto bobAddr = dhtnet::ip_utils::getLocalAddr(AF_INET);
     529           2 :     bobAddr.setPort(bobData_.listeningPort_);
     530             : 
     531           4 :     aliceData_.callId_ = libjami::placeCallWithMedia(aliceData_.accountId_,
     532           4 :                                                    bobAddr.toString(true),
     533           4 :                                                    MediaAttribute::mediaAttributesToMediaMaps(
     534           2 :                                                        offer));
     535           2 :     CPPUNIT_ASSERT(not aliceData_.callId_.empty());
     536             : 
     537           2 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     538             :               aliceData_.accountId_.c_str(),
     539             :               bobData_.accountId_.c_str());
     540             : 
     541             :     // Give it some time to ring
     542           2 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     543             : 
     544             :     // Wait for call to be processed.
     545           2 :     CPPUNIT_ASSERT(
     546             :         waitForSignal(aliceData_, libjami::CallSignal::StateChange::name, StateEvent::RINGING));
     547             : 
     548             :     // Wait for incoming call signal.
     549           2 :     CPPUNIT_ASSERT(waitForSignal(bobData_, libjami::CallSignal::IncomingCallWithMedia::name));
     550             : 
     551             :     // Answer the call.
     552           2 :     libjami::acceptWithMedia(bobData_.accountId_,
     553           2 :                            bobData_.callId_,
     554           4 :                            MediaAttribute::mediaAttributesToMediaMaps(answer));
     555             : 
     556             :     // Wait for media negotiation complete signal.
     557           2 :     CPPUNIT_ASSERT(waitForSignal(bobData_,
     558             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
     559             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     560             : 
     561             :     // Wait for the StateChange signal.
     562           2 :     CPPUNIT_ASSERT(
     563             :         waitForSignal(bobData_, libjami::CallSignal::StateChange::name, StateEvent::CURRENT));
     564             : 
     565           2 :     JAMI_INFO("BOB answered the call [%s]", bobData_.callId_.c_str());
     566             : 
     567             :     // Wait for media negotiation complete signal.
     568           2 :     CPPUNIT_ASSERT(waitForSignal(aliceData_,
     569             :                                  libjami::CallSignal::MediaNegotiationStatus::name,
     570             :                                  libjami::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
     571             : 
     572             :     // Give some time to media to start.
     573           2 :     std::this_thread::sleep_for(std::chrono::seconds(2));
     574             : 
     575             :     // Register the media observer to validate media flow.
     576           2 :     CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, mediaReceivers_.size());
     577             :     auto call = std::dynamic_pointer_cast<SIPCall>(
     578           2 :         Manager::instance().getCallFromCallID(aliceData_.callId_));
     579           2 :     CPPUNIT_ASSERT(call);
     580             : 
     581           2 :     auto rtpList = call->getRtpSessionList();
     582           2 :     CPPUNIT_ASSERT(rtpList.size() == offer.size());
     583             : 
     584           6 :     for (size_t i = 0; i < MEDIA_COUNT; i++) {
     585           4 :         CPPUNIT_ASSERT(rtpList[i]);
     586           4 :         CPPUNIT_ASSERT(rtpList[i]->getMediaType() == offer[i].type_);
     587           4 :         CPPUNIT_ASSERT(attachReceiver(mediaReceivers_[i], rtpList[i]));
     588             :     }
     589             : 
     590             :     // NOTE:
     591             :     // This validation step works on hosts/containers that have correctly
     592             :     // configured sound system.
     593             :     // Currenty hosts/containers used for testing are not setup to capture
     594             :     // and playback audio, so this validation will be disabled for now.
     595             : #if 0
     596             :     JAMI_INFO("Waiting for media to flow ...");
     597             :     for (size_t i = 0; i < MEDIA_COUNT; i++) {
     598             :         CPPUNIT_ASSERT(mediaReceivers_[i]->waitForMediaFlow());
     599             :     }
     600             : #endif
     601             :     // Detach the observers.
     602           6 :     for (size_t i = 0; i < MEDIA_COUNT; i++) {
     603           4 :         CPPUNIT_ASSERT(detachReceiver(mediaReceivers_[i], rtpList[i]));
     604             :     }
     605             : 
     606             :     // Bob hang-up.
     607           2 :     JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     608           2 :     Manager::instance().hangupCall(bobData_.accountId_, bobData_.callId_);
     609             : 
     610           2 :     CPPUNIT_ASSERT_EQUAL(true,
     611             :                          waitForSignal(aliceData_,
     612             :                                        libjami::CallSignal::StateChange::name,
     613             :                                        StateEvent::HUNGUP));
     614             : 
     615           2 :     CPPUNIT_ASSERT_EQUAL(true,
     616             :                          waitForSignal(bobData_,
     617             :                                        libjami::CallSignal::StateChange::name,
     618             :                                        StateEvent::HUNGUP));
     619             : 
     620           2 :     JAMI_INFO("Call terminated on both sides");
     621           2 : }
     622             : 
     623             : void
     624           1 : IceSdpParsingTest::call_with_rfc5245_compliancy_disabled()
     625             : {
     626           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     627             : 
     628           1 :     aliceData_.compliancyEnabled_ = bobData_.compliancyEnabled_ = false;
     629           1 :     test_call();
     630           1 : }
     631             : 
     632             : void
     633           1 : IceSdpParsingTest::call_with_rfc5245_compliancy_enabled()
     634             : {
     635           1 :     JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
     636             : 
     637           1 :     aliceData_.compliancyEnabled_ = bobData_.compliancyEnabled_ = true;
     638           1 :     test_call();
     639           1 : }
     640             : 
     641             : } // namespace test
     642             : } // namespace jami
     643             : 
     644           1 : RING_TEST_RUNNER(jami::test::IceSdpParsingTest::name())

Generated by: LCOV version 1.14