From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../transport/test/test_nr_socket_ice_unittest.cpp | 409 +++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp (limited to 'dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp') diff --git a/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp new file mode 100644 index 0000000000..b55b05f10c --- /dev/null +++ b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp @@ -0,0 +1,409 @@ +/* -*- 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" + +extern "C" { +#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(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(name_.c_str()), &peer_ctx_); + + nr_ice_add_media_stream(ice_ctx_, const_cast(name_.c_str()), "ufrag", + "pass", 2, &ice_media_stream_); + EXPECT_EQ(2UL, GetStreamAttributes().size()); + + nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_); + } + + virtual ~IcePeer() { Destroy(); } + + void Destroy() { + test_utils_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::Destroy_s)); + } + + 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_->SyncDispatchToSTS( + WrapRunnable(this, &IcePeer::Gather_s, default_route_only)); + } + + 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 GetStreamAttributes() { + std::vector attributes; + test_utils_->SyncDispatchToSTS( + WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s)); + return attributes; + } + + std::vector GetStreamAttributes_s() { + char** attrs = nullptr; + int attrct; + std::vector ret; + + int r = + nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct); + EXPECT_EQ(0, r); + + for (int i = 0; i < attrct; i++) { + ret.push_back(std::string(attrs[i])); + RFREE(attrs[i]); + } + RFREE(attrs); + + return ret; + } + + std::vector GetGlobalAttributes() { + std::vector attributes; + test_utils_->SyncDispatchToSTS( + WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s)); + return attributes; + } + + std::vector GetGlobalAttributes_s() { + char** attrs = nullptr; + int attrct; + std::vector 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 attrs) { + std::vector attrs_in; + attrs_in.reserve(attrs.size()); + for (auto& attr : attrs) { + attrs_in.push_back(const_cast(attr.c_str())); + } + + int r = nr_ice_peer_ctx_parse_global_attributes( + peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size()); + ASSERT_EQ(0, r); + } + + void SetControlling(bool controlling) { + peer_ctx_->controlling = controlling ? 1 : 0; + } + + void SetRemoteAttributes(std::vector attributes) { + test_utils_->SyncDispatchToSTS( + WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes)); + } + + void SetRemoteAttributes_s(std::vector attributes) { + int r; + + std::vector attrs; + attrs.reserve(attributes.size()); + for (auto& attr : attributes) { + attrs.push_back(const_cast(attr.c_str())); + } + + if (!attrs.empty()) { + 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_->SyncDispatchToSTS(WrapRunnable(this, &IcePeer::StartChecks_s)); + } + + 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(obj); + peer->stream_ready_ = true; + return 0; + } + + static int stream_failed(void* obj, nr_ice_media_stream* stream) { + IcePeer* peer = static_cast(obj); + peer->stream_failed_ = true; + return 0; + } + + static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast(obj); + peer->ice_checking_ = true; + return 0; + } + + static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast(obj); + peer->ice_connected_ = true; + return 0; + } + + static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast(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(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(NrIceCtx::GlobalConfig()); + } + + 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); + ASSERT_EQ(2UL, peer.GetStreamAttributes().size()) + << "Should have ice-ufrag and ice-pwd"; + peer.Gather(); + ASSERT_LT(2UL, peer.GetStreamAttributes().size()) + << "Should have ice-ufrag, ice-pwd, and at least one candidate."; +} + +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 attrs = peer.GetGlobalAttributes(); + peer2.ParseGlobalAttributes(attrs); + std::vector attributes = peer.GetStreamAttributes(); + peer2.SetRemoteAttributes(attributes); + + attrs = peer2.GetGlobalAttributes(); + peer.ParseGlobalAttributes(attrs); + attributes = peer2.GetStreamAttributes(); + peer.SetRemoteAttributes(attributes); + 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 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, + const 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 attrs = peer.GetGlobalAttributes(); + peer2.ParseGlobalAttributes(attrs); + std::vector attributes = peer.GetStreamAttributes(); + peer2.SetRemoteAttributes(attributes); + + attrs = peer2.GetGlobalAttributes(); + peer.ParseGlobalAttributes(attrs); + attributes = peer2.GetStreamAttributes(); + peer.SetRemoteAttributes(attributes); + peer2.StartChecks(); + peer.StartChecks(); + + ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout); + ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout); +} + +} // namespace mozilla -- cgit v1.2.3