From 1682c22a730317bed0c67ef06a4c796588b07561 Mon Sep 17 00:00:00 2001 From: "Byron Campen [:bwc]" Date: Wed, 4 Jun 2014 17:21:59 -0700 Subject: [PATCH] Bug 1004530 - Part 3: Unit test that verifies that new pairs will start when local gather happens after all preceding pairs have failed, provided the grace period has not elapsed. Also a couple more tests that use a new test-case feature. --- media/mtransport/test/ice_unittest.cpp | 198 +++++++++++++++++++++++-- 1 file changed, 188 insertions(+), 10 deletions(-) diff --git a/media/mtransport/test/ice_unittest.cpp b/media/mtransport/test/ice_unittest.cpp index 73249a62ef7..cf3dbdbd405 100644 --- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -59,6 +59,9 @@ const uint16_t kDefaultStunServerPort=3478; const std::string kBogusIceCandidate( (char *)"candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ"); +const std::string kUnreachableHostIceCandidate( + (char *)"candidate:0 1 UDP 2113601790 192.168.178.20 50769 typ host"); + std::string g_stun_server_address(kDefaultStunServerAddress); std::string g_stun_server_hostname(kDefaultStunServerHostname); std::string g_turn_server; @@ -85,7 +88,7 @@ static std::string SabotageHostCandidateAndDropReflexive( } if (candidate.find("typ host") != std::string::npos) { - return kBogusIceCandidate; + return kUnreachableHostIceCandidate; } return candidate; @@ -138,6 +141,74 @@ class IceCandidatePairCompare { } }; +class IceTestPeer; + +class SchedulableTrickleCandidate { + public: + SchedulableTrickleCandidate(IceTestPeer *peer, + size_t stream, + const std::string &candidate) : + peer_(peer), + stream_(stream), + candidate_(candidate), + timer_handle_(nullptr) { + } + + ~SchedulableTrickleCandidate() { + if (timer_handle_) + NR_async_timer_cancel(timer_handle_); + } + + void Schedule(unsigned int ms) { + test_utils->sts_target()->Dispatch( + WrapRunnable(this, &SchedulableTrickleCandidate::Schedule_s, ms), + NS_DISPATCH_SYNC); + } + + void Schedule_s(unsigned int ms) { + MOZ_ASSERT(!timer_handle_); + NR_ASYNC_TIMER_SET(ms, Trickle_cb, this, &timer_handle_); + } + + static void Trickle_cb(NR_SOCKET s, int how, void *cb_arg) { + static_cast(cb_arg)->Trickle(); + } + + void Trickle(); + + std::string& Candidate() { + return candidate_; + } + + const std::string& Candidate() const { + return candidate_; + } + + size_t Stream() const { + return stream_; + } + + bool IsHost() const { + return candidate_.find("typ host") != std::string::npos; + } + + bool IsReflexive() const { + return candidate_.find("typ srflx") != std::string::npos; + } + + bool IsRelay() const { + return candidate_.find("typ relay") != std::string::npos; + } + + private: + IceTestPeer *peer_; + size_t stream_; + std::string candidate_; + void *timer_handle_; + + DISALLOW_COPY_ASSIGN(SchedulableTrickleCandidate); +}; + class IceTestPeer : public sigslot::has_slots<> { public: @@ -364,23 +435,37 @@ class IceTestPeer : public sigslot::has_slots<> { void SimulateTrickle(size_t stream) { std::cerr << "Doing trickle for stream " << stream << std::endl; // If we are in trickle deferred mode, now trickle in the candidates - // for |stream} - nsresult res; + // for |stream| ASSERT_GT(remote_->streams_.size(), stream); + std::vector& candidates = + ControlTrickle(stream); + + for (auto i = candidates.begin(); i != candidates.end(); ++i) { + (*i)->Schedule(0); + } + } + + // Allows test case to completely control when/if candidates are trickled + // (test could also do things like insert extra trickle candidates, or + // change existing ones, or insert duplicates, really anything is fair game) + std::vector& ControlTrickle(size_t stream) { + std::cerr << "Doing controlled trickle for stream " << stream << std::endl; + std::vector candidates = remote_->GetCandidates(stream); for (size_t j=0; jsts_target()->Dispatch( - WrapRunnableRet(streams_[stream], - &NrIceMediaStream::ParseTrickleCandidate, - candidates[j], - &res), NS_DISPATCH_SYNC); - - ASSERT_TRUE(NS_SUCCEEDED(res)); + controlled_trickle_candidates_[stream].push_back( + new SchedulableTrickleCandidate(this, stream, candidates[j])); } + + return controlled_trickle_candidates_[stream]; + } + + nsresult TrickleCandidate_s(const std::string &candidate, size_t stream) { + return streams_[stream]->ParseTrickleCandidate(candidate); } void DumpCandidate(std::string which, const NrIceCandidate& cand) { @@ -457,6 +542,14 @@ class IceTestPeer : public sigslot::has_slots<> { } void Shutdown() { + for (auto s = controlled_trickle_candidates_.begin(); + s != controlled_trickle_candidates_.end(); + ++s) { + for (auto cand = s->second.begin(); cand != s->second.end(); ++cand) { + delete *cand; + } + } + ice_ctx_ = nullptr; } @@ -702,6 +795,9 @@ class IceTestPeer : public sigslot::has_slots<> { nsRefPtr ice_ctx_; std::vector > streams_; std::map > candidates_; + // Maps from stream id to list of remote trickle candidates + std::map > + controlled_trickle_candidates_; bool gathering_complete_; int ready_ct_; bool ice_complete_; @@ -718,6 +814,12 @@ class IceTestPeer : public sigslot::has_slots<> { int trickled_; }; +void SchedulableTrickleCandidate::Trickle() { + timer_handle_ = nullptr; + nsresult res = peer_->TrickleCandidate_s(candidate_, stream_); + ASSERT_TRUE(NS_SUCCEEDED(res)); +} + class IceGatherTest : public ::testing::Test { public: void SetUp() { @@ -1274,6 +1376,82 @@ TEST_F(IceConnectTest, TestConnectTurnWithDelay) { WaitForComplete(); } +void RealisticTrickleDelay( + std::vector& candidates) { + for (size_t i = 0; i < candidates.size(); ++i) { + SchedulableTrickleCandidate* cand = candidates[i]; + if (cand->IsHost()) { + cand->Schedule(i*10); + } else if (cand->IsReflexive()) { + cand->Schedule(i*10 + 100); + } else if (cand->IsRelay()) { + cand->Schedule(i*10 + 200); + } + } +} + +void DelayRelayCandidates( + std::vector& candidates, + unsigned int ms) { + for (auto i = candidates.begin(); i != candidates.end(); ++i) { + if ((*i)->IsRelay()) { + (*i)->Schedule(ms); + } else { + (*i)->Schedule(0); + } + } +} + +TEST_F(IceConnectTest, TestConnectTurnWithNormalTrickleDelay) { + if (g_turn_server.empty()) + return; + + AddStream("first", 1); + SetTurnServer(g_turn_server, kDefaultStunServerPort, + g_turn_user, g_turn_password); + ASSERT_TRUE(Gather(true)); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + + ASSERT_TRUE_WAIT(p1_->ice_complete(), 5000); + ASSERT_TRUE_WAIT(p2_->ice_complete(), 5000); +} + +TEST_F(IceConnectTest, TestConnectTurnWithNormalTrickleDelayOneSided) { + if (g_turn_server.empty()) + return; + + AddStream("first", 1); + SetTurnServer(g_turn_server, kDefaultStunServerPort, + g_turn_user, g_turn_password); + ASSERT_TRUE(Gather(true)); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + p2_->SimulateTrickle(0); + + ASSERT_TRUE_WAIT(p1_->ice_complete(), 5000); + ASSERT_TRUE_WAIT(p2_->ice_complete(), 5000); +} + +TEST_F(IceConnectTest, TestConnectTurnWithLargeTrickleDelay) { + if (g_turn_server.empty()) + return; + + AddStream("first", 1); + SetTurnServer(g_turn_server, kDefaultStunServerPort, + g_turn_user, g_turn_password); + SetCandidateFilter(SabotageHostCandidateAndDropReflexive); + ASSERT_TRUE(Gather(true)); + ConnectTrickle(); + // Trickle host candidates immediately, but delay relay candidates + DelayRelayCandidates(p1_->ControlTrickle(0), 3700); + DelayRelayCandidates(p2_->ControlTrickle(0), 3700); + + ASSERT_TRUE_WAIT(p1_->ice_complete(), 5000); + ASSERT_TRUE_WAIT(p2_->ice_complete(), 5000); +} + TEST_F(IceConnectTest, TestConnectTurnTcp) { if (g_turn_server.empty()) return;