 Chromium Code Reviews
 Chromium Code Reviews Issue 2141863003:
  Adding an end-to-end connection time test.  (Closed) 
  Base URL: https://chromium.googlesource.com/external/webrtc.git@master
    
  
    Issue 2141863003:
  Adding an end-to-end connection time test.  (Closed) 
  Base URL: https://chromium.googlesource.com/external/webrtc.git@master| Index: webrtc/api/peerconnection_unittest.cc | 
| diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc | 
| index 4b06b90d01c6c1c9dd5ed7e9aaf181c9f1fddfb2..b9eb7e4db3f95d3892c30642292524833c8539fb 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/physicalsocketserver.h" | 
| #include "webrtc/base/ssladapter.h" | 
| @@ -37,9 +38,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) \ | 
| @@ -102,6 +104,9 @@ static const char kDataChannelLabel[] = "data_channel"; | 
| static const int kDefaultSrtpCryptoSuite = rtc::SRTP_AES128_CM_SHA1_32; | 
| #endif | 
| +// Used to simulate signaling ICE/SDP between two PeerConnections. | 
| +enum { MSG_SDP_MESSAGE, MSG_ICE_MESSAGE }; | 
| + | 
| static void RemoveLinesFromSdp(const std::string& line_start, | 
| std::string* sdp) { | 
| const char kSdpLineEnd[] = "\r\n"; | 
| @@ -167,18 +172,21 @@ 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 webrtc::PeerConnectionInterface::RTCConfiguration* config, | 
| const PeerConnectionFactory::Options* options, | 
| std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, | 
| bool prefer_constraint_apis, | 
| rtc::Thread* network_thread, | 
| rtc::Thread* worker_thread) { | 
| PeerConnectionTestClient* client(new PeerConnectionTestClient(id)); | 
| - if (!client->Init(constraints, options, std::move(cert_generator), | 
| + if (!client->Init(constraints, config, options, std::move(cert_generator), | 
| prefer_constraint_apis, network_thread, worker_thread)) { | 
| delete client; | 
| return nullptr; | 
| @@ -189,6 +197,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| static PeerConnectionTestClient* CreateClient( | 
| const std::string& id, | 
| const MediaConstraintsInterface* constraints, | 
| + const webrtc::PeerConnectionInterface::RTCConfiguration* config, | 
| const PeerConnectionFactory::Options* options, | 
| rtc::Thread* network_thread, | 
| rtc::Thread* worker_thread) { | 
| @@ -196,9 +205,9 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| rtc::SSLStreamAdapter::HaveDtlsSrtp() ? | 
| new FakeRTCCertificateGenerator() : nullptr); | 
| - return CreateClientWithDtlsIdentityStore( | 
| - id, constraints, options, std::move(cert_generator), true, | 
| - network_thread, worker_thread); | 
| + return CreateClientWithDtlsIdentityStore(id, constraints, config, options, | 
| + std::move(cert_generator), true, | 
| + network_thread, worker_thread); | 
| } | 
| static PeerConnectionTestClient* CreateClientPreferNoConstraints( | 
| @@ -210,9 +219,9 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| rtc::SSLStreamAdapter::HaveDtlsSrtp() ? | 
| new FakeRTCCertificateGenerator() : nullptr); | 
| - return CreateClientWithDtlsIdentityStore( | 
| - id, nullptr, options, std::move(cert_generator), false, | 
| - network_thread, worker_thread); | 
| + return CreateClientWithDtlsIdentityStore(id, nullptr, nullptr, options, | 
| + std::move(cert_generator), false, | 
| + network_thread, worker_thread); | 
| } | 
| ~PeerConnectionTestClient() { | 
| @@ -234,8 +243,68 @@ 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); | 
| + } | 
| + | 
| + typedef std::pair<std::string, std::string> SdpMessage; | 
| + 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<SdpMessage>(SdpMessage(type, msg))); | 
| + } | 
| + } | 
| + | 
| + typedef std::tuple<std::string, int, std::string> IceMessage; | 
| + 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<IceMessage>( | 
| + IceMessage(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<rtc::TypedMessageData<SdpMessage>*>(msg->pdata); | 
| + if (signaling_message_receiver_) { | 
| + signaling_message_receiver_->ReceiveSdpMessage( | 
| + std::get<0>(sdp_message->data()), | 
| + std::get<1>(sdp_message->data())); | 
| + } | 
| + delete sdp_message; | 
| + break; | 
| + } | 
| + case MSG_ICE_MESSAGE: { | 
| + auto ice_message = | 
| + static_cast<rtc::TypedMessageData<IceMessage>*>(msg->pdata); | 
| + if (signaling_message_receiver_) { | 
| + signaling_message_receiver_->ReceiveIceMessage( | 
| + std::get<0>(ice_message->data()), | 
| + std::get<1>(ice_message->data()), | 
| + std::get<2>(ice_message->data())); | 
| + } | 
| + delete ice_message; | 
| + break; | 
| + } | 
| + default: | 
| + RTC_CHECK(false); | 
| + } | 
| } | 
| // SignalingMessageReceiver callback. | 
| @@ -294,8 +363,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 | 
| @@ -370,6 +438,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( | 
| @@ -388,6 +458,9 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| bool ExpectIceRestart() const { return expect_ice_restart_; } | 
| + // 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); | 
| @@ -396,15 +469,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; | 
| } | 
| @@ -843,6 +925,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| bool Init( | 
| const MediaConstraintsInterface* constraints, | 
| + const webrtc::PeerConnectionInterface::RTCConfiguration* config, | 
| const PeerConnectionFactory::Options* options, | 
| std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, | 
| bool prefer_constraint_apis, | 
| @@ -855,8 +938,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<cricket::PortAllocator> port_allocator( | 
| - new cricket::FakePortAllocator(network_thread, nullptr)); | 
| + new cricket::BasicPortAllocator(fake_network_manager_.get())); | 
| 
Taylor Brandstetter
2016/07/12 19:33:34
Switched to using a BasicPortAllocator so we can t
 | 
