test_nr_socket_ice_unittest.cpp (12713B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Some of this code is taken from nricectx.cpp and nricemediastream.cpp 8 // which in turn contains code cut-and-pasted from nICEr. Copyright is: 9 10 /* 11 Copyright (c) 2007, Adobe Systems, Incorporated 12 All rights reserved. 13 14 Redistribution and use in source and binary forms, with or without 15 modification, are permitted provided that the following conditions are 16 met: 17 18 * Redistributions of source code must retain the above copyright 19 notice, this list of conditions and the following disclaimer. 20 21 * Redistributions in binary form must reproduce the above copyright 22 notice, this list of conditions and the following disclaimer in the 23 documentation and/or other materials provided with the distribution. 24 25 * Neither the name of Adobe Systems, Network Resonance nor the names of its 26 contributors may be used to endorse or promote products derived from 27 this software without specific prior written permission. 28 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 #include "gtest/gtest.h" 43 #include "gtest_utils.h" 44 45 extern "C" { 46 #include "ice_ctx.h" 47 #include "ice_peer_ctx.h" 48 #include "nICEr/src/net/transport_addr.h" 49 } 50 51 #include "mtransport_test_utils.h" 52 #include "nricectx.h" 53 #include "nricemediastream.h" 54 #include "runnable_utils.h" 55 #include "test_nr_socket.h" 56 57 namespace mozilla { 58 59 static unsigned int kDefaultTimeout = 7000; 60 61 class IcePeer { 62 public: 63 IcePeer(const char* name, TestNat* nat, UINT4 flags, 64 MtransportTestUtils* test_utils) 65 : name_(name), 66 ice_checking_(false), 67 ice_connected_(false), 68 ice_disconnected_(false), 69 gather_cb_(false), 70 stream_ready_(false), 71 stream_failed_(false), 72 ice_ctx_(nullptr), 73 peer_ctx_(nullptr), 74 nat_(nat), 75 test_utils_(test_utils) { 76 nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, nullptr, 77 &ice_ctx_); 78 79 if (nat_) { 80 nr_socket_factory* factory; 81 nat_->create_socket_factory(&factory); 82 nr_ice_ctx_set_socket_factory(ice_ctx_, factory); 83 } 84 85 // Create the handler objects 86 ice_handler_vtbl_ = new nr_ice_handler_vtbl(); 87 ice_handler_vtbl_->select_pair = &IcePeer::select_pair; 88 ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready; 89 ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed; 90 ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected; 91 ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd; 92 ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking; 93 ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected; 94 95 ice_handler_ = new nr_ice_handler(); 96 ice_handler_->vtbl = ice_handler_vtbl_; 97 ice_handler_->obj = this; 98 99 nr_ice_peer_ctx_create(ice_ctx_, ice_handler_, 100 const_cast<char*>(name_.c_str()), &peer_ctx_); 101 102 nr_ice_add_media_stream(ice_ctx_, const_cast<char*>(name_.c_str()), "ufrag", 103 "pass", 2, &ice_media_stream_); 104 EXPECT_EQ(2UL, GetStreamAttributes().size()); 105 106 nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_); 107 } 108 109 virtual ~IcePeer() { Destroy(); } 110 111 void Destroy() { 112 test_utils_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::Destroy_s)); 113 } 114 115 void Destroy_s() { 116 nr_ice_peer_ctx_destroy(&peer_ctx_); 117 delete ice_handler_; 118 delete ice_handler_vtbl_; 119 nr_ice_ctx_destroy(&ice_ctx_); 120 } 121 122 void Gather(bool default_route_only = false) { 123 test_utils_->SyncDispatchToSTS( 124 WrapRunnable(this, &IcePeer::Gather_s, default_route_only)); 125 } 126 127 void Gather_s(bool default_route_only = false) { 128 int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this); 129 ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK); 130 } 131 132 std::vector<std::string> GetStreamAttributes() { 133 std::vector<std::string> attributes; 134 test_utils_->SyncDispatchToSTS( 135 WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s)); 136 return attributes; 137 } 138 139 std::vector<std::string> GetStreamAttributes_s() { 140 char** attrs = nullptr; 141 int attrct; 142 std::vector<std::string> ret; 143 144 int r = 145 nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct); 146 EXPECT_EQ(0, r); 147 148 for (int i = 0; i < attrct; i++) { 149 ret.push_back(std::string(attrs[i])); 150 RFREE(attrs[i]); 151 } 152 RFREE(attrs); 153 154 return ret; 155 } 156 157 std::vector<std::string> GetGlobalAttributes() { 158 std::vector<std::string> attributes; 159 test_utils_->SyncDispatchToSTS( 160 WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s)); 161 return attributes; 162 } 163 164 std::vector<std::string> GetGlobalAttributes_s() { 165 char** attrs = nullptr; 166 int attrct; 167 std::vector<std::string> ret; 168 169 nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct); 170 171 for (int i = 0; i < attrct; i++) { 172 ret.push_back(std::string(attrs[i])); 173 RFREE(attrs[i]); 174 } 175 RFREE(attrs); 176 177 return ret; 178 } 179 180 void ParseGlobalAttributes(std::vector<std::string> attrs) { 181 std::vector<char*> attrs_in; 182 attrs_in.reserve(attrs.size()); 183 for (auto& attr : attrs) { 184 attrs_in.push_back(const_cast<char*>(attr.c_str())); 185 } 186 187 int r = nr_ice_peer_ctx_parse_global_attributes( 188 peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size()); 189 ASSERT_EQ(0, r); 190 } 191 192 void SetControlling(bool controlling) { 193 peer_ctx_->controlling = controlling ? 1 : 0; 194 } 195 196 void SetRemoteAttributes(std::vector<std::string> attributes) { 197 test_utils_->SyncDispatchToSTS( 198 WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes)); 199 } 200 201 void SetRemoteAttributes_s(std::vector<std::string> attributes) { 202 int r; 203 204 std::vector<char*> attrs; 205 attrs.reserve(attributes.size()); 206 for (auto& attr : attributes) { 207 attrs.push_back(const_cast<char*>(attr.c_str())); 208 } 209 210 if (!attrs.empty()) { 211 r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_, 212 &attrs[0], attrs.size()); 213 ASSERT_EQ(0, r); 214 } 215 } 216 217 void StartChecks() { 218 test_utils_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::StartChecks_s)); 219 } 220 221 void StartChecks_s() { 222 int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_); 223 ASSERT_EQ(0, r); 224 225 r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1); 226 ASSERT_EQ(0, r); 227 } 228 229 // Handler callbacks 230 static int select_pair(void* obj, nr_ice_media_stream* stream, 231 int component_id, nr_ice_cand_pair** potentials, 232 int potential_ct) { 233 return 0; 234 } 235 236 static int stream_ready(void* obj, nr_ice_media_stream* stream) { 237 IcePeer* peer = static_cast<IcePeer*>(obj); 238 peer->stream_ready_ = true; 239 return 0; 240 } 241 242 static int stream_failed(void* obj, nr_ice_media_stream* stream) { 243 IcePeer* peer = static_cast<IcePeer*>(obj); 244 peer->stream_failed_ = true; 245 return 0; 246 } 247 248 static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) { 249 IcePeer* peer = static_cast<IcePeer*>(obj); 250 peer->ice_checking_ = true; 251 return 0; 252 } 253 254 static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) { 255 IcePeer* peer = static_cast<IcePeer*>(obj); 256 peer->ice_connected_ = true; 257 return 0; 258 } 259 260 static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { 261 IcePeer* peer = static_cast<IcePeer*>(obj); 262 peer->ice_disconnected_ = true; 263 return 0; 264 } 265 266 static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx, 267 nr_ice_media_stream* stream, int component_id, 268 UCHAR* msg, int len) { 269 return 0; 270 } 271 272 static void gather_cb(NR_SOCKET s, int h, void* arg) { 273 IcePeer* peer = static_cast<IcePeer*>(arg); 274 peer->gather_cb_ = true; 275 } 276 277 std::string name_; 278 279 bool ice_checking_; 280 bool ice_connected_; 281 bool ice_disconnected_; 282 bool gather_cb_; 283 bool stream_ready_; 284 bool stream_failed_; 285 286 nr_ice_ctx* ice_ctx_; 287 nr_ice_handler* ice_handler_; 288 nr_ice_handler_vtbl* ice_handler_vtbl_; 289 nr_ice_media_stream* ice_media_stream_; 290 nr_ice_peer_ctx* peer_ctx_; 291 TestNat* nat_; 292 MtransportTestUtils* test_utils_; 293 }; 294 295 class TestNrSocketIceUnitTest : public ::testing::Test { 296 public: 297 void SetUp() override { 298 NSS_NoDB_Init(nullptr); 299 NSS_SetDomesticPolicy(); 300 301 test_utils_ = new MtransportTestUtils(); 302 test_utils2_ = new MtransportTestUtils(); 303 304 test_utils_->SyncDispatchToSTS( 305 WrapRunnable(this, &TestNrSocketIceUnitTest::SetUp_s)); 306 } 307 308 void SetUp_s() { NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); } 309 310 void TearDown() override { 311 delete test_utils_; 312 delete test_utils2_; 313 } 314 315 MtransportTestUtils* test_utils_; 316 MtransportTestUtils* test_utils2_; 317 }; 318 319 TEST_F(TestNrSocketIceUnitTest, TestIcePeer) { 320 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, 321 test_utils_); 322 ASSERT_NE(peer.ice_ctx_, nullptr); 323 ASSERT_NE(peer.peer_ctx_, nullptr); 324 ASSERT_NE(peer.ice_media_stream_, nullptr); 325 ASSERT_EQ(2UL, peer.GetStreamAttributes().size()) 326 << "Should have ice-ufrag and ice-pwd"; 327 peer.Gather(); 328 ASSERT_LT(2UL, peer.GetStreamAttributes().size()) 329 << "Should have ice-ufrag, ice-pwd, and at least one candidate."; 330 } 331 332 TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) { 333 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, 334 test_utils_); 335 IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, 336 test_utils2_); 337 peer.SetControlling(true); 338 peer2.SetControlling(false); 339 340 peer.Gather(); 341 peer2.Gather(); 342 std::vector<std::string> attrs = peer.GetGlobalAttributes(); 343 peer2.ParseGlobalAttributes(attrs); 344 std::vector<std::string> attributes = peer.GetStreamAttributes(); 345 peer2.SetRemoteAttributes(attributes); 346 347 attrs = peer2.GetGlobalAttributes(); 348 peer.ParseGlobalAttributes(attrs); 349 attributes = peer2.GetStreamAttributes(); 350 peer.SetRemoteAttributes(attributes); 351 peer2.StartChecks(); 352 peer.StartChecks(); 353 354 ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout); 355 ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout); 356 } 357 358 TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) { 359 IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, 360 test_utils_); 361 362 RefPtr<TestNat> nat(new TestNat); 363 class NatDelegate : public TestNat::NatDelegate { 364 public: 365 NatDelegate() : messages(0) {} 366 367 int on_read(TestNat* nat, void* buf, size_t maxlen, size_t* len) override { 368 return 0; 369 } 370 371 int on_sendto(TestNat* nat, const void* msg, size_t len, int flags, 372 const nr_transport_addr* to) override { 373 ++messages; 374 // 25% packet loss 375 if (messages % 4 == 0) { 376 return 1; 377 } 378 return 0; 379 } 380 381 int on_write(TestNat* nat, const void* msg, size_t len, 382 size_t* written) override { 383 return 0; 384 } 385 386 int messages; 387 } delegate; 388 nat->nat_delegate_ = &delegate; 389 390 IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, 391 test_utils2_); 392 peer.SetControlling(true); 393 peer2.SetControlling(false); 394 395 peer.Gather(); 396 peer2.Gather(); 397 std::vector<std::string> attrs = peer.GetGlobalAttributes(); 398 peer2.ParseGlobalAttributes(attrs); 399 std::vector<std::string> attributes = peer.GetStreamAttributes(); 400 peer2.SetRemoteAttributes(attributes); 401 402 attrs = peer2.GetGlobalAttributes(); 403 peer.ParseGlobalAttributes(attrs); 404 attributes = peer2.GetStreamAttributes(); 405 peer.SetRemoteAttributes(attributes); 406 peer2.StartChecks(); 407 peer.StartChecks(); 408 409 ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout); 410 ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout); 411 } 412 413 } // namespace mozilla