|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h" | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" | 
|  | #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::NiceMock; | 
|  | using ::testing::Return; | 
|  |  | 
|  | namespace quic { | 
|  | namespace simulator { | 
|  |  | 
|  | const QuicBandwidth kDefaultBandwidth = | 
|  | QuicBandwidth::FromKBitsPerSecond(10 * 1000); | 
|  | const QuicTime::Delta kDefaultPropagationDelay = | 
|  | QuicTime::Delta::FromMilliseconds(20); | 
|  | const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay; | 
|  |  | 
|  | // A simple test harness where all hosts are connected to a switch with | 
|  | // identical links. | 
|  | class QuicEndpointTest : public QuicTest { | 
|  | public: | 
|  | QuicEndpointTest() | 
|  | : simulator_(), switch_(&simulator_, "Switch", 8, kDefaultBdp * 2) {} | 
|  |  | 
|  | protected: | 
|  | Simulator simulator_; | 
|  | Switch switch_; | 
|  |  | 
|  | std::unique_ptr<SymmetricLink> Link(Endpoint* a, Endpoint* b) { | 
|  | return QuicMakeUnique<SymmetricLink>(a, b, kDefaultBandwidth, | 
|  | kDefaultPropagationDelay); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SymmetricLink> CustomLink(Endpoint* a, | 
|  | Endpoint* b, | 
|  | uint64_t extra_rtt_ms) { | 
|  | return QuicMakeUnique<SymmetricLink>( | 
|  | a, b, kDefaultBandwidth, | 
|  | kDefaultPropagationDelay + | 
|  | QuicTime::Delta::FromMilliseconds(extra_rtt_ms)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Test transmission from one host to another. | 
|  | TEST_F(QuicEndpointTest, OneWayTransmission) { | 
|  | QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B", | 
|  | Perspective::IS_CLIENT, test::TestConnectionId(42)); | 
|  | QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A", | 
|  | Perspective::IS_SERVER, test::TestConnectionId(42)); | 
|  | auto link_a = Link(&endpoint_a, switch_.port(1)); | 
|  | auto link_b = Link(&endpoint_b, switch_.port(2)); | 
|  |  | 
|  | // First transmit a small, packet-size chunk of data. | 
|  | endpoint_a.AddBytesToTransfer(600); | 
|  | QuicTime end_time = | 
|  | simulator_.GetClock()->Now() + QuicTime::Delta::FromMilliseconds(1000); | 
|  | simulator_.RunUntil( | 
|  | [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; }); | 
|  |  | 
|  | EXPECT_EQ(600u, endpoint_a.bytes_transferred()); | 
|  | ASSERT_EQ(600u, endpoint_b.bytes_received()); | 
|  | EXPECT_FALSE(endpoint_a.wrong_data_received()); | 
|  | EXPECT_FALSE(endpoint_b.wrong_data_received()); | 
|  |  | 
|  | // After a small chunk succeeds, try to transfer 2 MiB. | 
|  | endpoint_a.AddBytesToTransfer(2 * 1024 * 1024); | 
|  | end_time = simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5); | 
|  | simulator_.RunUntil( | 
|  | [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; }); | 
|  |  | 
|  | const QuicByteCount total_bytes_transferred = 600 + 2 * 1024 * 1024; | 
|  | EXPECT_EQ(total_bytes_transferred, endpoint_a.bytes_transferred()); | 
|  | EXPECT_EQ(total_bytes_transferred, endpoint_b.bytes_received()); | 
|  | EXPECT_EQ(0u, endpoint_a.write_blocked_count()); | 
|  | EXPECT_FALSE(endpoint_a.wrong_data_received()); | 
|  | EXPECT_FALSE(endpoint_b.wrong_data_received()); | 
|  | } | 
|  |  | 
|  | // Test the situation in which the writer becomes write-blocked. | 
|  | TEST_F(QuicEndpointTest, WriteBlocked) { | 
|  | QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B", | 
|  | Perspective::IS_CLIENT, test::TestConnectionId(42)); | 
|  | QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A", | 
|  | Perspective::IS_SERVER, test::TestConnectionId(42)); | 
|  | auto link_a = Link(&endpoint_a, switch_.port(1)); | 
|  | auto link_b = Link(&endpoint_b, switch_.port(2)); | 
|  |  | 
|  | // Will be owned by the sent packet manager. | 
|  | auto* sender = new NiceMock<test::MockSendAlgorithm>(); | 
|  | EXPECT_CALL(*sender, CanSend(_)).WillRepeatedly(Return(true)); | 
|  | EXPECT_CALL(*sender, PacingRate(_)) | 
|  | .WillRepeatedly(Return(10 * kDefaultBandwidth)); | 
|  | EXPECT_CALL(*sender, BandwidthEstimate()) | 
|  | .WillRepeatedly(Return(10 * kDefaultBandwidth)); | 
|  | EXPECT_CALL(*sender, GetCongestionWindow()) | 
|  | .WillRepeatedly( | 
|  | Return(kMaxOutgoingPacketSize * kDefaultMaxCongestionWindowPackets)); | 
|  | test::QuicConnectionPeer::SetSendAlgorithm(endpoint_a.connection(), sender); | 
|  |  | 
|  | // First transmit a small, packet-size chunk of data. | 
|  | QuicByteCount bytes_to_transfer = 3 * 1024 * 1024; | 
|  | endpoint_a.AddBytesToTransfer(bytes_to_transfer); | 
|  | QuicTime end_time = | 
|  | simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(30); | 
|  | simulator_.RunUntil([this, &endpoint_b, bytes_to_transfer, end_time]() { | 
|  | return endpoint_b.bytes_received() == bytes_to_transfer || | 
|  | simulator_.GetClock()->Now() >= end_time; | 
|  | }); | 
|  |  | 
|  | EXPECT_EQ(bytes_to_transfer, endpoint_a.bytes_transferred()); | 
|  | EXPECT_EQ(bytes_to_transfer, endpoint_b.bytes_received()); | 
|  | EXPECT_GT(endpoint_a.write_blocked_count(), 0u); | 
|  | EXPECT_FALSE(endpoint_a.wrong_data_received()); | 
|  | EXPECT_FALSE(endpoint_b.wrong_data_received()); | 
|  | } | 
|  |  | 
|  | // Test transmission of 1 MiB of data between two hosts simultaneously in both | 
|  | // directions. | 
|  | TEST_F(QuicEndpointTest, TwoWayTransmission) { | 
|  | QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B", | 
|  | Perspective::IS_CLIENT, test::TestConnectionId(42)); | 
|  | QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A", | 
|  | Perspective::IS_SERVER, test::TestConnectionId(42)); | 
|  | auto link_a = Link(&endpoint_a, switch_.port(1)); | 
|  | auto link_b = Link(&endpoint_b, switch_.port(2)); | 
|  |  | 
|  | endpoint_a.RecordTrace(); | 
|  | endpoint_b.RecordTrace(); | 
|  |  | 
|  | endpoint_a.AddBytesToTransfer(1024 * 1024); | 
|  | endpoint_b.AddBytesToTransfer(1024 * 1024); | 
|  | QuicTime end_time = | 
|  | simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5); | 
|  | simulator_.RunUntil( | 
|  | [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; }); | 
|  |  | 
|  | EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_transferred()); | 
|  | EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_transferred()); | 
|  | EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_received()); | 
|  | EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_received()); | 
|  | EXPECT_FALSE(endpoint_a.wrong_data_received()); | 
|  | EXPECT_FALSE(endpoint_b.wrong_data_received()); | 
|  | } | 
|  |  | 
|  | // Simulate three hosts trying to send data to a fourth one simultaneously. | 
|  | TEST_F(QuicEndpointTest, Competition) { | 
|  | // TODO(63765788): Turn back on this flag when the issue if fixed. | 
|  | SetQuicReloadableFlag(quic_bbr_one_mss_conservation, false); | 
|  | auto endpoint_a = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint A", "Endpoint D (A)", Perspective::IS_CLIENT, | 
|  | test::TestConnectionId(42)); | 
|  | auto endpoint_b = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint B", "Endpoint D (B)", Perspective::IS_CLIENT, | 
|  | test::TestConnectionId(43)); | 
|  | auto endpoint_c = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint C", "Endpoint D (C)", Perspective::IS_CLIENT, | 
|  | test::TestConnectionId(44)); | 
|  | auto endpoint_d_a = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint D (A)", "Endpoint A", Perspective::IS_SERVER, | 
|  | test::TestConnectionId(42)); | 
|  | auto endpoint_d_b = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint D (B)", "Endpoint B", Perspective::IS_SERVER, | 
|  | test::TestConnectionId(43)); | 
|  | auto endpoint_d_c = QuicMakeUnique<QuicEndpoint>( | 
|  | &simulator_, "Endpoint D (C)", "Endpoint C", Perspective::IS_SERVER, | 
|  | test::TestConnectionId(44)); | 
|  | QuicEndpointMultiplexer endpoint_d( | 
|  | "Endpoint D", | 
|  | {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()}); | 
|  |  | 
|  | // Create links with slightly different RTTs in order to avoid pathological | 
|  | // side-effects of packets entering the queue at the exactly same time. | 
|  | auto link_a = CustomLink(endpoint_a.get(), switch_.port(1), 0); | 
|  | auto link_b = CustomLink(endpoint_b.get(), switch_.port(2), 1); | 
|  | auto link_c = CustomLink(endpoint_c.get(), switch_.port(3), 2); | 
|  | auto link_d = Link(&endpoint_d, switch_.port(4)); | 
|  |  | 
|  | endpoint_a->AddBytesToTransfer(2 * 1024 * 1024); | 
|  | endpoint_b->AddBytesToTransfer(2 * 1024 * 1024); | 
|  | endpoint_c->AddBytesToTransfer(2 * 1024 * 1024); | 
|  | QuicTime end_time = | 
|  | simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(10); | 
|  | simulator_.RunUntil( | 
|  | [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; }); | 
|  |  | 
|  | for (QuicEndpoint* endpoint : | 
|  | {endpoint_a.get(), endpoint_b.get(), endpoint_c.get()}) { | 
|  | EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_transferred()); | 
|  | EXPECT_GE(endpoint->connection()->GetStats().packets_lost, 0u); | 
|  | } | 
|  | for (QuicEndpoint* endpoint : | 
|  | {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()}) { | 
|  | EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_received()); | 
|  | EXPECT_FALSE(endpoint->wrong_data_received()); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace simulator | 
|  | }  // namespace quic |