blob: 04e317b625833c7bdf068ebf5530df32f748b492 [file] [log] [blame]
// Copyright (c) 2019 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/quartc/test/quartc_peer.h"
#include <utility>
#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_constants.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
namespace quic {
namespace test {
namespace {
class QuartcPeerTest : public QuicTest {
protected:
QuartcPeerTest()
: client_transport_(&simulator_,
"client_transport",
"server_transport",
10 * kDefaultMaxPacketSize),
server_transport_(&simulator_,
"server_transport",
"client_transport",
10 * kDefaultMaxPacketSize),
client_server_link_(&client_transport_,
&server_transport_,
QuicBandwidth::FromKBitsPerSecond(512),
QuicTime::Delta::FromMilliseconds(100)) {
SetQuicReloadableFlag(quic_default_to_bbr, true);
simulator_.set_random_generator(&rng_);
}
void CreatePeers(const std::vector<QuartcDataSource::Config>& configs) {
client_peer_ = std::make_unique<QuartcPeer>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(),
simulator_.GetStreamSendBufferAllocator(), configs);
server_peer_ = std::make_unique<QuartcPeer>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(),
simulator_.GetStreamSendBufferAllocator(), configs);
}
void Connect() {
DCHECK(client_peer_);
DCHECK(server_peer_);
server_endpoint_ = std::make_unique<QuartcServerEndpoint>(
simulator_.GetAlarmFactory(), simulator_.GetClock(),
simulator_.GetRandomGenerator(), server_peer_.get(),
QuartcSessionConfig());
client_endpoint_ = std::make_unique<QuartcClientEndpoint>(
simulator_.GetAlarmFactory(), simulator_.GetClock(),
simulator_.GetRandomGenerator(), client_peer_.get(),
QuartcSessionConfig(), server_endpoint_->server_crypto_config());
server_endpoint_->Connect(&server_transport_);
client_endpoint_->Connect(&client_transport_);
}
void RampUpBandwidth() {
// Run long enough for the bandwidth estimate to ramp up.
simulator_.RunUntilOrTimeout(
[this] {
return client_peer_->last_available_bandwidth() ==
client_server_link_.bandwidth() &&
server_peer_->last_available_bandwidth() ==
client_server_link_.bandwidth();
},
QuicTime::Delta::FromSeconds(60));
}
SimpleRandom rng_;
simulator::Simulator simulator_;
simulator::SimulatedQuartcPacketTransport client_transport_;
simulator::SimulatedQuartcPacketTransport server_transport_;
simulator::SymmetricLink client_server_link_;
std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
std::unique_ptr<QuartcPeer> client_peer_;
std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
std::unique_ptr<QuartcPeer> server_peer_;
};
const ReceivedMessage& FindLastMessageFromSource(
const std::vector<ReceivedMessage>& messages,
int32_t source_id) {
const auto& it = std::find_if(messages.rbegin(), messages.rend(),
[source_id](const ReceivedMessage& r) {
return r.frame.source_id == source_id;
});
return *it;
}
TEST_F(QuartcPeerTest, SendReceiveMessages) {
QuicTime start_time = simulator_.GetClock()->Now();
QuartcDataSource::Config config;
config.id = 1;
CreatePeers({config});
Connect();
ASSERT_TRUE(simulator_.RunUntil([this] {
return !client_peer_->received_messages().empty() &&
!server_peer_->received_messages().empty();
}));
QuicTime end_time = simulator_.GetClock()->Now();
// Sanity checks on messages.
const ReceivedMessage& client_message = client_peer_->received_messages()[0];
EXPECT_EQ(client_message.frame.source_id, 1);
EXPECT_EQ(client_message.frame.sequence_number, 0);
EXPECT_GE(client_message.frame.send_time, start_time);
EXPECT_LE(client_message.receive_time, end_time);
const ReceivedMessage& server_message = server_peer_->received_messages()[0];
EXPECT_EQ(server_message.frame.source_id, 1);
EXPECT_EQ(server_message.frame.sequence_number, 0);
EXPECT_GE(server_message.frame.send_time, start_time);
EXPECT_LE(server_message.receive_time, end_time);
}
TEST_F(QuartcPeerTest, MaxFrameSizeUnset) {
// Configure the source with no max frame size, and a framerate and max
// bandwidth that allows very large frames (larger than will fit in a packet).
QuartcDataSource::Config config;
config.id = 1;
config.frame_interval = QuicTime::Delta::FromMilliseconds(20);
config.max_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
2 * kDefaultMaxPacketSize, config.frame_interval);
CreatePeers({config});
Connect();
RampUpBandwidth();
// The peers generate frames that fit in one packet.
EXPECT_LT(client_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
EXPECT_LT(server_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
}
TEST_F(QuartcPeerTest, MaxFrameSizeLargerThanPacketSize) {
// Configure the source with a max frame size larger than the packet size.
QuartcDataSource::Config config;
config.id = 1;
config.max_frame_size = 2 * kDefaultMaxPacketSize;
CreatePeers({config});
Connect();
RampUpBandwidth();
// The peers generate frames that fit in one packet.
EXPECT_LT(client_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
EXPECT_LT(server_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
}
TEST_F(QuartcPeerTest, MaxFrameSizeSmallerThanPacketSize) {
QuartcDataSource::Config config;
config.id = 1;
config.max_frame_size = 100;
// Note that a long frame interval helps to ensure that the test produces
// enough bytes per frame to reach max_frame_size.
config.frame_interval = QuicTime::Delta::FromMilliseconds(100);
CreatePeers({config});
Connect();
RampUpBandwidth();
EXPECT_EQ(client_peer_->received_messages().back().frame.size, 100u);
EXPECT_EQ(server_peer_->received_messages().back().frame.size, 100u);
}
TEST_F(QuartcPeerTest, MaxFrameSizeSmallerThanFrameHeader) {
QuartcDataSource::Config config;
config.id = 1;
config.max_frame_size = kDataFrameHeaderSize - 1;
CreatePeers({config});
Connect();
RampUpBandwidth();
// Max frame sizes smaller than the header are ignored, and the frame size is
// limited by packet size.
EXPECT_LT(client_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
EXPECT_LT(server_peer_->received_messages().back().frame.size,
kDefaultMaxPacketSize);
}
TEST_F(QuartcPeerTest, SendReceiveMultipleSources) {
QuicTime start_time = simulator_.GetClock()->Now();
// Note: use of really long frame intervals means that each source should send
// one frame during this test. This simplifies expectations for received
// data.
QuartcDataSource::Config config_1;
config_1.id = 1;
config_1.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(32);
config_1.frame_interval = QuicTime::Delta::FromSeconds(10);
QuartcDataSource::Config config_2;
config_2.id = 2;
config_2.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(64);
config_2.frame_interval = QuicTime::Delta::FromSeconds(10);
QuartcDataSource::Config config_3;
config_3.id = 3;
config_3.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(128);
config_3.frame_interval = QuicTime::Delta::FromSeconds(10);
CreatePeers({config_1, config_2, config_3});
Connect();
ASSERT_TRUE(simulator_.RunUntil([this] {
return client_peer_->received_messages().size() == 3 &&
server_peer_->received_messages().size() == 3;
}));
QuicTime end_time = simulator_.GetClock()->Now();
// Sanity checks on messages.
const auto& order = [](const ReceivedMessage& lhs,
const ReceivedMessage& rhs) {
return lhs.frame.source_id < rhs.frame.source_id;
};
std::vector<ReceivedMessage> client_messages =
client_peer_->received_messages();
std::sort(client_messages.begin(), client_messages.end(), order);
for (size_t i = 0; i < client_messages.size(); ++i) {
EXPECT_EQ(client_messages[i].frame.source_id, static_cast<int32_t>(i + 1));
EXPECT_EQ(client_messages[i].frame.sequence_number, 0);
EXPECT_GE(client_messages[i].frame.send_time, start_time);
EXPECT_LE(client_messages[i].receive_time, end_time);
}
std::vector<ReceivedMessage> server_messages =
server_peer_->received_messages();
std::sort(server_messages.begin(), server_messages.end(), order);
for (size_t i = 0; i < server_messages.size(); ++i) {
EXPECT_EQ(server_messages[i].frame.source_id, static_cast<int32_t>(i + 1u));
EXPECT_EQ(server_messages[i].frame.sequence_number, 0);
EXPECT_GE(server_messages[i].frame.send_time, start_time);
EXPECT_LE(server_messages[i].receive_time, end_time);
}
}
TEST_F(QuartcPeerTest, BandwidthAllocationWithEnoughAvailable) {
QuartcDataSource::Config config_1;
config_1.id = 1;
config_1.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(32);
config_1.frame_interval = QuicTime::Delta::FromMilliseconds(100);
QuartcDataSource::Config config_2;
config_2.id = 2;
config_2.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(64);
config_2.frame_interval = QuicTime::Delta::FromMilliseconds(25);
QuartcDataSource::Config config_3;
config_3.id = 3;
config_3.max_bandwidth = QuicBandwidth::FromKBitsPerSecond(128);
config_3.frame_interval = QuicTime::Delta::FromMilliseconds(10);
CreatePeers({config_1, config_2, config_3});
Connect();
RampUpBandwidth();
// The last message from each source should be the size allowed by that
// source's maximum bandwidth and frame interval.
QuicByteCount source_1_size =
config_1.max_bandwidth.ToBytesPerPeriod(config_1.frame_interval);
QuicByteCount source_2_size =
config_2.max_bandwidth.ToBytesPerPeriod(config_2.frame_interval);
QuicByteCount source_3_size =
config_3.max_bandwidth.ToBytesPerPeriod(config_3.frame_interval);
const std::vector<ReceivedMessage>& client_messages =
client_peer_->received_messages();
EXPECT_EQ(FindLastMessageFromSource(client_messages, 1).frame.size,
source_1_size);
EXPECT_EQ(FindLastMessageFromSource(client_messages, 2).frame.size,
source_2_size);
EXPECT_EQ(FindLastMessageFromSource(client_messages, 3).frame.size,
source_3_size);
const std::vector<ReceivedMessage>& server_messages =
server_peer_->received_messages();
EXPECT_EQ(FindLastMessageFromSource(server_messages, 1).frame.size,
source_1_size);
EXPECT_EQ(FindLastMessageFromSource(server_messages, 2).frame.size,
source_2_size);
EXPECT_EQ(FindLastMessageFromSource(server_messages, 3).frame.size,
source_3_size);
}
TEST_F(QuartcPeerTest, BandwidthAllocationWithoutEnoughAvailable) {
QuartcDataSource::Config config_1;
config_1.id = 1;
config_1.max_bandwidth = client_server_link_.bandwidth() * 0.5;
config_1.frame_interval = QuicTime::Delta::FromMilliseconds(10);
QuartcDataSource::Config config_2;
config_2.id = 2;
config_2.min_bandwidth = QuicBandwidth::FromKBitsPerSecond(32);
config_2.max_bandwidth = client_server_link_.bandwidth();
config_2.frame_interval = QuicTime::Delta::FromMilliseconds(5);
QuartcDataSource::Config config_3;
config_3.id = 3;
config_3.min_bandwidth = QuicBandwidth::FromKBitsPerSecond(32);
config_3.max_bandwidth = client_server_link_.bandwidth() * 2;
config_3.frame_interval = QuicTime::Delta::FromMilliseconds(20);
CreatePeers({config_1, config_2, config_3});
Connect();
RampUpBandwidth();
const std::vector<ReceivedMessage>& client_messages =
client_peer_->received_messages();
const std::vector<ReceivedMessage>& server_messages =
server_peer_->received_messages();
// Source 1 eventually ramps up to full bandwidth.
const QuicByteCount source_1_size =
config_1.max_bandwidth.ToBytesPerPeriod(config_1.frame_interval);
EXPECT_EQ(FindLastMessageFromSource(client_messages, 1).frame.size,
source_1_size);
EXPECT_EQ(FindLastMessageFromSource(server_messages, 1).frame.size,
source_1_size);
// Source 2 takes the remainder of available bandwidth. However, the exact
// value depends on the bandwidth estimate.
const QuicByteCount source_2_min =
config_2.min_bandwidth.ToBytesPerPeriod(config_2.frame_interval);
const QuicByteCount source_2_max =
config_2.max_bandwidth.ToBytesPerPeriod(config_2.frame_interval);
EXPECT_GT(FindLastMessageFromSource(client_messages, 2).frame.size,
source_2_min);
EXPECT_LT(FindLastMessageFromSource(client_messages, 2).frame.size,
source_2_max);
EXPECT_GT(FindLastMessageFromSource(server_messages, 2).frame.size,
source_2_min);
EXPECT_LT(FindLastMessageFromSource(server_messages, 2).frame.size,
source_2_max);
// Source 3 gets only its minimum bandwidth.
const QuicByteCount source_3_size =
config_3.min_bandwidth.ToBytesPerPeriod(config_3.frame_interval);
EXPECT_EQ(FindLastMessageFromSource(client_messages, 3).frame.size,
source_3_size);
EXPECT_EQ(FindLastMessageFromSource(server_messages, 3).frame.size,
source_3_size);
}
TEST_F(QuartcPeerTest, DisableAndDrainMessages) {
QuartcDataSource::Config config;
config.id = 1;
config.max_bandwidth = client_server_link_.bandwidth() * 0.5;
config.frame_interval = QuicTime::Delta::FromMilliseconds(10);
CreatePeers({config});
Connect();
// Note: this time is completely arbitrary, to allow messages to be sent.
simulator_.RunFor(QuicTime::Delta::FromSeconds(10));
// After these calls, we should observe no new messages.
server_peer_->SetEnabled(false);
client_peer_->SetEnabled(false);
std::map<int32_t, int64_t> last_sent_by_client =
client_peer_->GetLastSequenceNumbers();
std::map<int32_t, int64_t> last_sent_by_server =
server_peer_->GetLastSequenceNumbers();
// Note: this time is completely arbitrary, to allow time for the peers to
// generate new messages after being disabled. The point of the test is that
// they should not do that.
simulator_.RunFor(QuicTime::Delta::FromSeconds(10));
// Messages sent prior to disabling the peers are eventually received.
EXPECT_TRUE(simulator_.RunUntilOrTimeout(
[this, last_sent_by_client, last_sent_by_server]() mutable -> bool {
return !client_peer_->received_messages().empty() &&
client_peer_->received_messages().back().frame.sequence_number ==
last_sent_by_server[1] &&
!server_peer_->received_messages().empty() &&
server_peer_->received_messages().back().frame.sequence_number ==
last_sent_by_client[1];
},
QuicTime::Delta::FromSeconds(60)));
}
} // namespace
} // namespace test
} // namespace quic