| fake_audio_capture_module_ = FakeAudioCaptureModule::Create(); | 
| if (fake_audio_capture_module_ == nullptr) { | 
| @@ -875,23 +961,26 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| if (options) { | 
| peer_connection_factory_->SetOptions(*options); | 
| } | 
| - peer_connection_ = CreatePeerConnection( | 
| - std::move(port_allocator), constraints, std::move(cert_generator)); | 
| + peer_connection_ = | 
| + CreatePeerConnection(std::move(port_allocator), constraints, config, | 
| + std::move(cert_generator)); | 
| return peer_connection_.get() != nullptr; | 
| } | 
| rtc::scoped_refptr<webrtc::PeerConnectionInterface> CreatePeerConnection( | 
| std::unique_ptr<cricket::PortAllocator> port_allocator, | 
| const MediaConstraintsInterface* constraints, | 
| + const webrtc::PeerConnectionInterface::RTCConfiguration* config, | 
| std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator) { | 
| // CreatePeerConnection with RTCConfiguration. | 
| - webrtc::PeerConnectionInterface::RTCConfiguration config; | 
| - webrtc::PeerConnectionInterface::IceServer ice_server; | 
| - ice_server.uri = "stun:stun.l.google.com:19302"; | 
| 
Taylor Brandstetter
2016/07/12 19:33:34
This STUN server wasn't doing anything since the F
 | 
| - config.servers.push_back(ice_server); | 
| + webrtc::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); | 
| } | 
| @@ -911,10 +1000,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) { | 
| @@ -1015,6 +1101,8 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, | 
| std::string id_; | 
| + std::unique_ptr<rtc::FakeNetworkManager> fake_network_manager_; | 
| + | 
| rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_; | 
| rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | 
| peer_connection_factory_; | 
| @@ -1043,6 +1131,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. | 
| @@ -1179,8 +1268,8 @@ 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 CreateTestClientsThatPreferNoConstraints() { | 
| @@ -1204,16 +1293,24 @@ class P2PTestConductor : public testing::Test { | 
| 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) { | 
| + void SetSignalingDelayMs(int delay_ms) { | 
| + initiating_client_->set_signaling_delay_ms(delay_ms); | 
| + receiving_client_->set_signaling_delay_ms(delay_ms); | 
| + } | 
| + | 
| + bool CreateTestClients( | 
| + MediaConstraintsInterface* init_constraints, | 
| + const webrtc::PeerConnectionInterface::RTCConfiguration* init_config, | 
| + PeerConnectionFactory::Options* init_options, | 
| + MediaConstraintsInterface* recv_constraints, | 
| + const webrtc::PeerConnectionInterface::RTCConfiguration* recv_config, | 
| + PeerConnectionFactory::Options* recv_options) { | 
| initiating_client_.reset(PeerConnectionTestClient::CreateClient( | 
| - "Caller: ", init_constraints, init_options, network_thread_.get(), | 
| - worker_thread_.get())); | 
| + "Caller: ", init_constraints, init_config, init_options, | 
| + network_thread_.get(), worker_thread_.get())); | 
| receiving_client_.reset(PeerConnectionTestClient::CreateClient( | 
| - "Callee: ", recv_constraints, recv_options, network_thread_.get(), | 
| - worker_thread_.get())); | 
| + "Callee: ", recv_constraints, recv_config, recv_options, | 
| + network_thread_.get(), worker_thread_.get())); | 
| if (!initiating_client_ || !receiving_client_) { | 
| return false; | 
| } | 
| @@ -1313,7 +1410,7 @@ class P2PTestConductor : public testing::Test { | 
| // Make sure the new client is using a different certificate. | 
| return PeerConnectionTestClient::CreateClientWithDtlsIdentityStore( | 
| - "New Peer: ", &setup_constraints, nullptr, | 
| + "New Peer: ", &setup_constraints, nullptr, nullptr, | 
| std::move(cert_generator), prefer_constraint_apis_, | 
| network_thread_.get(), worker_thread_.get()); | 
| } | 
| @@ -1328,6 +1425,10 @@ class P2PTestConductor : public testing::Test { | 
| } | 
| } | 
| + rtc::Thread* network_thread() { return network_thread_.get(); } | 
| + | 
| + rtc::VirtualSocketServer* virtual_socket_server() { return ss_.get(); } | 
| + | 
| PeerConnectionTestClient* initializing_client() { | 
| return initiating_client_.get(); | 
| } | 
| @@ -1718,8 +1819,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, nullptr, &init_options, nullptr, | 
| + nullptr, &recv_options)); | 
| rtc::scoped_refptr<webrtc::FakeMetricsObserver> | 
| init_observer = new rtc::RefCountedObject<webrtc::FakeMetricsObserver>(); | 
| initializing_client()->pc()->RegisterUMAObserver(init_observer); | 
| @@ -1743,8 +1844,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, nullptr, &init_options, nullptr, | 
| + nullptr, &recv_options)); | 
| rtc::scoped_refptr<webrtc::FakeMetricsObserver> | 
| init_observer = new rtc::RefCountedObject<webrtc::FakeMetricsObserver>(); | 
| initializing_client()->pc()->RegisterUMAObserver(init_observer); | 
| @@ -1769,8 +1870,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, nullptr, &init_options, nullptr, | 
| + nullptr, &recv_options)); | 
| rtc::scoped_refptr<webrtc::FakeMetricsObserver> | 
| init_observer = new rtc::RefCountedObject<webrtc::FakeMetricsObserver>(); | 
| initializing_client()->pc()->RegisterUMAObserver(init_observer); | 
| @@ -1795,8 +1896,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, nullptr, &init_options, nullptr, | 
| + nullptr, &recv_options)); | 
| rtc::scoped_refptr<webrtc::FakeMetricsObserver> | 
| init_observer = new rtc::RefCountedObject<webrtc::FakeMetricsObserver>(); | 
| initializing_client()->pc()->RegisterUMAObserver(init_observer); | 
| @@ -2106,6 +2207,103 @@ 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 trips we expect to be | 
| +// required: | 
| +// 1. 2 signaling trip: Signaling offer and offerer's TURN candidate, then | 
| +// signaling answer (with DTLS fingerprint). | 
| +// 2. 9 media trips: 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_trip_delay_ms = 50; | 
| + static constexpr int signaling_trip_delay_ms = 500; | 
| + // For explanation of these values, see comment above. | 
| + static constexpr int required_media_trips = 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_trip_delay_ms * required_media_trips + | 
| + 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); | 
| + | 
| + webrtc::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; | 
| + | 
| + webrtc::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(nullptr, &client_1_config, nullptr, nullptr, | 
| + &client_2_config, nullptr)); | 
| + // Set up the simulated delays. | 
| + SetSignalingDelayMs(signaling_trip_delay_ms); | 
| + virtual_socket_server()->set_delay_mean(media_trip_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. |