blob: 2f632693349a0e4c6098fe87924c54b11c690f06 [file] [log] [blame]
// Copyright 2014 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_write_blocked_list.h"
#include <optional>
#include <tuple>
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
using spdy::kV3HighestPriority;
using spdy::kV3LowestPriority;
namespace quic {
namespace test {
namespace {
constexpr bool kStatic = true;
constexpr bool kNotStatic = false;
constexpr bool kIncremental = true;
constexpr bool kNotIncremental = false;
class QuicWriteBlockedListTest : public QuicTest {
protected:
void SetUp() override {
// Delay construction of QuicWriteBlockedList object to allow constructor of
// derived test classes to manipulate reloadable flags that are latched in
// QuicWriteBlockedList constructor.
write_blocked_list_.emplace();
}
bool HasWriteBlockedDataStreams() const {
return write_blocked_list_->HasWriteBlockedDataStreams();
}
bool HasWriteBlockedSpecialStream() const {
return write_blocked_list_->HasWriteBlockedSpecialStream();
}
size_t NumBlockedSpecialStreams() const {
return write_blocked_list_->NumBlockedSpecialStreams();
}
size_t NumBlockedStreams() const {
return write_blocked_list_->NumBlockedStreams();
}
bool ShouldYield(QuicStreamId id) const {
return write_blocked_list_->ShouldYield(id);
}
QuicStreamPriority GetPriorityOfStream(QuicStreamId id) const {
return write_blocked_list_->GetPriorityOfStream(id);
}
QuicStreamId PopFront() { return write_blocked_list_->PopFront(); }
void RegisterStream(QuicStreamId stream_id, bool is_static_stream,
const HttpStreamPriority& priority) {
write_blocked_list_->RegisterStream(stream_id, is_static_stream,
QuicStreamPriority(priority));
}
void UnregisterStream(QuicStreamId stream_id) {
write_blocked_list_->UnregisterStream(stream_id);
}
void UpdateStreamPriority(QuicStreamId stream_id,
const HttpStreamPriority& new_priority) {
write_blocked_list_->UpdateStreamPriority(stream_id,
QuicStreamPriority(new_priority));
}
void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) {
write_blocked_list_->UpdateBytesForStream(stream_id, bytes);
}
void AddStream(QuicStreamId stream_id) {
write_blocked_list_->AddStream(stream_id);
}
bool IsStreamBlocked(QuicStreamId stream_id) const {
return write_blocked_list_->IsStreamBlocked(stream_id);
}
private:
std::optional<QuicWriteBlockedList> write_blocked_list_;
};
TEST_F(QuicWriteBlockedListTest, PriorityOrder) {
// Mark streams blocked in roughly reverse priority order, and
// verify that streams are sorted.
RegisterStream(40, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(23, kNotStatic, {kV3HighestPriority, kIncremental});
RegisterStream(17, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(1, kStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental});
EXPECT_EQ(kV3LowestPriority, GetPriorityOfStream(40).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(40).http().incremental);
EXPECT_EQ(kV3HighestPriority, GetPriorityOfStream(23).http().urgency);
EXPECT_EQ(kIncremental, GetPriorityOfStream(23).http().incremental);
EXPECT_EQ(kV3HighestPriority, GetPriorityOfStream(17).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(17).http().incremental);
AddStream(40);
EXPECT_TRUE(IsStreamBlocked(40));
AddStream(23);
EXPECT_TRUE(IsStreamBlocked(23));
AddStream(17);
EXPECT_TRUE(IsStreamBlocked(17));
AddStream(3);
EXPECT_TRUE(IsStreamBlocked(3));
AddStream(1);
EXPECT_TRUE(IsStreamBlocked(1));
EXPECT_EQ(5u, NumBlockedStreams());
EXPECT_TRUE(HasWriteBlockedSpecialStream());
EXPECT_EQ(2u, NumBlockedSpecialStreams());
EXPECT_TRUE(HasWriteBlockedDataStreams());
// Static streams are highest priority, regardless of priority value.
EXPECT_EQ(1u, PopFront());
EXPECT_EQ(1u, NumBlockedSpecialStreams());
EXPECT_FALSE(IsStreamBlocked(1));
EXPECT_EQ(3u, PopFront());
EXPECT_EQ(0u, NumBlockedSpecialStreams());
EXPECT_FALSE(IsStreamBlocked(3));
// Streams with same priority are popped in the order they were inserted.
EXPECT_EQ(23u, PopFront());
EXPECT_FALSE(IsStreamBlocked(23));
EXPECT_EQ(17u, PopFront());
EXPECT_FALSE(IsStreamBlocked(17));
// Low priority stream appears last.
EXPECT_EQ(40u, PopFront());
EXPECT_FALSE(IsStreamBlocked(40));
EXPECT_EQ(0u, NumBlockedStreams());
EXPECT_FALSE(HasWriteBlockedSpecialStream());
EXPECT_FALSE(HasWriteBlockedDataStreams());
}
TEST_F(QuicWriteBlockedListTest, SingleStaticStream) {
RegisterStream(5, kStatic, {kV3HighestPriority, kNotIncremental});
AddStream(5);
EXPECT_EQ(1u, NumBlockedStreams());
EXPECT_TRUE(HasWriteBlockedSpecialStream());
EXPECT_EQ(5u, PopFront());
EXPECT_EQ(0u, NumBlockedStreams());
EXPECT_FALSE(HasWriteBlockedSpecialStream());
}
TEST_F(QuicWriteBlockedListTest, StaticStreamsComeFirst) {
RegisterStream(5, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(3, kStatic, {kV3LowestPriority, kNotIncremental});
AddStream(5);
AddStream(3);
EXPECT_EQ(2u, NumBlockedStreams());
EXPECT_TRUE(HasWriteBlockedSpecialStream());
EXPECT_TRUE(HasWriteBlockedDataStreams());
EXPECT_EQ(3u, PopFront());
EXPECT_EQ(5u, PopFront());
EXPECT_EQ(0u, NumBlockedStreams());
EXPECT_FALSE(HasWriteBlockedSpecialStream());
EXPECT_FALSE(HasWriteBlockedDataStreams());
}
TEST_F(QuicWriteBlockedListTest, NoDuplicateEntries) {
// Test that QuicWriteBlockedList doesn't allow duplicate entries.
// Try to add a stream to the write blocked list multiple times at the same
// priority.
const QuicStreamId kBlockedId = 5;
RegisterStream(kBlockedId, kNotStatic, {kV3HighestPriority, kNotIncremental});
AddStream(kBlockedId);
AddStream(kBlockedId);
AddStream(kBlockedId);
// This should only result in one blocked stream being added.
EXPECT_EQ(1u, NumBlockedStreams());
EXPECT_TRUE(HasWriteBlockedDataStreams());
// There should only be one stream to pop off the front.
EXPECT_EQ(kBlockedId, PopFront());
EXPECT_EQ(0u, NumBlockedStreams());
EXPECT_FALSE(HasWriteBlockedDataStreams());
}
TEST_F(QuicWriteBlockedListTest, IncrementalStreamsRoundRobin) {
const QuicStreamId id1 = 5;
const QuicStreamId id2 = 7;
const QuicStreamId id3 = 9;
RegisterStream(id1, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id2, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id3, kNotStatic, {kV3LowestPriority, kIncremental});
AddStream(id1);
AddStream(id2);
AddStream(id3);
EXPECT_EQ(id1, PopFront());
const size_t kLargeWriteSize = 1000 * 1000 * 1000;
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
EXPECT_EQ(id3, PopFront());
UpdateBytesForStream(id3, kLargeWriteSize);
AddStream(id3);
AddStream(id2);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
EXPECT_EQ(id3, PopFront());
UpdateBytesForStream(id3, kLargeWriteSize);
AddStream(id3);
EXPECT_EQ(id2, PopFront());
EXPECT_EQ(id3, PopFront());
}
class QuicWriteBlockedListParameterizedTest
: public QuicWriteBlockedListTest,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
protected:
QuicWriteBlockedListParameterizedTest()
: priority_respect_incremental_(std::get<0>(GetParam())),
disable_batch_write_(std::get<1>(GetParam())) {
SetQuicReloadableFlag(quic_priority_respect_incremental,
priority_respect_incremental_);
SetQuicReloadableFlag(quic_disable_batch_write, disable_batch_write_);
}
const bool priority_respect_incremental_;
const bool disable_batch_write_;
};
INSTANTIATE_TEST_SUITE_P(
BatchWrite, QuicWriteBlockedListParameterizedTest,
::testing::Combine(::testing::Bool(), ::testing::Bool()),
[](const testing::TestParamInfo<
QuicWriteBlockedListParameterizedTest::ParamType>& info) {
return absl::StrCat(std::get<0>(info.param) ? "RespectIncrementalTrue"
: "RespectIncrementalFalse",
std::get<1>(info.param) ? "DisableBatchWriteTrue"
: "DisableBatchWriteFalse");
});
// If reloadable_flag_quic_disable_batch_write is false, writes are batched.
TEST_P(QuicWriteBlockedListParameterizedTest, BatchingWrites) {
if (disable_batch_write_) {
return;
}
const QuicStreamId id1 = 5;
const QuicStreamId id2 = 7;
const QuicStreamId id3 = 9;
RegisterStream(id1, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id2, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id3, kNotStatic, {kV3HighestPriority, kIncremental});
AddStream(id1);
AddStream(id2);
EXPECT_EQ(2u, NumBlockedStreams());
// The first stream we push back should stay at the front until 16k is
// written.
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, 15999);
AddStream(id1);
EXPECT_EQ(2u, NumBlockedStreams());
EXPECT_EQ(id1, PopFront());
// Once 16k is written the first stream will yield to the next.
UpdateBytesForStream(id1, 1);
AddStream(id1);
EXPECT_EQ(2u, NumBlockedStreams());
EXPECT_EQ(id2, PopFront());
// Set the new stream to have written all but one byte.
UpdateBytesForStream(id2, 15999);
AddStream(id2);
EXPECT_EQ(2u, NumBlockedStreams());
// Ensure higher priority streams are popped first.
AddStream(id3);
EXPECT_EQ(id3, PopFront());
// Higher priority streams will always be popped first, even if using their
// byte quota
UpdateBytesForStream(id3, 20000);
AddStream(id3);
EXPECT_EQ(id3, PopFront());
// Once the higher priority stream is out of the way, id2 will resume its 16k
// write, with only 1 byte remaining of its guaranteed write allocation.
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, 1);
AddStream(id2);
EXPECT_EQ(2u, NumBlockedStreams());
EXPECT_EQ(id1, PopFront());
}
// If reloadable_flag_quic_disable_batch_write is true, writes are performed
// round-robin regardless of how little data is written on each stream.
TEST_P(QuicWriteBlockedListParameterizedTest, RoundRobin) {
if (!disable_batch_write_) {
return;
}
const QuicStreamId id1 = 5;
const QuicStreamId id2 = 7;
const QuicStreamId id3 = 9;
RegisterStream(id1, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id2, kNotStatic, {kV3LowestPriority, kIncremental});
RegisterStream(id3, kNotStatic, {kV3LowestPriority, kIncremental});
AddStream(id1);
AddStream(id2);
AddStream(id3);
EXPECT_EQ(id1, PopFront());
AddStream(id1);
EXPECT_EQ(id2, PopFront());
EXPECT_EQ(id3, PopFront());
AddStream(id3);
AddStream(id2);
EXPECT_EQ(id1, PopFront());
EXPECT_EQ(id3, PopFront());
AddStream(id3);
EXPECT_EQ(id2, PopFront());
EXPECT_EQ(id3, PopFront());
}
TEST_P(QuicWriteBlockedListParameterizedTest,
NonIncrementalStreamsKeepWriting) {
if (!priority_respect_incremental_) {
return;
}
const QuicStreamId id1 = 1;
const QuicStreamId id2 = 2;
const QuicStreamId id3 = 3;
const QuicStreamId id4 = 4;
RegisterStream(id1, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(id2, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(id3, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(id4, kNotStatic, {kV3HighestPriority, kNotIncremental});
AddStream(id1);
AddStream(id2);
AddStream(id3);
// A non-incremental stream can continue writing as long as it has data.
EXPECT_EQ(id1, PopFront());
const size_t kLargeWriteSize = 1000 * 1000 * 1000;
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
// A higher priority stream takes precedence.
AddStream(id4);
EXPECT_EQ(id4, PopFront());
// When it is the turn of the lower urgency bucket again, writing of the first
// stream will continue.
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
// When there is no more data on the first stream, write can start on the
// second stream.
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
AddStream(id2);
// Write continues without limit.
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
AddStream(id2);
// Stream 1 is not the most recently written one, therefore it gets to the end
// of the dequeue.
AddStream(id1);
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
EXPECT_EQ(id3, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
AddStream(id3);
EXPECT_EQ(id3, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
// When there is no data to write either on stream 2 or stream 3, stream 1 can
// resume.
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
}
TEST_P(QuicWriteBlockedListParameterizedTest,
IncrementalAndNonIncrementalStreams) {
if (!priority_respect_incremental_) {
return;
}
const QuicStreamId id1 = 1;
const QuicStreamId id2 = 2;
RegisterStream(id1, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(id2, kNotStatic, {kV3LowestPriority, kIncremental});
AddStream(id1);
AddStream(id2);
// A non-incremental stream can continue writing as long as it has data.
EXPECT_EQ(id1, PopFront());
const size_t kSmallWriteSize = 1000;
UpdateBytesForStream(id1, kSmallWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kSmallWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kSmallWriteSize);
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kSmallWriteSize);
AddStream(id2);
AddStream(id1);
if (!disable_batch_write_) {
// Small writes do not exceed the batch limit.
// Writes continue even on an incremental stream.
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kSmallWriteSize);
AddStream(id2);
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kSmallWriteSize);
}
EXPECT_EQ(id1, PopFront());
const size_t kLargeWriteSize = 1000 * 1000 * 1000;
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id1);
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
AddStream(id2);
AddStream(id1);
// When batch writing is disabled, stream 2 immediately yields to stream 1,
// which is the non-incremental stream with most recent writes.
// When batch writing is enabled, stream 2 only yields to stream 1 after
// exceeding the batching limit.
if (!disable_batch_write_) {
EXPECT_EQ(id2, PopFront());
UpdateBytesForStream(id2, kLargeWriteSize);
AddStream(id2);
}
EXPECT_EQ(id1, PopFront());
UpdateBytesForStream(id1, kLargeWriteSize);
}
TEST_F(QuicWriteBlockedListTest, Ceding) {
RegisterStream(15, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(16, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(5, kNotStatic, {5, kNotIncremental});
RegisterStream(4, kNotStatic, {5, kNotIncremental});
RegisterStream(7, kNotStatic, {7, kNotIncremental});
RegisterStream(1, kStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental});
// When nothing is on the list, nothing yields.
EXPECT_FALSE(ShouldYield(5));
AddStream(5);
// 5 should not yield to itself.
EXPECT_FALSE(ShouldYield(5));
// 4 and 7 are equal or lower priority and should yield to 5.
EXPECT_TRUE(ShouldYield(4));
EXPECT_TRUE(ShouldYield(7));
// Stream 15 and static streams should preempt 5.
EXPECT_FALSE(ShouldYield(15));
EXPECT_FALSE(ShouldYield(3));
EXPECT_FALSE(ShouldYield(1));
// Block a high priority stream.
AddStream(15);
// 16 should yield (same priority) but static streams will still not.
EXPECT_TRUE(ShouldYield(16));
EXPECT_FALSE(ShouldYield(3));
EXPECT_FALSE(ShouldYield(1));
// Block a static stream. All non-static streams should yield.
AddStream(3);
EXPECT_TRUE(ShouldYield(16));
EXPECT_TRUE(ShouldYield(15));
EXPECT_FALSE(ShouldYield(3));
EXPECT_FALSE(ShouldYield(1));
// Block the other static stream. All other streams should yield.
AddStream(1);
EXPECT_TRUE(ShouldYield(16));
EXPECT_TRUE(ShouldYield(15));
EXPECT_TRUE(ShouldYield(3));
EXPECT_FALSE(ShouldYield(1));
}
TEST_F(QuicWriteBlockedListTest, UnregisterStream) {
RegisterStream(40, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(23, kNotStatic, {6, kNotIncremental});
RegisterStream(12, kNotStatic, {3, kNotIncremental});
RegisterStream(17, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(1, kStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental});
AddStream(40);
AddStream(23);
AddStream(12);
AddStream(17);
AddStream(1);
AddStream(3);
UnregisterStream(23);
UnregisterStream(1);
EXPECT_EQ(3u, PopFront());
EXPECT_EQ(17u, PopFront());
EXPECT_EQ(12u, PopFront());
EXPECT_EQ(40, PopFront());
}
TEST_F(QuicWriteBlockedListTest, UnregisterNotRegisteredStream) {
EXPECT_QUICHE_BUG(UnregisterStream(1), "Stream 1 not registered");
RegisterStream(2, kNotStatic, {kV3HighestPriority, kIncremental});
UnregisterStream(2);
EXPECT_QUICHE_BUG(UnregisterStream(2), "Stream 2 not registered");
}
TEST_F(QuicWriteBlockedListTest, UpdateStreamPriority) {
RegisterStream(40, kNotStatic, {kV3LowestPriority, kNotIncremental});
RegisterStream(23, kNotStatic, {6, kIncremental});
RegisterStream(17, kNotStatic, {kV3HighestPriority, kNotIncremental});
RegisterStream(1, kStatic, {2, kNotIncremental});
RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental});
EXPECT_EQ(kV3LowestPriority, GetPriorityOfStream(40).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(40).http().incremental);
EXPECT_EQ(6, GetPriorityOfStream(23).http().urgency);
EXPECT_EQ(kIncremental, GetPriorityOfStream(23).http().incremental);
EXPECT_EQ(kV3HighestPriority, GetPriorityOfStream(17).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(17).http().incremental);
UpdateStreamPriority(40, {3, kIncremental});
UpdateStreamPriority(23, {kV3HighestPriority, kNotIncremental});
UpdateStreamPriority(17, {5, kNotIncremental});
EXPECT_EQ(3, GetPriorityOfStream(40).http().urgency);
EXPECT_EQ(kIncremental, GetPriorityOfStream(40).http().incremental);
EXPECT_EQ(kV3HighestPriority, GetPriorityOfStream(23).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(23).http().incremental);
EXPECT_EQ(5, GetPriorityOfStream(17).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(17).http().incremental);
AddStream(40);
AddStream(23);
AddStream(17);
AddStream(1);
AddStream(3);
EXPECT_EQ(1u, PopFront());
EXPECT_EQ(3u, PopFront());
EXPECT_EQ(23u, PopFront());
EXPECT_EQ(40u, PopFront());
EXPECT_EQ(17u, PopFront());
}
// UpdateStreamPriority() must not be called for static streams.
TEST_F(QuicWriteBlockedListTest, UpdateStaticStreamPriority) {
RegisterStream(2, kStatic, {kV3LowestPriority, kNotIncremental});
EXPECT_QUICHE_DEBUG_DEATH(
UpdateStreamPriority(2, {kV3HighestPriority, kNotIncremental}),
"IsRegistered");
}
TEST_F(QuicWriteBlockedListTest, UpdateStreamPrioritySameUrgency) {
// Streams with same urgency are returned by PopFront() in the order they were
// added by AddStream().
RegisterStream(1, kNotStatic, {6, kNotIncremental});
RegisterStream(2, kNotStatic, {6, kNotIncremental});
AddStream(1);
AddStream(2);
EXPECT_EQ(1u, PopFront());
EXPECT_EQ(2u, PopFront());
// Calling UpdateStreamPriority() on the first stream does not change the
// order.
RegisterStream(3, kNotStatic, {6, kNotIncremental});
RegisterStream(4, kNotStatic, {6, kNotIncremental});
EXPECT_EQ(6, GetPriorityOfStream(3).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(3).http().incremental);
UpdateStreamPriority(3, {6, kIncremental});
EXPECT_EQ(6, GetPriorityOfStream(3).http().urgency);
EXPECT_EQ(kIncremental, GetPriorityOfStream(3).http().incremental);
AddStream(3);
AddStream(4);
EXPECT_EQ(3u, PopFront());
EXPECT_EQ(4u, PopFront());
// Calling UpdateStreamPriority() on the second stream does not change the
// order.
RegisterStream(5, kNotStatic, {6, kIncremental});
RegisterStream(6, kNotStatic, {6, kIncremental});
EXPECT_EQ(6, GetPriorityOfStream(6).http().urgency);
EXPECT_EQ(kIncremental, GetPriorityOfStream(6).http().incremental);
UpdateStreamPriority(6, {6, kNotIncremental});
EXPECT_EQ(6, GetPriorityOfStream(6).http().urgency);
EXPECT_EQ(kNotIncremental, GetPriorityOfStream(6).http().incremental);
AddStream(5);
AddStream(6);
EXPECT_EQ(5u, PopFront());
EXPECT_EQ(6u, PopFront());
}
} // namespace
} // namespace test
} // namespace quic