| // 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 "quiche/quic/core/quic_trace_visitor.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "quiche/quic/core/quic_constants.h" |
| #include "quiche/quic/platform/api/quic_test.h" |
| #include "quiche/quic/test_tools/quic_test_utils.h" |
| #include "quiche/quic/test_tools/simulator/quic_endpoint.h" |
| #include "quiche/quic/test_tools/simulator/simulator.h" |
| #include "quiche/quic/test_tools/simulator/switch.h" |
| |
| namespace quic::test { |
| 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::test |