When QPACK encoding, allow blocking references on a stream that is already blocked.

After this change, blocked_stream_count() would only be used in tests.  This CL
also takes the opportunity to replace it with a more expressive
stream_is_blocked() method.

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 270385673
Change-Id: I2b68be71f2db19e2acbc830b3a4579436fe4fa39
diff --git a/quic/core/qpack/qpack_blocking_manager_test.cc b/quic/core/qpack/qpack_blocking_manager_test.cc
index 6f78d87..f21f1e7 100644
--- a/quic/core/qpack/qpack_blocking_manager_test.cc
+++ b/quic/core/qpack/qpack_blocking_manager_test.cc
@@ -8,6 +8,27 @@
 
 namespace quic {
 namespace test {
+
+class QpackBlockingManagerPeer {
+ public:
+  static bool stream_is_blocked(const QpackBlockingManager* manager,
+                                QuicStreamId stream_id) {
+    for (const auto& header_blocks_for_stream : manager->header_blocks_) {
+      if (header_blocks_for_stream.first != stream_id) {
+        continue;
+      }
+      for (const auto& indices : header_blocks_for_stream.second) {
+        if (QpackBlockingManager::RequiredInsertCount(indices) >
+            manager->known_received_count_) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+};
+
 namespace {
 
 class QpackBlockingManagerTest : public QuicTest {
@@ -15,11 +36,14 @@
   QpackBlockingManagerTest() = default;
   ~QpackBlockingManagerTest() override = default;
 
+  bool stream_is_blocked(QuicStreamId stream_id) const {
+    return QpackBlockingManagerPeer::stream_is_blocked(&manager_, stream_id);
+  }
+
   QpackBlockingManager manager_;
 };
 
 TEST_F(QpackBlockingManagerTest, Empty) {
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
   EXPECT_EQ(0u, manager_.known_received_count());
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
             manager_.smallest_blocking_index());
@@ -34,37 +58,39 @@
   // Stream 0 is not blocked, because it only references entries that are
   // already acknowledged by an Insert Count Increment instruction.
   manager_.OnHeaderBlockSent(0, {1, 0});
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
 }
 
 TEST_F(QpackBlockingManagerTest, UnblockedByInsertCountIncrement) {
   manager_.OnHeaderBlockSent(0, {1, 0});
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
 
   manager_.OnInsertCountIncrement(2);
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
 }
 
 TEST_F(QpackBlockingManagerTest, NotBlockedByHeaderAcknowledgement) {
   manager_.OnHeaderBlockSent(0, {2, 1, 1});
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
 
   EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
 
   // Stream 1 is not blocked, because it only references entries that are
   // already acknowledged by a Header Acknowledgement instruction.
   manager_.OnHeaderBlockSent(1, {2, 2});
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(1));
 }
 
 TEST_F(QpackBlockingManagerTest, UnblockedByHeaderAcknowledgement) {
   manager_.OnHeaderBlockSent(0, {2, 1, 1});
   manager_.OnHeaderBlockSent(1, {2, 2});
-  EXPECT_EQ(2u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
+  EXPECT_TRUE(stream_is_blocked(1));
 
   EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
+  EXPECT_FALSE(stream_is_blocked(1));
 }
 
 TEST_F(QpackBlockingManagerTest, KnownReceivedCount) {
@@ -145,38 +171,37 @@
 
 TEST_F(QpackBlockingManagerTest, HeaderAcknowledgementsOnSingleStream) {
   EXPECT_EQ(0u, manager_.known_received_count());
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
             manager_.smallest_blocking_index());
 
   manager_.OnHeaderBlockSent(0, {2, 1, 1});
   EXPECT_EQ(0u, manager_.known_received_count());
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(1u, manager_.smallest_blocking_index());
 
   manager_.OnHeaderBlockSent(0, {1, 0});
   EXPECT_EQ(0u, manager_.known_received_count());
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(0u, manager_.smallest_blocking_index());
 
   EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
   EXPECT_EQ(3u, manager_.known_received_count());
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
   EXPECT_EQ(0u, manager_.smallest_blocking_index());
 
   manager_.OnHeaderBlockSent(0, {3});
   EXPECT_EQ(3u, manager_.known_received_count());
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(0u, manager_.smallest_blocking_index());
 
   EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
   EXPECT_EQ(3u, manager_.known_received_count());
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(3u, manager_.smallest_blocking_index());
 
   EXPECT_TRUE(manager_.OnHeaderAcknowledgement(0));
   EXPECT_EQ(4u, manager_.known_received_count());
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
             manager_.smallest_blocking_index());
 
@@ -185,23 +210,26 @@
 
 TEST_F(QpackBlockingManagerTest, CancelStream) {
   manager_.OnHeaderBlockSent(0, {3});
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(3u, manager_.smallest_blocking_index());
 
   manager_.OnHeaderBlockSent(0, {2});
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
   EXPECT_EQ(2u, manager_.smallest_blocking_index());
 
   manager_.OnHeaderBlockSent(1, {4});
-  EXPECT_EQ(2u, manager_.blocked_stream_count());
+  EXPECT_TRUE(stream_is_blocked(0));
+  EXPECT_TRUE(stream_is_blocked(1));
   EXPECT_EQ(2u, manager_.smallest_blocking_index());
 
   manager_.OnStreamCancellation(0);
-  EXPECT_EQ(1u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
+  EXPECT_TRUE(stream_is_blocked(1));
   EXPECT_EQ(4u, manager_.smallest_blocking_index());
 
   manager_.OnStreamCancellation(1);
-  EXPECT_EQ(0u, manager_.blocked_stream_count());
+  EXPECT_FALSE(stream_is_blocked(0));
+  EXPECT_FALSE(stream_is_blocked(1));
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
             manager_.smallest_blocking_index());
 }
@@ -288,6 +316,76 @@
             manager_.smallest_blocking_index());
 }
 
+TEST_F(QpackBlockingManagerTest, BlockingAllowedOnStream) {
+  const QuicStreamId kStreamId1 = 1;
+  const QuicStreamId kStreamId2 = 2;
+  const QuicStreamId kStreamId3 = 3;
+
+  // No stream can block if limit is 0.
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 0));
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 0));
+
+  // Either stream can block if limit is larger.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
+
+  // Doubly block first stream.
+  manager_.OnHeaderBlockSent(kStreamId1, {0});
+  manager_.OnHeaderBlockSent(kStreamId1, {1});
+
+  // First stream is already blocked so it can carry more blocking references.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
+  // Second stream is not allowed to block if limit is already reached.
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
+
+  // Either stream can block if limit is larger than number of blocked streams.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
+
+  // Block second stream.
+  manager_.OnHeaderBlockSent(kStreamId2, {2});
+
+  // Streams are already blocked so either can carry more blocking references.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
+
+  // Third, unblocked stream is not allowed to block unless limit is strictly
+  // larger than number of blocked streams.
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId3, 2));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId3, 3));
+
+  // Acknowledge decoding of first header block on first stream.
+  // Stream is still blocked on its second header block.
+  manager_.OnHeaderAcknowledgement(kStreamId1);
+
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
+
+  // Acknowledge decoding of second header block on first stream.
+  // This unblocks the stream.
+  manager_.OnHeaderAcknowledgement(kStreamId1);
+
+  // First stream is not allowed to block if limit is already reached.
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
+  // Second stream is already blocked so it can carry more blocking references.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
+
+  // Either stream can block if limit is larger than number of blocked streams.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 2));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 2));
+
+  // Unblock second stream.
+  manager_.OnHeaderAcknowledgement(kStreamId2);
+
+  // No stream can block if limit is 0.
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId1, 0));
+  EXPECT_FALSE(manager_.blocking_allowed_on_stream(kStreamId2, 0));
+
+  // Either stream can block if limit is larger.
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId1, 1));
+  EXPECT_TRUE(manager_.blocking_allowed_on_stream(kStreamId2, 1));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic