Bug 1355947 - Use TestNrSocket to build a fake ICE implementation for testing; r=drno

MozReview-Commit-ID: GhSX9t8DmTO

--HG--
extra : rebase_source : 8fea1934f88b378606b4af96d96c27560906fd88
extra : histedit_source : 4d6fc904c79c74ba8c24e816d70368ee3a850769
This commit is contained in:
Dan Minor 2017-04-18 06:54:34 -04:00
Родитель 1cc1e5734f
Коммит a9df96e992
2 изменённых файлов: 426 добавлений и 0 удалений

Просмотреть файл

@ -16,6 +16,7 @@ if CONFIG['OS_TARGET'] != 'WINNT':
'simpletokenbucket_unittest.cpp',
'sockettransportservice_unittest.cpp',
'stunserver.cpp',
'test_nr_socket_ice_unittest.cpp',
'test_nr_socket_unittest.cpp',
'TestSyncRunnable.cpp',
'transport_unittests.cpp',

Просмотреть файл

@ -0,0 +1,425 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Some of this code is taken from nricectx.cpp and nricemediastream.cpp
// which in turn contains code cut-and-pasted from nICEr. Copyright is:
/*
Copyright (c) 2007, Adobe Systems, Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems, Network Resonance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "gtest/gtest.h"
#include "gtest_utils.h"
#include "nss.h"
#include "ssl.h"
extern "C" {
#include "stun_msg.h"
#include "ice_ctx.h"
#include "ice_peer_ctx.h"
#include "nICEr/src/net/transport_addr.h"
}
#include "mtransport_test_utils.h"
#include "nricectx.h"
#include "nricemediastream.h"
#include "runnable_utils.h"
#include "test_nr_socket.h"
namespace mozilla {
static unsigned int kDefaultTimeout = 7000;
class IcePeer {
public:
IcePeer(const char* name, TestNat* nat, UINT4 flags,
MtransportTestUtils* test_utils)
: name_(name)
, ice_checking_(false)
, ice_connected_(false)
, ice_disconnected_(false)
, gather_cb_(false)
, stream_ready_(false)
, stream_failed_(false)
, ice_ctx_(nullptr)
, peer_ctx_(nullptr)
, nat_(nat)
, test_utils_(test_utils)
{
nr_ice_ctx_create(const_cast<char *>(name_.c_str()), flags, &ice_ctx_);
if (nat_) {
nr_socket_factory* factory;
nat_->create_socket_factory(&factory);
nr_ice_ctx_set_socket_factory(ice_ctx_, factory);
}
// Create the handler objects
ice_handler_vtbl_ = new nr_ice_handler_vtbl();
ice_handler_vtbl_->select_pair = &IcePeer::select_pair;
ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready;
ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed;
ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected;
ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd;
ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking;
ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected;
ice_handler_ = new nr_ice_handler();
ice_handler_->vtbl = ice_handler_vtbl_;
ice_handler_->obj = this;
nr_ice_peer_ctx_create(ice_ctx_, ice_handler_,
const_cast<char *>(name_.c_str()),
&peer_ctx_);
nr_ice_add_media_stream(ice_ctx_,
const_cast<char *>(name_.c_str()),
2, &ice_media_stream_);
nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_);
}
virtual ~IcePeer()
{
Destroy();
}
void Destroy()
{
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IcePeer::Destroy_s),
NS_DISPATCH_SYNC);
}
void Destroy_s()
{
nr_ice_peer_ctx_destroy(&peer_ctx_);
delete ice_handler_;
delete ice_handler_vtbl_;
nr_ice_ctx_destroy(&ice_ctx_);
}
void Gather(bool default_route_only=false)
{
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IcePeer::Gather_s, default_route_only),
NS_DISPATCH_SYNC);
}
void Gather_s(bool default_route_only=false)
{
int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this);
ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK);
}
std::vector<std::string> GetLocalCandidates() const {
char attr[256];
std::vector<std::string> candidates;
nr_ice_component* comp = STAILQ_FIRST(&ice_media_stream_->components);
while(comp){
if (comp->state != NR_ICE_COMPONENT_DISABLED) {
nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates);
while(cand){
int r = nr_ice_format_candidate_attribute(cand, attr, 255);
if (r == 0) {
candidates.push_back(attr);
}
cand = TAILQ_NEXT(cand, entry_comp);
}
}
comp = STAILQ_NEXT(comp, entry);
}
return candidates;
}
std::vector<std::string> GetGlobalAttributes() {
char **attrs = nullptr;
int attrct;
std::vector<std::string> ret;
nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct);
for (int i=0; i<attrct; i++) {
ret.push_back(std::string(attrs[i]));
RFREE(attrs[i]);
}
RFREE(attrs);
return ret;
}
void ParseGlobalAttributes(std::vector<std::string> attrs) {
std::vector<char *> attrs_in;
for (auto& attr : attrs) {
attrs_in.push_back(const_cast<char *>(attr.c_str()));
}
int r = nr_ice_peer_ctx_parse_global_attributes(peer_ctx_,
attrs_in.size() ?
&attrs_in[0] : nullptr,
attrs_in.size());
ASSERT_EQ(0, r);
}
void SetControlling(bool controlling) {
peer_ctx_->controlling = controlling ? 1 : 0;
}
void SetRemoteAttributes(std::vector<std::string> attributes) {
int r;
std::vector<char*> attrs;
for (auto& attr: attributes) {
attrs.push_back(const_cast<char*>(attr.c_str()));
}
if (attrs.size()) {
r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_, &attrs[0], attrs.size());
ASSERT_EQ(0, r);
}
}
void StartChecks() {
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IcePeer::StartChecks_s),
NS_DISPATCH_SYNC);
}
void StartChecks_s() {
int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_);
ASSERT_EQ(0, r);
r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1);
ASSERT_EQ(0, r);
}
// Handler callbacks
static int select_pair(void *obj, nr_ice_media_stream *stream,
int component_id, nr_ice_cand_pair **potentials,
int potential_ct) {
return 0;
}
static int stream_ready(void *obj, nr_ice_media_stream *stream) {
IcePeer* peer = static_cast<IcePeer*>(obj);
peer->stream_ready_ = true;
return 0;
}
static int stream_failed(void *obj, nr_ice_media_stream *stream) {
IcePeer* peer = static_cast<IcePeer*>(obj);
peer->stream_failed_ = true;
return 0;
}
static int ice_checking(void *obj, nr_ice_peer_ctx *pctx) {
IcePeer* peer = static_cast<IcePeer*>(obj);
peer->ice_checking_ = true;
return 0;
}
static int ice_connected(void *obj, nr_ice_peer_ctx *pctx) {
IcePeer* peer = static_cast<IcePeer*>(obj);
peer->ice_connected_ = true;
return 0;
}
static int ice_disconnected(void *obj, nr_ice_peer_ctx *pctx) {
IcePeer* peer = static_cast<IcePeer*>(obj);
peer->ice_disconnected_ = true;
return 0;
}
static int msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
nr_ice_media_stream *stream, int component_id,
UCHAR *msg, int len) {
return 0;
}
static void gather_cb(NR_SOCKET s, int h, void *arg) {
IcePeer* peer = static_cast<IcePeer*>(arg);
peer->gather_cb_ = true;
}
std::string name_;
bool ice_checking_;
bool ice_connected_;
bool ice_disconnected_;
bool gather_cb_;
bool stream_ready_;
bool stream_failed_;
nr_ice_ctx* ice_ctx_;
nr_ice_handler* ice_handler_;
nr_ice_handler_vtbl* ice_handler_vtbl_;
nr_ice_media_stream* ice_media_stream_;
nr_ice_peer_ctx* peer_ctx_;
TestNat* nat_;
MtransportTestUtils* test_utils_;
};
class TestNrSocketIceUnitTest : public ::testing::Test {
public:
void SetUp() override
{
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();
test_utils_ = new MtransportTestUtils();
test_utils2_ = new MtransportTestUtils();
NrIceCtx::InitializeGlobals(false, false, false);
}
void TearDown() override
{
delete test_utils_;
delete test_utils2_;
}
MtransportTestUtils* test_utils_;
MtransportTestUtils* test_utils2_;
};
TEST_F(TestNrSocketIceUnitTest, TestIcePeer) {
IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
test_utils_);
ASSERT_NE(peer.ice_ctx_, nullptr);
ASSERT_NE(peer.peer_ctx_, nullptr);
ASSERT_NE(peer.ice_media_stream_, nullptr);
peer.Gather();
std::vector<std::string> attrs = peer.GetGlobalAttributes();
ASSERT_NE(attrs.size(), 0UL);
std::vector<std::string> candidates = peer.GetLocalCandidates();
ASSERT_NE(candidates.size(), 0UL);
}
TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) {
IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
test_utils_);
IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
test_utils2_);
peer.SetControlling(true);
peer2.SetControlling(false);
peer.Gather();
peer2.Gather();
std::vector<std::string> attrs = peer.GetGlobalAttributes();
peer2.ParseGlobalAttributes(attrs);
std::vector<std::string> candidates = peer.GetLocalCandidates();
peer2.SetRemoteAttributes(candidates);
attrs = peer2.GetGlobalAttributes();
peer.ParseGlobalAttributes(attrs);
candidates = peer2.GetLocalCandidates();
peer.SetRemoteAttributes(candidates);
peer2.StartChecks();
peer.StartChecks();
ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
}
TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) {
IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
test_utils_);
RefPtr<TestNat> nat(new TestNat);
class NatDelegate : public TestNat::NatDelegate {
public:
NatDelegate()
: messages(0) {}
int on_read(TestNat *nat, void *buf, size_t maxlen, size_t *len) override
{
return 0;
}
int on_sendto(TestNat *nat, const void *msg, size_t len,
int flags, nr_transport_addr *to) override
{
++messages;
// 25% packet loss
if (messages % 4 == 0) {
return 1;
}
return 0;
}
int on_write(TestNat *nat, const void *msg, size_t len, size_t *written) override
{
return 0;
}
int messages;
} delegate;
nat->nat_delegate_ = &delegate;
IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
test_utils2_);
peer.SetControlling(true);
peer2.SetControlling(false);
peer.Gather();
peer2.Gather();
std::vector<std::string> attrs = peer.GetGlobalAttributes();
peer2.ParseGlobalAttributes(attrs);
std::vector<std::string> candidates = peer.GetLocalCandidates();
peer2.SetRemoteAttributes(candidates);
attrs = peer2.GetGlobalAttributes();
peer.ParseGlobalAttributes(attrs);
candidates = peer2.GetLocalCandidates();
peer.SetRemoteAttributes(candidates);
peer2.StartChecks();
peer.StartChecks();
ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
}
}