Commit 04ec59a1 authored by Mohamed Chibani's avatar Mohamed Chibani

ut_ice_sdp_parser: rework test

Rework the test to use two audio streams instead of audio+video.
This is needed to run the test on environments where the camera
is not available (e.g. Docker). For this specific test, the media
type is irrelevant, so using two audio media is acceptable.

Gitlab: #516

Change-Id: Idc4911d5aea5ffdd931bd7d63516ed484344d41c
parent 54d9c0f0
......@@ -86,7 +86,19 @@ SIPAccountBase::CreateClientDialogAndInvite(const pj_str_t* from,
pjsip_dialog** dlg,
pjsip_inv_session** inv)
{
sip_utils::register_thread();
JAMI_DBG("Creating SIP dialog: \n"
"from: %s\n"
"contact: %s\n"
"to: %s\n",
from->ptr,
contact->ptr,
to->ptr);
if (target) {
JAMI_DBG("target: %s", target->ptr);
} else {
JAMI_DBG("No target provided, using 'to' as target");
}
if (pjsip_dlg_create_uac(pjsip_ua_instance(), from, contact, to, target, dlg) != PJ_SUCCESS) {
JAMI_ERR("Unable to create SIP dialogs for user agent client when calling %s", to->ptr);
......
......@@ -2715,8 +2715,8 @@ SIPCall::addDummyVideoRtpSession()
std::shared_ptr<AudioRtpSession>
SIPCall::getAudioRtp() const
{
// For the moment, we support only one audio stream.
// For the moment, the clients support only one audio stream, so we
// return the first audio stream.
for (auto const& stream : rtpStreams_) {
auto rtp = stream.rtpSession_;
if (rtp->getMediaType() == MediaType::MEDIA_AUDIO) {
......@@ -2741,6 +2741,17 @@ SIPCall::getVideoRtp() const
}
#endif
std::vector<std::shared_ptr<RtpSession>>
SIPCall::getRtpSessionList() const
{
std::vector<std::shared_ptr<RtpSession>> rtpList;
rtpList.reserve(rtpStreams_.size());
for (auto const& stream : rtpStreams_) {
rtpList.emplace_back(stream.rtpSession_);
}
return rtpList;
}
void
SIPCall::monitor() const
{
......
......@@ -247,7 +247,6 @@ public:
void operator()(pjsip_inv_session*) const noexcept;
};
// NOT SIP RELATED (good candidates to be moved elsewhere)
std::shared_ptr<AudioRtpSession> getAudioRtp() const;
#ifdef ENABLE_VIDEO
/**
......@@ -256,6 +255,8 @@ public:
std::shared_ptr<video::VideoRtpSession> getVideoRtp() const;
std::shared_ptr<video::VideoRtpSession> addDummyVideoRtpSession();
#endif
// Get the list of current RTP sessions
std::vector<std::shared_ptr<RtpSession>> getRtpSessionList() const;
bool isSrtpEnabled() const { return srtpEnabled_; }
......
......@@ -25,7 +25,7 @@
#include <string>
#include "manager.h"
#include "jamidht/jamiaccount.h"
#include "sip/sipaccount.h"
#include "../../test_runner.h"
#include "dring.h"
......@@ -35,6 +35,7 @@
#include "sip/sipcall.h"
#include "media/audio/audio_rtp_session.h"
#include "media/audio/audio_receive_thread.h"
#include "media/video/video_rtp_session.h"
#include "common.h"
......@@ -58,6 +59,7 @@ struct CallData
std::string accountId_ {};
std::string userName_ {};
uint16_t listeningPort_ {0};
std::string alias_ {};
std::string callId_ {};
std::vector<Signal> signals_;
......@@ -119,15 +121,15 @@ class IceSdpParsingTest : public CppUnit::TestFixture
{
public:
IceSdpParsingTest()
: audioReceiver_(std::make_shared<MediaReceiver>(MediaType::MEDIA_AUDIO))
{
// Init daemon
DRing::init(DRing::InitFlag(DRing::DRING_FLAG_DEBUG | DRing::DRING_FLAG_CONSOLE_LOG));
if (not Manager::instance().initialized)
CPPUNIT_ASSERT(DRing::start("dring-sample.yml"));
// We must have valid media receiver.
CPPUNIT_ASSERT(audioReceiver_);
for (size_t idx = 0; idx < MEDIA_COUNT; idx++) {
mediaReceivers_.emplace_back(std::make_shared<MediaReceiver>(MediaType::MEDIA_AUDIO));
}
}
~IceSdpParsingTest() { DRing::fini(); }
......@@ -158,7 +160,7 @@ private:
CallData& callData);
// Helpers
void audio_video_call();
void test_call();
static void configureTest(CallData& bob, CallData& alice);
static std::string getUserAlias(const std::string& callId);
// Wait for a signal from the callbacks. Some signals also report the event that
......@@ -166,13 +168,16 @@ private:
static bool waitForSignal(CallData& callData,
const std::string& signal,
const std::string& expectedEvent = {});
static bool attachReceiver(const std::string& callId, std::shared_ptr<MediaReceiver> receiver);
static bool detachReceiver(const std::string& callId, std::shared_ptr<MediaReceiver> receiver);
static bool attachReceiver(std::shared_ptr<MediaReceiver> receiver,
std::shared_ptr<RtpSession> rtpStream);
static bool detachReceiver(std::shared_ptr<MediaReceiver> receiver,
std::shared_ptr<RtpSession> rtpStream);
private:
CallData aliceData_;
CallData bobData_;
std::shared_ptr<MediaReceiver> audioReceiver_;
const size_t MEDIA_COUNT {2};
std::vector<std::shared_ptr<MediaReceiver>> mediaReceivers_;
};
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(IceSdpParsingTest, IceSdpParsingTest::name());
......@@ -180,22 +185,35 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(IceSdpParsingTest, IceSdpParsingTest::name
void
IceSdpParsingTest::setUp()
{
auto actors = load_actors("actors/alice-bob-no-upnp.yml");
aliceData_.accountId_ = actors["alice"];
bobData_.accountId_ = actors["bob"];
JAMI_INFO("Initializing accounts ...");
auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceData_.accountId_);
aliceData_.listeningPort_ = 5080;
std::map<std::string, std::string> details = DRing::getAccountTemplate("SIP");
details[ConfProperties::TYPE] = "SIP";
details[ConfProperties::DISPLAYNAME] = "ALICE";
details[ConfProperties::ALIAS] = "ALICE";
details[ConfProperties::LOCAL_PORT] = std::to_string(aliceData_.listeningPort_);
details[ConfProperties::UPNP_ENABLED] = "false";
aliceData_.accountId_ = Manager::instance().addAccount(details);
bobData_.listeningPort_ = 5082;
details = DRing::getAccountTemplate("SIP");
details[ConfProperties::TYPE] = "SIP";
details[ConfProperties::DISPLAYNAME] = "BOB";
details[ConfProperties::ALIAS] = "BOB";
details[ConfProperties::LOCAL_PORT] = std::to_string(bobData_.listeningPort_);
details[ConfProperties::UPNP_ENABLED] = "false";
bobData_.accountId_ = Manager::instance().addAccount(details);
JAMI_INFO("Initialize accounts ...");
auto aliceAccount = Manager::instance().getAccount<SIPAccount>(aliceData_.accountId_);
aliceAccount->enableMultiStream(true);
auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobData_.accountId_);
auto bobAccount = Manager::instance().getAccount<SIPAccount>(bobData_.accountId_);
bobAccount->enableMultiStream(true);
wait_for_announcement_of({aliceAccount->getAccountID(), bobAccount->getAccountID()});
}
void
IceSdpParsingTest::tearDown()
{
JAMI_INFO("Remove created accounts...");
wait_for_removal_of({aliceData_.accountId_, bobData_.accountId_});
}
......@@ -369,21 +387,15 @@ IceSdpParsingTest::waitForSignal(CallData& callData,
}
bool
IceSdpParsingTest::attachReceiver(const std::string& callId,
std::shared_ptr<MediaReceiver> mediaReceiver)
IceSdpParsingTest::attachReceiver(std::shared_ptr<MediaReceiver> mediaReceiver,
std::shared_ptr<RtpSession> rtpSession)
{
CPPUNIT_ASSERT(mediaReceiver);
auto call = std::dynamic_pointer_cast<SIPCall>(Manager::instance().getCallFromCallID(callId));
if (not call) {
JAMI_ERR("Call [%s] does not exist!", callId.c_str());
}
CPPUNIT_ASSERT(call);
CPPUNIT_ASSERT(mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO
or mediaReceiver->mediaType_ == MediaType::MEDIA_VIDEO);
if (mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO) {
auto audioRtp = call->getAudioRtp();
auto audioRtp = std::dynamic_pointer_cast<AudioRtpSession>(rtpSession);
auto receiver = audioRtp->getAudioReceive().get();
CPPUNIT_ASSERT(receiver != nullptr);
if (receiver == nullptr)
......@@ -391,31 +403,28 @@ IceSdpParsingTest::attachReceiver(const std::string& callId,
return receiver->attach(mediaReceiver.get());
}
auto videoRtp = call->getVideoRtp();
auto videoRtp = std::dynamic_pointer_cast<video::VideoRtpSession>(rtpSession);
auto receiver = videoRtp->getVideoReceive().get();
CPPUNIT_ASSERT(receiver != nullptr);
return receiver->attach(mediaReceiver.get());
}
bool
IceSdpParsingTest::detachReceiver(const std::string& callId,
std::shared_ptr<MediaReceiver> mediaReceiver)
IceSdpParsingTest::detachReceiver(std::shared_ptr<MediaReceiver> mediaReceiver,
std::shared_ptr<RtpSession> rtpSession)
{
auto call = std::dynamic_pointer_cast<SIPCall>(Manager::instance().getCallFromCallID(callId));
CPPUNIT_ASSERT(call);
CPPUNIT_ASSERT(mediaReceiver);
CPPUNIT_ASSERT(mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO
or mediaReceiver->mediaType_ == MediaType::MEDIA_VIDEO);
if (mediaReceiver->mediaType_ == MediaType::MEDIA_AUDIO) {
auto audioRtp = call->getAudioRtp();
auto audioRtp = std::dynamic_pointer_cast<AudioRtpSession>(rtpSession);
auto receiver = audioRtp->getAudioReceive().get();
CPPUNIT_ASSERT(receiver != nullptr);
return receiver->detach(mediaReceiver.get());
}
auto videoRtp = call->getVideoRtp();
auto videoRtp = std::dynamic_pointer_cast<video::VideoRtpSession>(rtpSession);
auto receiver = videoRtp->getVideoReceive().get();
CPPUNIT_ASSERT(receiver != nullptr);
return receiver->detach(mediaReceiver.get());
......@@ -426,19 +435,20 @@ IceSdpParsingTest::configureTest(CallData& aliceData, CallData& bobData)
{
{
CPPUNIT_ASSERT(not aliceData.accountId_.empty());
auto const& account = Manager::instance().getAccount<JamiAccount>(aliceData.accountId_);
auto const& account = Manager::instance().getAccount<SIPAccount>(aliceData.accountId_);
aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
account->setLocalPort(aliceData.listeningPort_);
account->enableIceForMedia(true);
account->enableIceCompIdRfc5245Compliance(aliceData.compliancyEnabled_);
}
{
CPPUNIT_ASSERT(not bobData.accountId_.empty());
auto const& account = Manager::instance().getAccount<JamiAccount>(bobData.accountId_);
auto const& account = Manager::instance().getAccount<SIPAccount>(bobData.accountId_);
bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
account->setLocalPort(bobData.listeningPort_);
account->enableIceCompIdRfc5245Compliance(bobData.compliancyEnabled_);
}
......@@ -466,7 +476,9 @@ IceSdpParsingTest::configureTest(CallData& aliceData, CallData& bobData)
}));
signalHandlers.insert(DRing::exportable_callback<DRing::CallSignal::MediaNegotiationStatus>(
[&](const std::string& callId, const std::string& event, const std::vector<std::map<std::string, std::string>>& media) {
[&](const std::string& callId,
const std::string& event,
const std::vector<std::map<std::string, std::string>>& /* mediaList */) {
auto user = getUserAlias(callId);
if (not user.empty())
onMediaNegotiationStatus(callId,
......@@ -478,46 +490,68 @@ IceSdpParsingTest::configureTest(CallData& aliceData, CallData& bobData)
}
void
IceSdpParsingTest::audio_video_call()
IceSdpParsingTest::test_call()
{
configureTest(aliceData_, bobData_);
JAMI_INFO("=== Start a call and validate ===");
MediaAttribute audio(MediaType::MEDIA_AUDIO);
audio.label_ = "main audio";
MediaAttribute video(MediaType::MEDIA_VIDEO);
video.label_ = "main video";
// NOTE:
// We use two audio media instead of one audio and one video media
// to be able to run the test on machines that do not have access to
// camera.
// For this specific UT, testing with two audio media is valid, because
// the main goal is to validate that the media sockets negotiated
// through ICE can correctly exchange media (RTP packets).
MediaAttribute media_0(MediaType::MEDIA_AUDIO);
media_0.label_ = "audio_0";
media_0.enabled_ = true;
MediaAttribute media_1(MediaType::MEDIA_AUDIO);
media_1.label_ = "audio_1";
media_1.enabled_ = true;
std::vector<MediaAttribute> offer;
offer.emplace_back(audio);
offer.emplace_back(video);
offer.emplace_back(media_0);
offer.emplace_back(media_1);
std::vector<MediaAttribute> answer;
answer.emplace_back(audio);
answer.emplace_back(video);
answer.emplace_back(media_0);
answer.emplace_back(media_1);
auto const& aliceCall = std::dynamic_pointer_cast<SIPCall>(
(Manager::instance().getAccount<JamiAccount>(aliceData_.accountId_))
->newOutgoingCall(bobData_.userName_, offer));
CPPUNIT_ASSERT(aliceCall);
aliceData_.callId_ = aliceCall->getCallId();
CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, offer.size());
CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, answer.size());
auto bobAddr = ip_utils::getLocalAddr(AF_INET);
bobAddr.setPort(bobData_.listeningPort_);
aliceData_.callId_ = DRing::placeCallWithMedia(aliceData_.accountId_,
bobAddr.toString(true),
MediaAttribute::mediaAttributesToMediaMaps(
offer));
CPPUNIT_ASSERT(not aliceData_.callId_.empty());
JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
aliceData_.accountId_.c_str(),
bobData_.accountId_.c_str());
// Give it some time to ring
std::this_thread::sleep_for(std::chrono::seconds(2));
// Wait for call to be processed.
CPPUNIT_ASSERT(
waitForSignal(aliceData_, DRing::CallSignal::StateChange::name, StateEvent::RINGING));
// Wait for incoming call signal.
CPPUNIT_ASSERT(waitForSignal(bobData_, DRing::CallSignal::IncomingCallWithMedia::name));
// Answer the call.
{
auto const& mediaList = MediaAttribute::mediaAttributesToMediaMaps(answer);
Manager::instance().answerCallWithMedia(bobData_.callId_, mediaList);
}
DRing::acceptWithMedia(bobData_.callId_, MediaAttribute::mediaAttributesToMediaMaps(answer));
// Wait for media negotiation complete signal.
CPPUNIT_ASSERT(waitForSignal(bobData_,
DRing::CallSignal::MediaNegotiationStatus::name,
DRing::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
// Wait for the StateChange signal.
CPPUNIT_ASSERT(
waitForSignal(bobData_, DRing::CallSignal::StateChange::name, StateEvent::CURRENT));
......@@ -533,12 +567,35 @@ IceSdpParsingTest::audio_video_call()
std::this_thread::sleep_for(std::chrono::seconds(2));
// Register the media observer to validate media flow.
// NOTE: For now, only audio is validated.
CPPUNIT_ASSERT(attachReceiver(aliceData_.callId_, audioReceiver_));
CPPUNIT_ASSERT_EQUAL(MEDIA_COUNT, mediaReceivers_.size());
auto call = std::dynamic_pointer_cast<SIPCall>(
Manager::instance().getCallFromCallID(aliceData_.callId_));
CPPUNIT_ASSERT(call);
JAMI_INFO("Waiting for media fot flow ...");
CPPUNIT_ASSERT(audioReceiver_->waitForMediaFlow());
CPPUNIT_ASSERT(detachReceiver(aliceData_.callId_, audioReceiver_));
auto rtpList = call->getRtpSessionList();
CPPUNIT_ASSERT(rtpList.size() == offer.size());
for (size_t i = 0; i < MEDIA_COUNT; i++) {
CPPUNIT_ASSERT(rtpList[i]);
CPPUNIT_ASSERT(rtpList[i]->getMediaType() == offer[i].type_);
CPPUNIT_ASSERT(attachReceiver(mediaReceivers_[i], rtpList[i]));
}
// NOTE:
// This validation step works on hosts/containers that have correctly
// configured sound system.
// Currenty hosts/containers used for testing are not setup to capture
// and playback audio, so this validation will be disabled for now.
#if 0
JAMI_INFO("Waiting for media to flow ...");
for (size_t i = 0; i < MEDIA_COUNT; i++) {
CPPUNIT_ASSERT(mediaReceivers_[i]->waitForMediaFlow());
}
#endif
// Detach the observers.
for (size_t i = 0; i < MEDIA_COUNT; i++) {
CPPUNIT_ASSERT(detachReceiver(mediaReceivers_[i], rtpList[i]));
}
// Bob hang-up.
JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
......@@ -549,6 +606,11 @@ IceSdpParsingTest::audio_video_call()
DRing::CallSignal::StateChange::name,
StateEvent::HUNGUP));
CPPUNIT_ASSERT_EQUAL(true,
waitForSignal(bobData_,
DRing::CallSignal::StateChange::name,
StateEvent::HUNGUP));
JAMI_INFO("Call terminated on both sides");
}
......@@ -558,7 +620,7 @@ IceSdpParsingTest::call_with_rfc5245_compliancy_disabled()
JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
aliceData_.compliancyEnabled_ = bobData_.compliancyEnabled_ = false;
audio_video_call();
test_call();
}
void
......@@ -567,7 +629,7 @@ IceSdpParsingTest::call_with_rfc5245_compliancy_enabled()
JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
aliceData_.compliancyEnabled_ = bobData_.compliancyEnabled_ = true;
audio_video_call();
test_call();
}
} // namespace test
......
......@@ -90,7 +90,7 @@ private:
CPPUNIT_TEST_SUITE(SipBasicCallTest);
CPPUNIT_TEST(audio_only_test);
CPPUNIT_TEST(audio_video_test);
#if 0
#if 0
// Test when the peer answers will all media disabled (RTP port = 0)
// For now, this test will cause a crash. Must be enabled once the
// crash is fixed
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment