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-05-02 09:40:27 Functions: 28 32 87.5 %

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

Generated by: LCOV version 1.14