| // 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 <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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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_ = QuicMakeUnique<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 |