blob: 07bc4f2ea37a5de9de95520a125e715d1210c286 [file] [log] [blame]
// 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 "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
#include "net/third_party/quiche/src/quic/core/quic_constants.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.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"
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);
CHECK(simulator_result);
// Save the trace and ensure some loss was observed.
trace_.Swap(visitor.trace());
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());
EXPECT_NE(0, version[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