From e5835f5d849f49eed7a3bf8d1455688a93149324 Mon Sep 17 00:00:00 2001 From: Taylor Brandstetter Date: Fri, 16 Sep 2016 15:07:50 -0700 Subject: [PATCH] Adding an end-to-end connection time test. The test uses a fake clock and simulates network and signaling delays in order to get a repeatable measurement of the time to establish a connection (including DTLS). This will help ensure that various optimizations continue to work as expected, and no new delays are introduced. This CL depends on: https://codereview.webrtc.org/2140283002/ R=honghaiz@webrtc.org, pthatcher@webrtc.org, skvlad@webrtc.org Review URL: https://codereview.webrtc.org/2141863003 . Cr-Commit-Position: refs/heads/master@{#14270} --- webrtc/api/peerconnection_unittest.cc | 345 ++++++++++++++++++++------ webrtc/base/fakenetwork.h | 5 +- webrtc/p2p/base/testturnserver.h | 15 +- 3 files changed, 286 insertions(+), 79 deletions(-) diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc index fb3c1887ba..981cec778a 100644 --- a/webrtc/api/peerconnection_unittest.cc +++ b/webrtc/api/peerconnection_unittest.cc @@ -30,6 +30,7 @@ #include "webrtc/api/test/fakertccertificategenerator.h" #include "webrtc/api/test/fakevideotrackrenderer.h" #include "webrtc/api/test/mockpeerconnectionobservers.h" +#include "webrtc/base/fakenetwork.h" #include "webrtc/base/gunit.h" #include "webrtc/base/helpers.h" #include "webrtc/base/physicalsocketserver.h" @@ -38,9 +39,10 @@ #include "webrtc/base/thread.h" #include "webrtc/base/virtualsocketserver.h" #include "webrtc/media/engine/fakewebrtcvideoengine.h" -#include "webrtc/p2p/base/fakeportallocator.h" #include "webrtc/p2p/base/p2pconstants.h" #include "webrtc/p2p/base/sessiondescription.h" +#include "webrtc/p2p/base/testturnserver.h" +#include "webrtc/p2p/client/basicportallocator.h" #include "webrtc/pc/mediasession.h" #define MAYBE_SKIP_TEST(feature) \ @@ -104,6 +106,20 @@ static const int kDefaultSrtpCryptoSuite = rtc::SRTP_AES128_CM_SHA1_32; static const int kDefaultSrtpCryptoSuiteGcm = rtc::SRTP_AEAD_AES_256_GCM; #endif +// Used to simulate signaling ICE/SDP between two PeerConnections. +enum Message { MSG_SDP_MESSAGE, MSG_ICE_MESSAGE }; + +struct SdpMessage { + std::string type; + std::string msg; +}; + +struct IceMessage { + std::string sdp_mid; + int sdp_mline_index; + std::string msg; +}; + static void RemoveLinesFromSdp(const std::string& line_start, std::string* sdp) { const char kSdpLineEnd[] = "\r\n"; @@ -169,13 +185,15 @@ class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface { class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, public SignalingMessageReceiver, - public ObserverInterface { + public ObserverInterface, + public rtc::MessageHandler { public: + // If |config| is not provided, uses a default constructed RTCConfiguration. static PeerConnectionTestClient* CreateClientWithDtlsIdentityStore( const std::string& id, const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, - const PeerConnectionInterface::RTCConfiguration& config, + const PeerConnectionInterface::RTCConfiguration* config, std::unique_ptr cert_generator, bool prefer_constraint_apis, rtc::Thread* network_thread, @@ -193,7 +211,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, const std::string& id, const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, - const PeerConnectionInterface::RTCConfiguration& config, + const PeerConnectionInterface::RTCConfiguration* config, rtc::Thread* network_thread, rtc::Thread* worker_thread) { std::unique_ptr cert_generator( @@ -208,14 +226,13 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, static PeerConnectionTestClient* CreateClientPreferNoConstraints( const std::string& id, const PeerConnectionFactory::Options* options, - const PeerConnectionInterface::RTCConfiguration& config, rtc::Thread* network_thread, rtc::Thread* worker_thread) { std::unique_ptr cert_generator( rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeRTCCertificateGenerator() : nullptr); - return CreateClientWithDtlsIdentityStore(id, nullptr, options, config, + return CreateClientWithDtlsIdentityStore(id, nullptr, options, nullptr, std::move(cert_generator), false, network_thread, worker_thread); } @@ -239,8 +256,64 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, std::string sdp; EXPECT_TRUE(offer->ToString(&sdp)); EXPECT_TRUE(DoSetLocalDescription(offer.release())); - signaling_message_receiver_->ReceiveSdpMessage( - webrtc::SessionDescriptionInterface::kOffer, sdp); + SendSdpMessage(webrtc::SessionDescriptionInterface::kOffer, sdp); + } + + void SendSdpMessage(const std::string& type, std::string& msg) { + if (signaling_delay_ms_ == 0) { + if (signaling_message_receiver_) { + signaling_message_receiver_->ReceiveSdpMessage(type, msg); + } + } else { + rtc::Thread::Current()->PostDelayed( + RTC_FROM_HERE, signaling_delay_ms_, this, MSG_SDP_MESSAGE, + new rtc::TypedMessageData({type, msg})); + } + } + + void SendIceMessage(const std::string& sdp_mid, + int sdp_mline_index, + const std::string& msg) { + if (signaling_delay_ms_ == 0) { + if (signaling_message_receiver_) { + signaling_message_receiver_->ReceiveIceMessage(sdp_mid, sdp_mline_index, + msg); + } + } else { + rtc::Thread::Current()->PostDelayed(RTC_FROM_HERE, signaling_delay_ms_, + this, MSG_ICE_MESSAGE, + new rtc::TypedMessageData( + {sdp_mid, sdp_mline_index, msg})); + } + } + + // MessageHandler callback. + void OnMessage(rtc::Message* msg) override { + switch (msg->message_id) { + case MSG_SDP_MESSAGE: { + auto sdp_message = + static_cast*>(msg->pdata); + if (signaling_message_receiver_) { + signaling_message_receiver_->ReceiveSdpMessage( + sdp_message->data().type, sdp_message->data().msg); + } + delete sdp_message; + break; + } + case MSG_ICE_MESSAGE: { + auto ice_message = + static_cast*>(msg->pdata); + if (signaling_message_receiver_) { + signaling_message_receiver_->ReceiveIceMessage( + ice_message->data().sdp_mid, ice_message->data().sdp_mline_index, + ice_message->data().msg); + } + delete ice_message; + break; + } + default: + RTC_CHECK(false); + } } // SignalingMessageReceiver callback. @@ -299,8 +372,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, // Remote party may be deleted. return; } - signaling_message_receiver_->ReceiveIceMessage( - candidate->sdp_mid(), candidate->sdp_mline_index(), ice_sdp); + SendIceMessage(candidate->sdp_mid(), candidate->sdp_mline_index(), ice_sdp); } // MediaStreamInterface callback @@ -375,6 +447,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, signaling_message_receiver_ = signaling_message_receiver; } + void set_signaling_delay_ms(int delay_ms) { signaling_delay_ms_ = delay_ms; } + void EnableVideoDecoderFactory() { video_decoder_factory_enabled_ = true; fake_video_decoder_factory_->AddSupportedVideoCodecType( @@ -402,6 +476,9 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, bool ExpectIceRenomination() { return expect_ice_renomination_; } bool ExpectRemoteIceRenomination() { return expect_remote_ice_renomination_; } + // The below 3 methods assume streams will be offered. + // Thus they'll only set the "offer to receive" flag to true if it's + // currently false, not if it's just unset. void SetReceiveAudioVideo(bool audio, bool video) { SetReceiveAudio(audio); SetReceiveVideo(video); @@ -410,15 +487,24 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, } void SetReceiveAudio(bool audio) { - if (audio && can_receive_audio()) + if (audio && can_receive_audio()) { return; + } offer_answer_constraints_.SetMandatoryReceiveAudio(audio); offer_answer_options_.offer_to_receive_audio = audio ? 1 : 0; } void SetReceiveVideo(bool video) { - if (video && can_receive_video()) + if (video && can_receive_video()) { return; + } + offer_answer_constraints_.SetMandatoryReceiveVideo(video); + offer_answer_options_.offer_to_receive_video = video ? 1 : 0; + } + + void SetOfferToReceiveAudioVideo(bool audio, bool video) { + offer_answer_constraints_.SetMandatoryReceiveAudio(audio); + offer_answer_options_.offer_to_receive_audio = audio ? 1 : 0; offer_answer_constraints_.SetMandatoryReceiveVideo(video); offer_answer_options_.offer_to_receive_video = video ? 1 : 0; } @@ -896,7 +982,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, bool Init( const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, - const PeerConnectionInterface::RTCConfiguration& config, + const PeerConnectionInterface::RTCConfiguration* config, std::unique_ptr cert_generator, bool prefer_constraint_apis, rtc::Thread* network_thread, @@ -908,8 +994,11 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, } prefer_constraint_apis_ = prefer_constraint_apis; + fake_network_manager_.reset(new rtc::FakeNetworkManager()); + fake_network_manager_->AddInterface(rtc::SocketAddress("192.168.1.1", 0)); + std::unique_ptr port_allocator( - new cricket::FakePortAllocator(network_thread, nullptr)); + new cricket::BasicPortAllocator(fake_network_manager_.get())); fake_audio_capture_module_ = FakeAudioCaptureModule::Create(); if (fake_audio_capture_module_ == nullptr) { @@ -931,17 +1020,23 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, peer_connection_ = CreatePeerConnection(std::move(port_allocator), constraints, config, std::move(cert_generator)); - return peer_connection_.get() != nullptr; } rtc::scoped_refptr CreatePeerConnection( std::unique_ptr port_allocator, const MediaConstraintsInterface* constraints, - const PeerConnectionInterface::RTCConfiguration& config, + const PeerConnectionInterface::RTCConfiguration* config, std::unique_ptr cert_generator) { + // CreatePeerConnection with RTCConfiguration. + PeerConnectionInterface::RTCConfiguration default_config; + + if (!config) { + config = &default_config; + } + return peer_connection_factory_->CreatePeerConnection( - config, constraints, std::move(port_allocator), + *config, constraints, std::move(port_allocator), std::move(cert_generator), this); } @@ -961,10 +1056,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, std::string sdp; EXPECT_TRUE(answer->ToString(&sdp)); EXPECT_TRUE(DoSetLocalDescription(answer.release())); - if (signaling_message_receiver_) { - signaling_message_receiver_->ReceiveSdpMessage( - webrtc::SessionDescriptionInterface::kAnswer, sdp); - } + SendSdpMessage(webrtc::SessionDescriptionInterface::kAnswer, sdp); } void HandleIncomingAnswer(const std::string& msg) { @@ -1065,6 +1157,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, std::string id_; + std::unique_ptr fake_network_manager_; + rtc::scoped_refptr peer_connection_; rtc::scoped_refptr peer_connection_factory_; @@ -1095,6 +1189,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, // For remote peer communication. SignalingMessageReceiver* signaling_message_receiver_ = nullptr; + int signaling_delay_ms_ = 0; // Store references to the video capturers we've created, so that we can stop // them, if required. @@ -1129,9 +1224,6 @@ class P2PTestConductor : public testing::Test { worker_thread_(rtc::Thread::Create()) { RTC_CHECK(network_thread_->Start()); RTC_CHECK(worker_thread_->Start()); - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.uri = "stun:stun.l.google.com:19302"; - config_.servers.push_back(ice_server); } bool SessionActive() { @@ -1234,19 +1326,24 @@ class P2PTestConductor : public testing::Test { bool CreateTestClients(MediaConstraintsInterface* init_constraints, MediaConstraintsInterface* recv_constraints) { - return CreateTestClients(init_constraints, nullptr, recv_constraints, - nullptr); + return CreateTestClients(init_constraints, nullptr, nullptr, + recv_constraints, nullptr, nullptr); + } + + bool CreateTestClients( + const PeerConnectionInterface::RTCConfiguration& init_config, + const PeerConnectionInterface::RTCConfiguration& recv_config) { + return CreateTestClients(nullptr, nullptr, &init_config, nullptr, nullptr, + &recv_config); } bool CreateTestClientsThatPreferNoConstraints() { initiating_client_.reset( PeerConnectionTestClient::CreateClientPreferNoConstraints( - "Caller: ", nullptr, config_, network_thread_.get(), - worker_thread_.get())); + "Caller: ", nullptr, network_thread_.get(), worker_thread_.get())); receiving_client_.reset( PeerConnectionTestClient::CreateClientPreferNoConstraints( - "Callee: ", nullptr, config_, network_thread_.get(), - worker_thread_.get())); + "Callee: ", nullptr, network_thread_.get(), worker_thread_.get())); if (!initiating_client_ || !receiving_client_) { return false; } @@ -1256,20 +1353,18 @@ class P2PTestConductor : public testing::Test { return true; } - void SetSignalingReceivers() { - initiating_client_->set_signaling_message_receiver(receiving_client_.get()); - receiving_client_->set_signaling_message_receiver(initiating_client_.get()); - } - - bool CreateTestClients(MediaConstraintsInterface* init_constraints, - PeerConnectionFactory::Options* init_options, - MediaConstraintsInterface* recv_constraints, - PeerConnectionFactory::Options* recv_options) { + bool CreateTestClients( + MediaConstraintsInterface* init_constraints, + PeerConnectionFactory::Options* init_options, + const PeerConnectionInterface::RTCConfiguration* init_config, + MediaConstraintsInterface* recv_constraints, + PeerConnectionFactory::Options* recv_options, + const PeerConnectionInterface::RTCConfiguration* recv_config) { initiating_client_.reset(PeerConnectionTestClient::CreateClient( - "Caller: ", init_constraints, init_options, config_, + "Caller: ", init_constraints, init_options, init_config, network_thread_.get(), worker_thread_.get())); receiving_client_.reset(PeerConnectionTestClient::CreateClient( - "Callee: ", recv_constraints, recv_options, config_, + "Callee: ", recv_constraints, recv_options, recv_config, network_thread_.get(), worker_thread_.get())); if (!initiating_client_ || !receiving_client_) { return false; @@ -1278,6 +1373,16 @@ class P2PTestConductor : public testing::Test { return true; } + void SetSignalingReceivers() { + initiating_client_->set_signaling_message_receiver(receiving_client_.get()); + receiving_client_->set_signaling_message_receiver(initiating_client_.get()); + } + + void SetSignalingDelayMs(int delay_ms) { + initiating_client_->set_signaling_delay_ms(delay_ms); + receiving_client_->set_signaling_delay_ms(delay_ms); + } + void SetVideoConstraints(const webrtc::FakeConstraints& init_constraints, const webrtc::FakeConstraints& recv_constraints) { initiating_client_->SetVideoConstraints(init_constraints); @@ -1370,7 +1475,7 @@ class P2PTestConductor : public testing::Test { // Make sure the new client is using a different certificate. return PeerConnectionTestClient::CreateClientWithDtlsIdentityStore( - "New Peer: ", &setup_constraints, nullptr, config_, + "New Peer: ", &setup_constraints, nullptr, nullptr, std::move(cert_generator), prefer_constraint_apis_, network_thread_.get(), worker_thread_.get()); } @@ -1385,6 +1490,8 @@ class P2PTestConductor : public testing::Test { } } + rtc::Thread* network_thread() { return network_thread_.get(); } + rtc::VirtualSocketServer* virtual_socket_server() { return ss_.get(); } PeerConnectionTestClient* initializing_client() { @@ -1412,9 +1519,6 @@ class P2PTestConductor : public testing::Test { receiving_client_.reset(client); return old; } - webrtc::PeerConnectionInterface::RTCConfiguration* config() { - return &config_; - } bool AllObserversReceived( const std::vector>& observers) { @@ -1432,8 +1536,8 @@ class P2PTestConductor : public testing::Test { init_options.crypto_options.enable_gcm_crypto_suites = local_gcm_enabled; PeerConnectionFactory::Options recv_options; recv_options.crypto_options.enable_gcm_crypto_suites = remote_gcm_enabled; - ASSERT_TRUE( - CreateTestClients(nullptr, &init_options, nullptr, &recv_options)); + ASSERT_TRUE(CreateTestClients(nullptr, &init_options, nullptr, nullptr, + &recv_options, nullptr)); rtc::scoped_refptr init_observer = new rtc::RefCountedObject(); @@ -1460,7 +1564,6 @@ class P2PTestConductor : public testing::Test { std::unique_ptr initiating_client_; std::unique_ptr receiving_client_; bool prefer_constraint_apis_ = true; - webrtc::PeerConnectionInterface::RTCConfiguration config_; }; // Disable for TSan v2, see @@ -1803,8 +1906,8 @@ TEST_F(P2PTestConductor, GetDtls12None) { init_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; - ASSERT_TRUE( - CreateTestClients(nullptr, &init_options, nullptr, &recv_options)); + ASSERT_TRUE(CreateTestClients(nullptr, &init_options, nullptr, nullptr, + &recv_options, nullptr)); rtc::scoped_refptr init_observer = new rtc::RefCountedObject(); initializing_client()->pc()->RegisterUMAObserver(init_observer); @@ -1828,8 +1931,8 @@ TEST_F(P2PTestConductor, GetDtls12Both) { init_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; - ASSERT_TRUE( - CreateTestClients(nullptr, &init_options, nullptr, &recv_options)); + ASSERT_TRUE(CreateTestClients(nullptr, &init_options, nullptr, nullptr, + &recv_options, nullptr)); rtc::scoped_refptr init_observer = new rtc::RefCountedObject(); initializing_client()->pc()->RegisterUMAObserver(init_observer); @@ -1854,8 +1957,8 @@ TEST_F(P2PTestConductor, GetDtls12Init) { init_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; - ASSERT_TRUE( - CreateTestClients(nullptr, &init_options, nullptr, &recv_options)); + ASSERT_TRUE(CreateTestClients(nullptr, &init_options, nullptr, nullptr, + &recv_options, nullptr)); rtc::scoped_refptr init_observer = new rtc::RefCountedObject(); initializing_client()->pc()->RegisterUMAObserver(init_observer); @@ -1880,8 +1983,8 @@ TEST_F(P2PTestConductor, GetDtls12Recv) { init_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; PeerConnectionFactory::Options recv_options; recv_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; - ASSERT_TRUE( - CreateTestClients(nullptr, &init_options, nullptr, &recv_options)); + ASSERT_TRUE(CreateTestClients(nullptr, &init_options, nullptr, nullptr, + &recv_options, nullptr)); rtc::scoped_refptr init_observer = new rtc::RefCountedObject(); initializing_client()->pc()->RegisterUMAObserver(init_observer); @@ -2176,8 +2279,9 @@ TEST_F(P2PTestConductor, IceRestart) { } TEST_F(P2PTestConductor, IceRenominationDisabled) { - config()->enable_ice_renomination = false; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration config; + config.enable_ice_renomination = false; + ASSERT_TRUE(CreateTestClients(config, config)); LocalP2PTest(); initializing_client()->VerifyLocalIceRenomination(); @@ -2187,8 +2291,9 @@ TEST_F(P2PTestConductor, IceRenominationDisabled) { } TEST_F(P2PTestConductor, IceRenominationEnabled) { - config()->enable_ice_renomination = true; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration config; + config.enable_ice_renomination = true; + ASSERT_TRUE(CreateTestClients(config, config)); initializing_client()->SetExpectIceRenomination(true); initializing_client()->SetExpectRemoteIceRenomination(true); receiving_client()->SetExpectIceRenomination(true); @@ -2268,8 +2373,9 @@ TEST_F(P2PTestConductor, EarlyWarmupTest) { // This test sets up a call between two parties using QUIC instead of DTLS for // audio and video, and a QUIC data channel. TEST_F(P2PTestConductor, LocalP2PTestQuicDataChannel) { - config()->enable_quic = true; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration quic_config; + quic_config.enable_quic = true; + ASSERT_TRUE(CreateTestClients(quic_config, quic_config)); webrtc::DataChannelInit init; init.ordered = false; init.reliable = true; @@ -2296,7 +2402,9 @@ TEST_F(P2PTestConductor, LocalP2PTestQuicDataChannel) { // Tests that negotiation of QUIC data channels is completed without error. TEST_F(P2PTestConductor, NegotiateQuicDataChannel) { - config()->enable_quic = true; + PeerConnectionInterface::RTCConfiguration quic_config; + quic_config.enable_quic = true; + ASSERT_TRUE(CreateTestClients(quic_config, quic_config)); FakeConstraints constraints; constraints.SetMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true); ASSERT_TRUE(CreateTestClients(&constraints, &constraints)); @@ -2310,16 +2418,18 @@ TEST_F(P2PTestConductor, NegotiateQuicDataChannel) { // This test sets up a JSEP call using QUIC. The callee only receives video. TEST_F(P2PTestConductor, LocalP2PTestVideoOnlyWithQuic) { - config()->enable_quic = true; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration quic_config; + quic_config.enable_quic = true; + ASSERT_TRUE(CreateTestClients(quic_config, quic_config)); receiving_client()->SetReceiveAudioVideo(false, true); LocalP2PTest(); } // This test sets up a JSEP call using QUIC. The callee only receives audio. TEST_F(P2PTestConductor, LocalP2PTestAudioOnlyWithQuic) { - config()->enable_quic = true; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration quic_config; + quic_config.enable_quic = true; + ASSERT_TRUE(CreateTestClients(quic_config, quic_config)); receiving_client()->SetReceiveAudioVideo(true, false); LocalP2PTest(); } @@ -2327,8 +2437,9 @@ TEST_F(P2PTestConductor, LocalP2PTestAudioOnlyWithQuic) { // This test sets up a JSEP call using QUIC. The callee rejects both audio and // video. TEST_F(P2PTestConductor, LocalP2PTestNoVideoAudioWithQuic) { - config()->enable_quic = true; - ASSERT_TRUE(CreateTestClients()); + PeerConnectionInterface::RTCConfiguration quic_config; + quic_config.enable_quic = true; + ASSERT_TRUE(CreateTestClients(quic_config, quic_config)); receiving_client()->SetReceiveAudioVideo(false, false); LocalP2PTest(); } @@ -2368,6 +2479,102 @@ TEST_F(P2PTestConductor, ForwardVideoOnlyStream) { kMaxWaitForFramesMs); } +// Test that we achieve the expected end-to-end connection time, using a +// fake clock and simulated latency on the media and signaling paths. +// We use a TURN<->TURN connection because this is usually the quickest to +// set up initially, especially when we're confident the connection will work +// and can start sending media before we get a STUN response. +// +// With various optimizations enabled, here are the network delays we expect to +// be on the critical path: +// 1. 2 signaling trips: Signaling offer and offerer's TURN candidate, then +// signaling answer (with DTLS fingerprint). +// 2. 9 media hops: Rest of the DTLS handshake. 3 hops in each direction when +// using TURN<->TURN pair, and DTLS exchange is 4 packets, +// the first of which should have arrived before the answer. +TEST_F(P2PTestConductor, EndToEndConnectionTimeWithTurnTurnPair) { + rtc::ScopedFakeClock fake_clock; + // Some things use a time of "0" as a special value, so we need to start out + // the fake clock at a nonzero time. + // TODO(deadbeef): Fix this. + fake_clock.AdvanceTime(rtc::TimeDelta::FromSeconds(1)); + + static constexpr int media_hop_delay_ms = 50; + static constexpr int signaling_trip_delay_ms = 500; + // For explanation of these values, see comment above. + static constexpr int required_media_hops = 9; + static constexpr int required_signaling_trips = 2; + // For internal delays (such as posting an event asychronously). + static constexpr int allowed_internal_delay_ms = 20; + static constexpr int total_connection_time_ms = + media_hop_delay_ms * required_media_hops + + signaling_trip_delay_ms * required_signaling_trips + + allowed_internal_delay_ms; + + static const rtc::SocketAddress turn_server_1_internal_address{"88.88.88.0", + 3478}; + static const rtc::SocketAddress turn_server_1_external_address{"88.88.88.1", + 0}; + static const rtc::SocketAddress turn_server_2_internal_address{"99.99.99.0", + 3478}; + static const rtc::SocketAddress turn_server_2_external_address{"99.99.99.1", + 0}; + cricket::TestTurnServer turn_server_1(network_thread(), + turn_server_1_internal_address, + turn_server_1_external_address); + cricket::TestTurnServer turn_server_2(network_thread(), + turn_server_2_internal_address, + turn_server_2_external_address); + // Bypass permission check on received packets so media can be sent before + // the candidate is signaled. + turn_server_1.set_enable_permission_checks(false); + turn_server_2.set_enable_permission_checks(false); + + PeerConnectionInterface::RTCConfiguration client_1_config; + webrtc::PeerConnectionInterface::IceServer ice_server_1; + ice_server_1.urls.push_back("turn:88.88.88.0:3478"); + ice_server_1.username = "test"; + ice_server_1.password = "test"; + client_1_config.servers.push_back(ice_server_1); + client_1_config.type = webrtc::PeerConnectionInterface::kRelay; + client_1_config.presume_writable_when_fully_relayed = true; + + PeerConnectionInterface::RTCConfiguration client_2_config; + webrtc::PeerConnectionInterface::IceServer ice_server_2; + ice_server_2.urls.push_back("turn:99.99.99.0:3478"); + ice_server_2.username = "test"; + ice_server_2.password = "test"; + client_2_config.servers.push_back(ice_server_2); + client_2_config.type = webrtc::PeerConnectionInterface::kRelay; + client_2_config.presume_writable_when_fully_relayed = true; + + ASSERT_TRUE(CreateTestClients(client_1_config, client_2_config)); + // Set up the simulated delays. + SetSignalingDelayMs(signaling_trip_delay_ms); + virtual_socket_server()->set_delay_mean(media_hop_delay_ms); + virtual_socket_server()->UpdateDelayDistribution(); + + initializing_client()->SetOfferToReceiveAudioVideo(true, true); + initializing_client()->Negotiate(); + // TODO(deadbeef): kIceConnectionConnected currently means both ICE and DTLS + // are connected. This is an important distinction. Once we have separate ICE + // and DTLS state, this check needs to use the DTLS state. + EXPECT_TRUE_SIMULATED_WAIT( + (receiving_client()->ice_connection_state() == + webrtc::PeerConnectionInterface::kIceConnectionConnected || + receiving_client()->ice_connection_state() == + webrtc::PeerConnectionInterface::kIceConnectionCompleted) && + (initializing_client()->ice_connection_state() == + webrtc::PeerConnectionInterface::kIceConnectionConnected || + initializing_client()->ice_connection_state() == + webrtc::PeerConnectionInterface::kIceConnectionCompleted), + total_connection_time_ms, fake_clock); + // Need to free the clients here since they're using things we created on + // the stack. + delete set_initializing_client(nullptr); + delete set_receiving_client(nullptr); +} + class IceServerParsingTest : public testing::Test { public: // Convenience for parsing a single URL. diff --git a/webrtc/base/fakenetwork.h b/webrtc/base/fakenetwork.h index 1912dd0242..108e738468 100644 --- a/webrtc/base/fakenetwork.h +++ b/webrtc/base/fakenetwork.h @@ -31,7 +31,7 @@ const int kFakeIPv6NetworkPrefixLength = 64; class FakeNetworkManager : public NetworkManagerBase, public MessageHandler { public: - FakeNetworkManager() : thread_(Thread::Current()) {} + FakeNetworkManager() {} typedef std::vector> IfaceList; @@ -68,7 +68,7 @@ class FakeNetworkManager : public NetworkManagerBase, ++start_count_; if (start_count_ == 1) { sent_first_update_ = false; - thread_->Post(RTC_FROM_HERE, this); + rtc::Thread::Current()->Post(RTC_FROM_HERE, this); } else { if (sent_first_update_) { SignalNetworksChanged(); @@ -115,7 +115,6 @@ class FakeNetworkManager : public NetworkManagerBase, } } - Thread* thread_; IfaceList ifaces_; int next_index_ = 0; int start_count_ = 0; diff --git a/webrtc/p2p/base/testturnserver.h b/webrtc/p2p/base/testturnserver.h index 43d363cb35..9cd8203762 100644 --- a/webrtc/p2p/base/testturnserver.h +++ b/webrtc/p2p/base/testturnserver.h @@ -52,10 +52,10 @@ class TestTurnServer : public TurnAuthInterface { const rtc::SocketAddress& int_addr, const rtc::SocketAddress& udp_ext_addr, ProtocolType int_protocol = PROTO_UDP) - : server_(thread) { + : server_(thread), thread_(thread) { AddInternalSocket(int_addr, int_protocol); - server_.SetExternalSocketFactory(new rtc::BasicPacketSocketFactory(), - udp_ext_addr); + server_.SetExternalSocketFactory(new rtc::BasicPacketSocketFactory(thread), + udp_ext_addr); server_.set_realm(kTestRealm); server_.set_software(kTestSoftware); server_.set_auth_hook(this); @@ -77,15 +77,15 @@ class TestTurnServer : public TurnAuthInterface { void AddInternalSocket(const rtc::SocketAddress& int_addr, ProtocolType proto) { - rtc::Thread* thread = rtc::Thread::Current(); if (proto == cricket::PROTO_UDP) { - server_.AddInternalSocket(rtc::AsyncUDPSocket::Create( - thread->socketserver(), int_addr), proto); + server_.AddInternalSocket( + rtc::AsyncUDPSocket::Create(thread_->socketserver(), int_addr), + proto); } else if (proto == cricket::PROTO_TCP) { // For TCP we need to create a server socket which can listen for incoming // new connections. rtc::AsyncSocket* socket = - thread->socketserver()->CreateAsyncSocket(SOCK_STREAM); + thread_->socketserver()->CreateAsyncSocket(SOCK_STREAM); socket->Bind(int_addr); socket->Listen(5); server_.AddInternalServerSocket(socket, proto); @@ -114,6 +114,7 @@ class TestTurnServer : public TurnAuthInterface { } TurnServer server_; + rtc::Thread* thread_; }; } // namespace cricket