|  | // Copyright (c) 2018 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 "quic/core/quic_trace_visitor.h" | 
|  |  | 
|  | #include "quic/core/quic_constants.h" | 
|  | #include "quic/platform/api/quic_test.h" | 
|  | #include "quic/test_tools/quic_test_utils.h" | 
|  | #include "quic/test_tools/simulator/quic_endpoint.h" | 
|  | #include "quic/test_tools/simulator/simulator.h" | 
|  | #include "quic/test_tools/simulator/switch.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace { | 
|  |  | 
|  | const QuicByteCount kTransferSize = 1000 * kMaxOutgoingPacketSize; | 
|  | const QuicByteCount kTestStreamNumber = 3; | 
|  | const QuicTime::Delta kDelay = QuicTime::Delta::FromMilliseconds(20); | 
|  |  | 
|  | // The trace for this test is generated using a simulator transfer. | 
|  | class QuicTraceVisitorTest : public QuicTest { | 
|  | public: | 
|  | QuicTraceVisitorTest() { | 
|  | QuicConnectionId connection_id = test::TestConnectionId(); | 
|  | simulator::Simulator simulator; | 
|  | simulator::QuicEndpoint client(&simulator, "Client", "Server", | 
|  | Perspective::IS_CLIENT, connection_id); | 
|  | simulator::QuicEndpoint server(&simulator, "Server", "Client", | 
|  | Perspective::IS_SERVER, connection_id); | 
|  |  | 
|  | const QuicBandwidth kBandwidth = QuicBandwidth::FromKBitsPerSecond(1000); | 
|  | const QuicByteCount kBdp = kBandwidth * (2 * kDelay); | 
|  |  | 
|  | // Create parameters such that some loss is observed. | 
|  | simulator::Switch network_switch(&simulator, "Switch", 8, 0.5 * kBdp); | 
|  | simulator::SymmetricLink client_link(&client, network_switch.port(1), | 
|  | 2 * kBandwidth, kDelay); | 
|  | simulator::SymmetricLink server_link(&server, network_switch.port(2), | 
|  | kBandwidth, kDelay); | 
|  |  | 
|  | QuicTraceVisitor visitor(client.connection()); | 
|  | client.connection()->set_debug_visitor(&visitor); | 
|  |  | 
|  | // Transfer about a megabyte worth of data from client to server. | 
|  | const QuicTime::Delta kDeadline = | 
|  | 3 * kBandwidth.TransferTime(kTransferSize); | 
|  | client.AddBytesToTransfer(kTransferSize); | 
|  | bool simulator_result = simulator.RunUntilOrTimeout( | 
|  | [&]() { return server.bytes_received() >= kTransferSize; }, kDeadline); | 
|  | QUICHE_CHECK(simulator_result); | 
|  |  | 
|  | // Save the trace and ensure some loss was observed. | 
|  | trace_.Swap(visitor.trace()); | 
|  | QUICHE_CHECK_NE(0u, client.connection()->GetStats().packets_retransmitted); | 
|  | packets_sent_ = client.connection()->GetStats().packets_sent; | 
|  | } | 
|  |  | 
|  | std::vector<quic_trace::Event> AllEventsWithType( | 
|  | quic_trace::EventType event_type) { | 
|  | std::vector<quic_trace::Event> result; | 
|  | for (const auto& event : trace_.events()) { | 
|  | if (event.event_type() == event_type) { | 
|  | result.push_back(event); | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | quic_trace::Trace trace_; | 
|  | QuicPacketCount packets_sent_; | 
|  | }; | 
|  |  | 
|  | TEST_F(QuicTraceVisitorTest, ConnectionId) { | 
|  | char expected_cid[] = {0, 0, 0, 0, 0, 0, 0, 42}; | 
|  | EXPECT_EQ(std::string(expected_cid, sizeof(expected_cid)), | 
|  | trace_.destination_connection_id()); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicTraceVisitorTest, Version) { | 
|  | std::string version = trace_.protocol_version(); | 
|  | ASSERT_EQ(4u, version.size()); | 
|  | // Ensure version isn't all-zeroes. | 
|  | EXPECT_TRUE(version[0] != 0 || version[1] != 0 || version[2] != 0 || | 
|  | version[3] != 0); | 
|  | } | 
|  |  | 
|  | // Check that basic metadata about sent packets is recorded. | 
|  | TEST_F(QuicTraceVisitorTest, SentPacket) { | 
|  | auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT); | 
|  | EXPECT_EQ(packets_sent_, sent_packets.size()); | 
|  | ASSERT_GT(sent_packets.size(), 0u); | 
|  |  | 
|  | EXPECT_EQ(sent_packets[0].packet_size(), kDefaultMaxPacketSize); | 
|  | EXPECT_EQ(sent_packets[0].packet_number(), 1u); | 
|  | } | 
|  |  | 
|  | // Ensure that every stream frame that was sent is recorded. | 
|  | TEST_F(QuicTraceVisitorTest, SentStream) { | 
|  | auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT); | 
|  |  | 
|  | QuicIntervalSet<QuicStreamOffset> offsets; | 
|  | for (const quic_trace::Event& packet : sent_packets) { | 
|  | for (const quic_trace::Frame& frame : packet.frames()) { | 
|  | if (frame.frame_type() != quic_trace::STREAM) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const quic_trace::StreamFrameInfo& info = frame.stream_frame_info(); | 
|  | if (info.stream_id() != kTestStreamNumber) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | ASSERT_GT(info.length(), 0u); | 
|  | offsets.Add(info.offset(), info.offset() + info.length()); | 
|  | } | 
|  | } | 
|  |  | 
|  | ASSERT_EQ(1u, offsets.Size()); | 
|  | EXPECT_EQ(0u, offsets.begin()->min()); | 
|  | EXPECT_EQ(kTransferSize, offsets.rbegin()->max()); | 
|  | } | 
|  |  | 
|  | // Ensure that all packets are either acknowledged or lost. | 
|  | TEST_F(QuicTraceVisitorTest, AckPackets) { | 
|  | QuicIntervalSet<QuicPacketNumber> packets; | 
|  | for (const quic_trace::Event& packet : trace_.events()) { | 
|  | if (packet.event_type() == quic_trace::PACKET_RECEIVED) { | 
|  | for (const quic_trace::Frame& frame : packet.frames()) { | 
|  | if (frame.frame_type() != quic_trace::ACK) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const quic_trace::AckInfo& info = frame.ack_info(); | 
|  | for (const auto& block : info.acked_packets()) { | 
|  | packets.Add(QuicPacketNumber(block.first_packet()), | 
|  | QuicPacketNumber(block.last_packet()) + 1); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (packet.event_type() == quic_trace::PACKET_LOST) { | 
|  | packets.Add(QuicPacketNumber(packet.packet_number()), | 
|  | QuicPacketNumber(packet.packet_number()) + 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | ASSERT_EQ(1u, packets.Size()); | 
|  | EXPECT_EQ(QuicPacketNumber(1u), packets.begin()->min()); | 
|  | // We leave some room (20 packets) for the packets which did not receive | 
|  | // conclusive status at the end of simulation. | 
|  | EXPECT_GT(packets.rbegin()->max(), QuicPacketNumber(packets_sent_ - 20)); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicTraceVisitorTest, TransportState) { | 
|  | auto acks = AllEventsWithType(quic_trace::PACKET_RECEIVED); | 
|  | ASSERT_EQ(1, acks[0].frames_size()); | 
|  | ASSERT_EQ(quic_trace::ACK, acks[0].frames(0).frame_type()); | 
|  |  | 
|  | // Check that min-RTT at the end is a reasonable approximation. | 
|  | EXPECT_LE((4 * kDelay).ToMicroseconds() * 1., | 
|  | acks.rbegin()->transport_state().min_rtt_us()); | 
|  | EXPECT_GE((4 * kDelay).ToMicroseconds() * 1.25, | 
|  | acks.rbegin()->transport_state().min_rtt_us()); | 
|  | } | 
|  |  | 
|  | TEST_F(QuicTraceVisitorTest, EncryptionLevels) { | 
|  | for (const auto& event : trace_.events()) { | 
|  | switch (event.event_type()) { | 
|  | case quic_trace::PACKET_SENT: | 
|  | case quic_trace::PACKET_RECEIVED: | 
|  | case quic_trace::PACKET_LOST: | 
|  | ASSERT_TRUE(event.has_encryption_level()); | 
|  | ASSERT_NE(event.encryption_level(), quic_trace::ENCRYPTION_UNKNOWN); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace quic |