Update QuicWriteBlockedList tests and comments. Add kStatic and kNonStatic for expressiveness and also for consistency with kNonIncremental (and future kIncremental). Make QuicWriteBlockedListTest not inherit from QuicTestWithParam<>, as the tests are not actually parametrized. Add QuicWriteBlockedListTest wrapper methods for all `write_blocked_list_` public methods to make tests more concise. Remove QuicStreamPriority type name from argument calls to make tests even more concise. These two together allow repeated RegisterStream() calls to fit on a single line each, making tests easier to read. Add tests and augment existsing tests to cover previously not exercised methods: GetSpdyPriorityOfStream(), UpdateStreamPriority(), UnregisterStream(). Remove references to crypto stream and headers stream in comments, as these only exists in later versions of gQUIC, not in HTTP/3. Use "static stream" instead, which is not an HTTP/3 concept, but used widely in QUICHE source to refer to streams that are essential (in the sense that closure is a connection error). PiperOrigin-RevId: 502864670
diff --git a/quiche/quic/core/quic_write_blocked_list.h b/quiche/quic/core/quic_write_blocked_list.h index f0f86a8..3a8fc6c 100644 --- a/quiche/quic/core/quic_write_blocked_list.h +++ b/quiche/quic/core/quic_write_blocked_list.h
@@ -20,9 +20,10 @@ namespace quic { -// Keeps tracks of the QUIC streams that have data to write, sorted by -// priority. QUIC stream priority order is: -// Crypto stream > Headers stream > Data streams by requested priority. +// Keeps tracks of the order of QUIC streams that have data to write. +// Static streams come first, in the order they were registered with +// QuicWriteBlockedList. They are followed by non-static streams, ordered by +// priority. class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { public: explicit QuicWriteBlockedList(); @@ -53,16 +54,19 @@ return priority_write_scheduler_.GetStreamPrecedence(id).spdy3_priority(); } - // Pops the highest priority stream, special casing crypto and headers - // streams. Latches the most recently popped data stream for batch writing - // purposes. + // Pops the highest priority stream, special casing static streams. Latches + // the most recently popped data stream for batch writing purposes. QuicStreamId PopFront(); + // Register a stream with given priority. + // `priority` is ignored for static streams. void RegisterStream(QuicStreamId stream_id, bool is_static_stream, const QuicStreamPriority& priority); void UnregisterStream(QuicStreamId stream_id, bool is_static); + // Updates the stored priority of a stream. Must not be called for static + // streams. void UpdateStreamPriority(QuicStreamId stream_id, const QuicStreamPriority& new_priority); @@ -71,7 +75,8 @@ // Pushes a stream to the back of the list for its priority level *unless* it // is latched for doing batched writes in which case it goes to the front of // the list for its priority level. - // Headers and crypto streams are special cased to always resume first. + // Static streams are special cased to always resume first. + // Stream must already be registered. void AddStream(QuicStreamId stream_id); // Returns true if stream with |stream_id| is write blocked.
diff --git a/quiche/quic/core/quic_write_blocked_list_test.cc b/quiche/quic/core/quic_write_blocked_list_test.cc index c04b891..2acaa35 100644 --- a/quiche/quic/core/quic_write_blocked_list_test.cc +++ b/quiche/quic/core/quic_write_blocked_list_test.cc
@@ -7,7 +7,6 @@ #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/quic_test_utils.h" -using spdy::kHttp2DefaultStreamWeight; using spdy::kV3HighestPriority; using spdy::kV3LowestPriority; @@ -15,257 +14,368 @@ namespace test { namespace { +constexpr bool kStatic = true; +constexpr bool kNotStatic = false; + // Default value for `incremental`, see RFC9218. // TODO(b/147306124): Add support and tests for `incremental` being true. constexpr bool kNotIncremental = false; -class QuicWriteBlockedListTest : public QuicTestWithParam<bool> { +class QuicWriteBlockedListTest : public QuicTest { protected: + 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); + } + + spdy::SpdyPriority GetSpdyPriorityofStream(QuicStreamId id) const { + return write_blocked_list_.GetSpdyPriorityofStream(id); + } + + QuicStreamId PopFront() { return write_blocked_list_.PopFront(); } + + void RegisterStream(QuicStreamId stream_id, bool is_static_stream, + const QuicStreamPriority& priority) { + write_blocked_list_.RegisterStream(stream_id, is_static_stream, priority); + } + + void UnregisterStream(QuicStreamId stream_id, bool is_static) { + write_blocked_list_.UnregisterStream(stream_id, is_static); + } + + void UpdateStreamPriority(QuicStreamId stream_id, + const QuicStreamPriority& new_priority) { + write_blocked_list_.UpdateStreamPriority(stream_id, 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: QuicWriteBlockedList write_blocked_list_; }; TEST_F(QuicWriteBlockedListTest, PriorityOrder) { - /* - 0 - | - 23 - | - 17 - | - 40 - */ // Mark streams blocked in roughly reverse priority order, and // verify that streams are sorted. - write_blocked_list_.RegisterStream( - 40, false, QuicStreamPriority{kV3LowestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 23, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 17, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 1, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 3, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); + RegisterStream(40, kNotStatic, {kV3LowestPriority, kNotIncremental}); + RegisterStream(23, kNotStatic, {kV3HighestPriority, kNotIncremental}); + RegisterStream(17, kNotStatic, {kV3HighestPriority, kNotIncremental}); + RegisterStream(1, kStatic, {kV3HighestPriority, kNotIncremental}); + RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(40); - EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(40)); - write_blocked_list_.AddStream(23); - EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(23)); - write_blocked_list_.AddStream(17); - EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(17)); - write_blocked_list_.AddStream(3); - EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(3)); - write_blocked_list_.AddStream(1); - EXPECT_TRUE(write_blocked_list_.IsStreamBlocked(1)); + EXPECT_EQ(kV3LowestPriority, GetSpdyPriorityofStream(40)); + EXPECT_EQ(kV3HighestPriority, GetSpdyPriorityofStream(23)); + EXPECT_EQ(kV3HighestPriority, GetSpdyPriorityofStream(17)); - EXPECT_EQ(5u, write_blocked_list_.NumBlockedStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedSpecialStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedDataStreams()); - // The Crypto stream is highest priority. - EXPECT_EQ(1u, write_blocked_list_.PopFront()); - EXPECT_EQ(1u, write_blocked_list_.NumBlockedSpecialStreams()); - EXPECT_FALSE(write_blocked_list_.IsStreamBlocked(1)); - // Followed by the Headers stream. - EXPECT_EQ(3u, write_blocked_list_.PopFront()); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedSpecialStreams()); - EXPECT_FALSE(write_blocked_list_.IsStreamBlocked(3)); + 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, write_blocked_list_.PopFront()); - EXPECT_FALSE(write_blocked_list_.IsStreamBlocked(23)); - EXPECT_EQ(17u, write_blocked_list_.PopFront()); - EXPECT_FALSE(write_blocked_list_.IsStreamBlocked(17)); + EXPECT_EQ(23u, PopFront()); + EXPECT_FALSE(IsStreamBlocked(23)); + EXPECT_EQ(17u, PopFront()); + EXPECT_FALSE(IsStreamBlocked(17)); + // Low priority stream appears last. - EXPECT_EQ(40u, write_blocked_list_.PopFront()); - EXPECT_FALSE(write_blocked_list_.IsStreamBlocked(40)); + EXPECT_EQ(40u, PopFront()); + EXPECT_FALSE(IsStreamBlocked(40)); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedStreams()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); + EXPECT_EQ(0u, NumBlockedStreams()); + EXPECT_FALSE(HasWriteBlockedSpecialStream()); + EXPECT_FALSE(HasWriteBlockedDataStreams()); } -TEST_F(QuicWriteBlockedListTest, CryptoStream) { - write_blocked_list_.RegisterStream( - 1, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(1); +TEST_F(QuicWriteBlockedListTest, SingleStaticStream) { + RegisterStream(5, kStatic, {kV3HighestPriority, kNotIncremental}); + AddStream(5); - EXPECT_EQ(1u, write_blocked_list_.NumBlockedStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_EQ(1u, write_blocked_list_.PopFront()); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedStreams()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); + EXPECT_EQ(1u, NumBlockedStreams()); + EXPECT_TRUE(HasWriteBlockedSpecialStream()); + EXPECT_EQ(5u, PopFront()); + EXPECT_EQ(0u, NumBlockedStreams()); + EXPECT_FALSE(HasWriteBlockedSpecialStream()); } -TEST_F(QuicWriteBlockedListTest, HeadersStream) { - write_blocked_list_.RegisterStream( - 3, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(3); +TEST_F(QuicWriteBlockedListTest, StaticStreamsComeFirst) { + RegisterStream(5, kNotStatic, {kV3HighestPriority, kNotIncremental}); + RegisterStream(3, kStatic, {kV3LowestPriority, kNotIncremental}); + AddStream(5); + AddStream(3); - EXPECT_EQ(1u, write_blocked_list_.NumBlockedStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_EQ(3u, write_blocked_list_.PopFront()); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedStreams()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); -} + EXPECT_EQ(2u, NumBlockedStreams()); + EXPECT_TRUE(HasWriteBlockedSpecialStream()); + EXPECT_TRUE(HasWriteBlockedDataStreams()); -TEST_F(QuicWriteBlockedListTest, VerifyHeadersStream) { - write_blocked_list_.RegisterStream( - 5, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 3, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(5); - write_blocked_list_.AddStream(3); + EXPECT_EQ(3u, PopFront()); + EXPECT_EQ(5u, PopFront()); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedDataStreams()); - // In newer QUIC versions, there is a headers stream which is - // higher priority than data streams. - EXPECT_EQ(3u, write_blocked_list_.PopFront()); - EXPECT_EQ(5u, write_blocked_list_.PopFront()); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedStreams()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedSpecialStream()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); + 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 = 3 + 2; - write_blocked_list_.RegisterStream( - kBlockedId, false, - QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(kBlockedId); - write_blocked_list_.AddStream(kBlockedId); - write_blocked_list_.AddStream(kBlockedId); + 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, write_blocked_list_.NumBlockedStreams()); - EXPECT_TRUE(write_blocked_list_.HasWriteBlockedDataStreams()); + EXPECT_EQ(1u, NumBlockedStreams()); + EXPECT_TRUE(HasWriteBlockedDataStreams()); // There should only be one stream to pop off the front. - EXPECT_EQ(kBlockedId, write_blocked_list_.PopFront()); - EXPECT_EQ(0u, write_blocked_list_.NumBlockedStreams()); - EXPECT_FALSE(write_blocked_list_.HasWriteBlockedDataStreams()); + EXPECT_EQ(kBlockedId, PopFront()); + EXPECT_EQ(0u, NumBlockedStreams()); + EXPECT_FALSE(HasWriteBlockedDataStreams()); } TEST_F(QuicWriteBlockedListTest, BatchingWrites) { - const QuicStreamId id1 = 3 + 2; - const QuicStreamId id2 = id1 + 2; - const QuicStreamId id3 = id2 + 2; - write_blocked_list_.RegisterStream( - id1, false, QuicStreamPriority{kV3LowestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - id2, false, QuicStreamPriority{kV3LowestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - id3, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); + const QuicStreamId id1 = 5; + const QuicStreamId id2 = 7; + const QuicStreamId id3 = 9; + RegisterStream(id1, kNotStatic, {kV3LowestPriority, kNotIncremental}); + RegisterStream(id2, kNotStatic, {kV3LowestPriority, kNotIncremental}); + RegisterStream(id3, kNotStatic, {kV3HighestPriority, kNotIncremental}); - write_blocked_list_.AddStream(id1); - write_blocked_list_.AddStream(id2); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); + 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, write_blocked_list_.PopFront()); - write_blocked_list_.UpdateBytesForStream(id1, 15999); - write_blocked_list_.AddStream(id1); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); - EXPECT_EQ(id1, write_blocked_list_.PopFront()); + 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. - write_blocked_list_.UpdateBytesForStream(id1, 1); - write_blocked_list_.AddStream(id1); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); - EXPECT_EQ(id2, write_blocked_list_.PopFront()); + UpdateBytesForStream(id1, 1); + AddStream(id1); + EXPECT_EQ(2u, NumBlockedStreams()); + EXPECT_EQ(id2, PopFront()); // Set the new stream to have written all but one byte. - write_blocked_list_.UpdateBytesForStream(id2, 15999); - write_blocked_list_.AddStream(id2); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); + UpdateBytesForStream(id2, 15999); + AddStream(id2); + EXPECT_EQ(2u, NumBlockedStreams()); // Ensure higher priority streams are popped first. - write_blocked_list_.AddStream(id3); - EXPECT_EQ(id3, write_blocked_list_.PopFront()); + AddStream(id3); + EXPECT_EQ(id3, PopFront()); // Higher priority streams will always be popped first, even if using their // byte quota - write_blocked_list_.UpdateBytesForStream(id3, 20000); - write_blocked_list_.AddStream(id3); - EXPECT_EQ(id3, write_blocked_list_.PopFront()); + 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, write_blocked_list_.PopFront()); - write_blocked_list_.UpdateBytesForStream(id2, 1); - write_blocked_list_.AddStream(id2); - EXPECT_EQ(2u, write_blocked_list_.NumBlockedStreams()); - EXPECT_EQ(id1, write_blocked_list_.PopFront()); + EXPECT_EQ(id2, PopFront()); + UpdateBytesForStream(id2, 1); + AddStream(id2); + EXPECT_EQ(2u, NumBlockedStreams()); + EXPECT_EQ(id1, PopFront()); } TEST_F(QuicWriteBlockedListTest, Ceding) { - /* - 0 - | - 15 - | - 16 - | - 5 - | - 4 - | - 7 - */ - write_blocked_list_.RegisterStream( - 15, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 16, false, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream(5, false, - QuicStreamPriority{5, kNotIncremental}); - write_blocked_list_.RegisterStream(4, false, - QuicStreamPriority{5, kNotIncremental}); - write_blocked_list_.RegisterStream(7, false, - QuicStreamPriority{7, kNotIncremental}); - write_blocked_list_.RegisterStream( - 1, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); - write_blocked_list_.RegisterStream( - 3, true, QuicStreamPriority{kV3HighestPriority, kNotIncremental}); + 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(write_blocked_list_.ShouldYield(5)); + EXPECT_FALSE(ShouldYield(5)); - write_blocked_list_.AddStream(5); + AddStream(5); // 5 should not yield to itself. - EXPECT_FALSE(write_blocked_list_.ShouldYield(5)); + EXPECT_FALSE(ShouldYield(5)); // 4 and 7 are equal or lower priority and should yield to 5. - EXPECT_TRUE(write_blocked_list_.ShouldYield(4)); - EXPECT_TRUE(write_blocked_list_.ShouldYield(7)); - // 15, headers and crypto should preempt 5. - EXPECT_FALSE(write_blocked_list_.ShouldYield(15)); - EXPECT_FALSE(write_blocked_list_.ShouldYield(3)); - EXPECT_FALSE(write_blocked_list_.ShouldYield(1)); + 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. - write_blocked_list_.AddStream(15); - // 16 should yield (same priority) but headers and crypto will still not. - EXPECT_TRUE(write_blocked_list_.ShouldYield(16)); - EXPECT_FALSE(write_blocked_list_.ShouldYield(3)); - EXPECT_FALSE(write_blocked_list_.ShouldYield(1)); + 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 the headers stream. All streams but crypto and headers should yield. - write_blocked_list_.AddStream(3); - EXPECT_TRUE(write_blocked_list_.ShouldYield(16)); - EXPECT_TRUE(write_blocked_list_.ShouldYield(15)); - EXPECT_FALSE(write_blocked_list_.ShouldYield(3)); - EXPECT_FALSE(write_blocked_list_.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 crypto stream. All streams but crypto should yield. - write_blocked_list_.AddStream(1); - EXPECT_TRUE(write_blocked_list_.ShouldYield(16)); - EXPECT_TRUE(write_blocked_list_.ShouldYield(15)); - EXPECT_TRUE(write_blocked_list_.ShouldYield(3)); - EXPECT_FALSE(write_blocked_list_.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, kNotStatic); + UnregisterStream(1, kStatic); + + EXPECT_EQ(3u, PopFront()); + EXPECT_EQ(17u, PopFront()); + EXPECT_EQ(12u, PopFront()); + EXPECT_EQ(40, PopFront()); +} + +TEST_F(QuicWriteBlockedListTest, UpdateStreamPriority) { + RegisterStream(40, kNotStatic, {kV3LowestPriority, kNotIncremental}); + RegisterStream(23, kNotStatic, {6, kNotIncremental}); + RegisterStream(17, kNotStatic, {kV3HighestPriority, kNotIncremental}); + RegisterStream(1, kStatic, {2, kNotIncremental}); + RegisterStream(3, kStatic, {kV3HighestPriority, kNotIncremental}); + + EXPECT_EQ(kV3LowestPriority, GetSpdyPriorityofStream(40)); + EXPECT_EQ(6, GetSpdyPriorityofStream(23)); + EXPECT_EQ(kV3HighestPriority, GetSpdyPriorityofStream(17)); + + UpdateStreamPriority(40, {3, kNotIncremental}); + UpdateStreamPriority(23, {kV3HighestPriority, kNotIncremental}); + UpdateStreamPriority(17, {5, kNotIncremental}); + + EXPECT_EQ(3, GetSpdyPriorityofStream(40)); + EXPECT_EQ(kV3HighestPriority, GetSpdyPriorityofStream(23)); + EXPECT_EQ(5, GetSpdyPriorityofStream(17)); + + 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"); +} + +// TODO(bnc): Test that incremental bit can be changed if urgency is the same. +TEST_F(QuicWriteBlockedListTest, UpdateStreamPrioritySame) { + RegisterStream(1, kNotStatic, {6, kNotIncremental}); + RegisterStream(2, kNotStatic, {6, kNotIncremental}); + + AddStream(1); + AddStream(2); + + EXPECT_EQ(1u, PopFront()); + EXPECT_EQ(2u, PopFront()); + + RegisterStream(3, kNotStatic, {6, kNotIncremental}); + RegisterStream(4, kNotStatic, {6, kNotIncremental}); + + EXPECT_EQ(6, GetSpdyPriorityofStream(3)); + UpdateStreamPriority(3, {6, kNotIncremental}); + EXPECT_EQ(6, GetSpdyPriorityofStream(3)); + + AddStream(3); + AddStream(4); + + EXPECT_EQ(3u, PopFront()); + EXPECT_EQ(4u, PopFront()); + + RegisterStream(5, kNotStatic, {6, kNotIncremental}); + RegisterStream(6, kNotStatic, {6, kNotIncremental}); + + EXPECT_EQ(6, GetSpdyPriorityofStream(6)); + UpdateStreamPriority(6, {6, kNotIncremental}); + EXPECT_EQ(6, GetSpdyPriorityofStream(6)); + + AddStream(5); + AddStream(6); + + EXPECT_EQ(5u, PopFront()); + EXPECT_EQ(6u, PopFront()); } } // namespace