blob: 62038c62637e6a7082b1698032b2d65e291018b9 [file] [log] [blame]
// Copyright (c) 2017 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_data_source.h"
#include <utility>
#include <vector>
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
namespace quic {
namespace test {
namespace {
class FakeDelegate : public QuartcDataSource::Delegate {
public:
void OnDataProduced(const char* data, size_t length) override;
const std::vector<ParsedQuartcDataFrame>& frames() { return frames_; }
private:
std::vector<ParsedQuartcDataFrame> frames_;
};
void FakeDelegate::OnDataProduced(const char* data, size_t length) {
ParsedQuartcDataFrame frame;
QuicStringPiece message(data, length);
if (ParsedQuartcDataFrame::Parse(message, &frame)) {
frames_.push_back(frame);
} else {
QUIC_LOG(FATAL) << "Data source produced a frame it can't parse: "
<< message;
}
}
class QuartcDataSourceTest : public QuicTest {
protected:
QuartcDataSourceTest() : simulator_() {}
simulator::Simulator simulator_;
FakeDelegate delegate_;
std::unique_ptr<QuartcDataSource> source_;
};
TEST_F(QuartcDataSourceTest, ProducesFrameEveryInterval) {
QuartcDataSource::Config config;
config.frame_interval = QuicTime::Delta::FromMilliseconds(20);
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval);
EXPECT_EQ(delegate_.frames().size(), 1u);
simulator_.RunFor(config.frame_interval);
EXPECT_EQ(delegate_.frames().size(), 2u);
simulator_.RunFor(config.frame_interval * 20);
EXPECT_EQ(delegate_.frames().size(), 22u);
}
TEST_F(QuartcDataSourceTest, DoesNotProduceFramesUntilEnabled) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
simulator_.RunFor(config.frame_interval * 20);
EXPECT_EQ(delegate_.frames().size(), 0u);
// The first frame is produced immediately (but asynchronously) upon enabling
// the source.
source_->SetEnabled(true);
simulator_.RunFor(QuicTime::Delta::FromMicroseconds(1));
EXPECT_EQ(delegate_.frames().size(), 1u);
}
TEST_F(QuartcDataSourceTest, DisableAndEnable) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval * 20);
EXPECT_EQ(delegate_.frames().size(), 20u);
// No new frames while the source is disabled.
source_->SetEnabled(false);
simulator_.RunFor(config.frame_interval * 20);
EXPECT_EQ(delegate_.frames().size(), 20u);
// The first frame is produced immediately (but asynchronously) upon enabling
// the source.
source_->SetEnabled(true);
simulator_.RunFor(QuicTime::Delta::FromMicroseconds(1));
ASSERT_EQ(delegate_.frames().size(), 21u);
// The first frame after a pause should be no larger than previous frames.
EXPECT_EQ(delegate_.frames()[0].payload.size(),
delegate_.frames()[20].payload.size());
// The first frame after the pause should have a much later timestamp.
// Note that the previous frame (19) happens at the *start* of the 20th
// interval. Frame 20 would normally happen one interval later, but we've
// delayed it by an extra 20 intervals (for a total of 21 intervals later).
EXPECT_EQ(delegate_.frames()[20].send_time - delegate_.frames()[19].send_time,
21 * config.frame_interval);
}
TEST_F(QuartcDataSourceTest, EnablingTwiceDoesNotChangeSchedule) {
QuartcDataSource::Config config;
config.frame_interval = QuicTime::Delta::FromMilliseconds(20);
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
// The first frame is produced immediately (but asynchronously) upon enabling
// the source.
source_->SetEnabled(true);
simulator_.RunFor(QuicTime::Delta::FromMicroseconds(1));
EXPECT_EQ(delegate_.frames().size(), 1u);
// Enabling the source again does not re-schedule the alarm.
source_->SetEnabled(true);
simulator_.RunFor(QuicTime::Delta::FromMicroseconds(1));
EXPECT_EQ(delegate_.frames().size(), 1u);
// The second frame is sent at the expected interval after the first.
ASSERT_TRUE(
simulator_.RunUntil([this] { return delegate_.frames().size() == 2; }));
EXPECT_EQ(delegate_.frames()[1].send_time - delegate_.frames()[0].send_time,
config.frame_interval);
}
TEST_F(QuartcDataSourceTest, ProducesFramesWithConfiguredSourceId) {
QuartcDataSource::Config config;
config.id = 7;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].source_id, config.id);
}
TEST_F(QuartcDataSourceTest, ProducesFramesAtAllocatedBandwidth) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
constexpr QuicByteCount bytes_per_frame = 1000;
source_->AllocateBandwidth(QuicBandwidth::FromBytesAndTimeDelta(
bytes_per_frame, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].payload.size(),
bytes_per_frame - kDataFrameHeaderSize);
EXPECT_EQ(delegate_.frames()[0].size, bytes_per_frame);
}
TEST_F(QuartcDataSourceTest, ProducesParseableHeaderWhenNotEnoughBandwidth) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
// Allocate less bandwidth than the source requires for its header.
source_->AllocateBandwidth(QuicBandwidth::FromBytesAndTimeDelta(
kDataFrameHeaderSize - 10, config.frame_interval));
source_->SetEnabled(true);
QuicTime start_time = simulator_.GetClock()->Now();
simulator_.RunFor(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].payload.size(), 0u);
EXPECT_EQ(delegate_.frames()[0].size, kDataFrameHeaderSize);
// Header fields are still present and parseable.
EXPECT_EQ(delegate_.frames()[0].source_id, 0);
EXPECT_EQ(delegate_.frames()[0].sequence_number, 0);
EXPECT_EQ(delegate_.frames()[0].send_time, start_time);
}
TEST_F(QuartcDataSourceTest, ProducesSequenceNumbers) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval * 20);
ASSERT_EQ(delegate_.frames().size(), 20u);
for (int i = 0; i < 20; ++i) {
EXPECT_EQ(delegate_.frames()[i].sequence_number, i);
}
}
TEST_F(QuartcDataSourceTest, ProducesSendTimes) {
QuartcDataSource::Config config;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(1000, config.frame_interval));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval * 20);
ASSERT_EQ(delegate_.frames().size(), 20u);
QuicTime first_send_time = delegate_.frames()[0].send_time;
for (int i = 1; i < 20; ++i) {
EXPECT_EQ(delegate_.frames()[i].send_time,
first_send_time + i * config.frame_interval);
}
}
TEST_F(QuartcDataSourceTest, AllocateClampsToMin) {
QuartcDataSource::Config config;
config.min_bandwidth = QuicBandwidth::FromBitsPerSecond(8000);
config.frame_interval = QuicTime::Delta::FromMilliseconds(100);
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
// When allocating less than the minimum, there is nothing left over.
EXPECT_EQ(source_->AllocateBandwidth(QuicBandwidth::FromBitsPerSecond(6000)),
QuicBandwidth::Zero());
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval);
// The frames produced use min_bandwidth instead of the lower allocation.
QuicByteCount bytes_per_frame =
config.min_bandwidth.ToBytesPerPeriod(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].payload.size(),
bytes_per_frame - kDataFrameHeaderSize);
EXPECT_EQ(delegate_.frames()[0].size, bytes_per_frame);
}
TEST_F(QuartcDataSourceTest, AllocateClampsToMax) {
QuartcDataSource::Config config;
config.max_bandwidth = QuicBandwidth::FromBitsPerSecond(8000);
config.frame_interval = QuicTime::Delta::FromMilliseconds(100);
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
// When allocating more than the maximum, the excess is returned.
EXPECT_EQ(source_->AllocateBandwidth(QuicBandwidth::FromBitsPerSecond(10000)),
QuicBandwidth::FromBitsPerSecond(2000));
source_->SetEnabled(true);
simulator_.RunFor(config.frame_interval);
// The frames produced use max_bandwidth instead of the higher allocation.
QuicByteCount bytes_per_frame =
config.max_bandwidth.ToBytesPerPeriod(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].payload.size(),
bytes_per_frame - kDataFrameHeaderSize);
EXPECT_EQ(delegate_.frames()[0].size, bytes_per_frame);
}
TEST_F(QuartcDataSourceTest, MaxFrameSize) {
constexpr QuicByteCount bytes_per_frame = 1000;
QuartcDataSource::Config config;
config.max_frame_size = bytes_per_frame;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
// Allocate enough bandwidth for more than one frame per interval.
source_->AllocateBandwidth(QuicBandwidth::FromBytesAndTimeDelta(
3 * bytes_per_frame, config.frame_interval));
source_->SetEnabled(true);
QuicTime start_time = simulator_.GetClock()->Now();
simulator_.RunFor(config.frame_interval);
// Since there's enough bandwidth for three frames per interval, that's what
// the source should generate.
EXPECT_EQ(delegate_.frames().size(), 3u);
int i = 0;
for (const auto& frame : delegate_.frames()) {
// Each of the frames should start with a header that can be parsed.
// Each gets the same timestamp, but a different sequence number.
EXPECT_EQ(frame.source_id, config.id);
EXPECT_EQ(frame.sequence_number, i++);
EXPECT_EQ(frame.send_time, start_time);
// Each of the frames should have the configured maximum size.
EXPECT_EQ(frame.payload.size(), bytes_per_frame - kDataFrameHeaderSize);
EXPECT_EQ(frame.size, bytes_per_frame);
}
}
TEST_F(QuartcDataSourceTest, ProducesParseableHeaderWhenMaxFrameSizeTooSmall) {
QuartcDataSource::Config config;
config.max_frame_size = kDataFrameHeaderSize - 1;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(200, config.frame_interval));
source_->SetEnabled(true);
QuicTime start_time = simulator_.GetClock()->Now();
simulator_.RunFor(config.frame_interval);
ASSERT_GE(delegate_.frames().size(), 1u);
EXPECT_EQ(delegate_.frames()[0].payload.size(), 0u);
EXPECT_EQ(delegate_.frames()[0].size, kDataFrameHeaderSize);
// Header fields are still present and parseable.
EXPECT_EQ(delegate_.frames()[0].source_id, 0);
EXPECT_EQ(delegate_.frames()[0].sequence_number, 0);
EXPECT_EQ(delegate_.frames()[0].send_time, start_time);
}
TEST_F(QuartcDataSourceTest, ProducesParseableHeaderWhenLeftoverSizeTooSmall) {
QuartcDataSource::Config config;
config.max_frame_size = 200;
source_ = std::make_unique<QuartcDataSource>(
simulator_.GetClock(), simulator_.GetAlarmFactory(),
simulator_.GetRandomGenerator(), config, &delegate_);
// Allocate enough bandwidth to send a 200-byte frame and a 1-byte frame.
source_->AllocateBandwidth(
QuicBandwidth::FromBytesAndTimeDelta(201, config.frame_interval));
source_->SetEnabled(true);
QuicTime start_time = simulator_.GetClock()->Now();
simulator_.RunFor(config.frame_interval);
ASSERT_EQ(delegate_.frames().size(), 2u);
EXPECT_EQ(delegate_.frames()[0].payload.size(), 200u - kDataFrameHeaderSize);
EXPECT_EQ(delegate_.frames()[0].size, 200u);
// The second frame, using the 1 leftover byte from the first, rounds up to
// the minimum frame size (just the header and no payload).
EXPECT_EQ(delegate_.frames()[1].payload.size(), 0u);
EXPECT_EQ(delegate_.frames()[1].size, kDataFrameHeaderSize);
// Header fields are still present and parseable.
EXPECT_EQ(delegate_.frames()[1].source_id, 0);
EXPECT_EQ(delegate_.frames()[1].sequence_number, 1);
EXPECT_EQ(delegate_.frames()[1].send_time, start_time);
}
} // namespace
} // namespace test
} // namespace quic