Project import generated by Copybara.

PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/quic_stream_id_manager_test.cc b/quic/core/quic_stream_id_manager_test.cc
new file mode 100644
index 0000000..51799e2
--- /dev/null
+++ b/quic/core/quic_stream_id_manager_test.cc
@@ -0,0 +1,844 @@
+// Copyright (c) 2018 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/core/quic_stream_id_manager.h"
+
+#include <cstdint>
+#include <set>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestQuicStream : public QuicStream {
+  // TestQuicStream exists simply as a place to hang OnDataAvailable().
+ public:
+  TestQuicStream(QuicStreamId id, QuicSession* session, StreamType type)
+      : QuicStream(id, session, /*is_static=*/false, type) {}
+
+  void OnDataAvailable() override {}
+};
+
+class TestQuicSession : public MockQuicSession {
+ public:
+  TestQuicSession(QuicConnection* connection)
+      : MockQuicSession(connection, /*create_mock_crypto_stream=*/true) {
+    Initialize();
+  }
+
+  TestQuicStream* CreateIncomingStream(QuicStreamId id) override {
+    TestQuicStream* stream = new TestQuicStream(
+        id, this,
+        DetermineStreamType(id, connection()->transport_version(),
+                            perspective(),
+                            /*is_incoming=*/true, BIDIRECTIONAL));
+    ActivateStream(QuicWrapUnique(stream));
+    return stream;
+  }
+
+  bool SaveFrame(const QuicFrame& frame) {
+    save_frame_ = frame;
+    DeleteFrame(&const_cast<QuicFrame&>(frame));
+    return true;
+  }
+
+  const QuicFrame& save_frame() { return save_frame_; }
+
+  bool ClearControlFrame(const QuicFrame& frame) {
+    DeleteFrame(&const_cast<QuicFrame&>(frame));
+    return true;
+  }
+
+  TestQuicStream* CreateOutgoingBidirectionalStream() {
+    if (!CanOpenNextOutgoingBidirectionalStream()) {
+      return nullptr;
+    }
+    QuicStreamId id = GetNextOutgoingBidirectionalStreamId();
+    TestQuicStream* stream = new TestQuicStream(id, this, BIDIRECTIONAL);
+    ActivateStream(QuicWrapUnique(stream));
+    return stream;
+  }
+
+  TestQuicStream* CreateOutgoingUnidirectionalStream() {
+    if (!CanOpenNextOutgoingUnidirectionalStream()) {
+      return nullptr;
+    }
+    QuicStreamId id = GetNextOutgoingUnidirectionalStreamId();
+    TestQuicStream* stream = new TestQuicStream(id, this, WRITE_UNIDIRECTIONAL);
+    ActivateStream(QuicWrapUnique(stream));
+    return stream;
+  }
+
+ private:
+  QuicFrame save_frame_;
+};
+
+class QuicStreamIdManagerTestBase : public QuicTestWithParam<bool> {
+ protected:
+  explicit QuicStreamIdManagerTestBase(Perspective perspective)
+      : connection_(new StrictMock<MockQuicConnection>(
+            &helper_,
+            &alarm_factory_,
+            perspective,
+            ParsedQuicVersionVector(
+                {{PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99}}))) {
+    connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+    session_ = QuicMakeUnique<TestQuicSession>(connection_);
+    stream_id_manager_ =
+        GetParam() ? QuicSessionPeer::v99_bidirectional_stream_id_manager(
+                         session_.get())
+                   : QuicSessionPeer::v99_unidirectional_stream_id_manager(
+                         session_.get());
+  }
+
+  QuicTransportVersion transport_version() const {
+    return connection_->transport_version();
+  }
+
+  void CloseStream(QuicStreamId id) { session_->CloseStream(id); }
+
+  QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+    return QuicUtils::GetFirstBidirectionalStreamId(
+               connection_->transport_version(), Perspective::IS_CLIENT) +
+           kV99StreamIdIncrement * n;
+  }
+
+  QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
+    return QuicUtils::GetFirstUnidirectionalStreamId(
+               connection_->transport_version(), Perspective::IS_CLIENT) +
+           kV99StreamIdIncrement * n;
+  }
+
+  QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+    return QuicUtils::GetFirstBidirectionalStreamId(
+               connection_->transport_version(), Perspective::IS_SERVER) +
+           kV99StreamIdIncrement * n;
+  }
+
+  QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+    return QuicUtils::GetFirstUnidirectionalStreamId(
+               connection_->transport_version(), Perspective::IS_SERVER) +
+           kV99StreamIdIncrement * n;
+  }
+
+  MockQuicConnectionHelper helper_;
+  MockAlarmFactory alarm_factory_;
+  StrictMock<MockQuicConnection>* connection_;
+  std::unique_ptr<TestQuicSession> session_;
+  QuicStreamIdManager* stream_id_manager_;
+};
+
+// Following tests are either client-specific (they depend, in some way, on
+// client-specific attributes, such as the initial stream ID) or are
+// server/client independent (arbitrarily all such tests have been placed here).
+
+class QuicStreamIdManagerTestClient : public QuicStreamIdManagerTestBase {
+ protected:
+  QuicStreamIdManagerTestClient()
+      : QuicStreamIdManagerTestBase(Perspective::IS_CLIENT) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool());
+
+// Check that the parameters used by the stream ID manager are properly
+// initialized.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientInitialization) {
+  // These fields are inited via the QuicSession constructor to default
+  // values defined as a constant.
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+            stream_id_manager_->max_allowed_incoming_streams());
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+            stream_id_manager_->max_allowed_outgoing_streams());
+
+  // The window for advertising updates to the MAX STREAM ID is half the number
+  // of streams allowed.
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor,
+            stream_id_manager_->max_stream_id_window());
+
+  // This test runs as a client, so it initiates (that is to say, outgoing)
+  // even-numbered stream IDs. Also, our implementation starts allocating
+  // stream IDs at 0 (for clients) 1 (for servers) -- before taking statically
+  // allocated streams into account. The -1 in the calculation is
+  // because the value being tested is the maximum allowed stream ID, not the
+  // first unallowed stream id.
+  const QuicStreamId kExpectedMaxOutgoingStreamId =
+      (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+                  : session_->next_outgoing_unidirectional_stream_id()) +
+      ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement);
+  EXPECT_EQ(kExpectedMaxOutgoingStreamId,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+
+  // Same for IDs of incoming streams...
+  const QuicStreamId kExpectedMaxIncomingStreamId =
+      stream_id_manager_->first_incoming_dynamic_stream_id() +
+      (kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement;
+  EXPECT_EQ(kExpectedMaxIncomingStreamId,
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+  EXPECT_EQ(kExpectedMaxIncomingStreamId,
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed outgoing
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedOutgoing) {
+  const size_t kNumOutgoingStreams = 124;
+  stream_id_manager_->SetMaxOpenOutgoingStreams(kNumOutgoingStreams);
+  EXPECT_EQ(kNumOutgoingStreams,
+            stream_id_manager_->max_allowed_outgoing_streams());
+
+  // Check that the maximum available stream is properly set.
+  size_t expected_max_outgoing_id =
+      (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+                  : session_->next_outgoing_unidirectional_stream_id()) +
+      ((kNumOutgoingStreams - 1) * kV99StreamIdIncrement);
+  EXPECT_EQ(expected_max_outgoing_id,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed incoming
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedIncoming) {
+  const size_t kStreamCount = 245;
+  stream_id_manager_->SetMaxOpenIncomingStreams(kStreamCount);
+  EXPECT_EQ(kStreamCount, stream_id_manager_->max_allowed_incoming_streams());
+  // Check that the window is 1/2 (integer math) of the stream count.
+  EXPECT_EQ(kStreamCount / 2, stream_id_manager_->max_stream_id_window());
+
+  // Actual- and advertised- maxima start out equal.
+  EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+  // Check that the maximum stream ID is properly calculated.
+  EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() +
+                ((kStreamCount - 1) * kV99StreamIdIncrement),
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+}
+
+// This test checks that the stream advertisement window is set to 1
+// if the number of stream ids is 1. This is a special case in the code.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamIdWindow1) {
+  stream_id_manager_->SetMaxOpenIncomingStreams(1);
+  EXPECT_EQ(1u, stream_id_manager_->max_allowed_incoming_streams());
+  // If streamid_count/2==0 (integer math) force it to 1.
+  EXPECT_EQ(1u, stream_id_manager_->max_stream_id_window());
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is less than the
+// stream ID most recently advertised in a MAX_STREAM_ID frame. This should
+// cause a MAX_STREAM_ID frame with the most recently advertised stream id to be
+// sent.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedOk) {
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+  QuicStreamId stream_id =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id() -
+      kV99StreamIdIncrement;
+  QuicStreamIdBlockedFrame frame(0, stream_id);
+  session_->OnStreamIdBlockedFrame(frame);
+
+  // We should see a MAX_STREAM_ID frame.
+  EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+
+  // and it should advertise the current max-allowed value.
+  EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+            session_->save_frame().max_stream_id_frame.max_stream_id);
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is equal to
+// stream ID most recently advertised in a MAX_STREAM_ID frame. No
+// MAX_STREAM_ID should be generated.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedNoOp) {
+  EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+  QuicStreamId stream_id =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+  QuicStreamIdBlockedFrame frame(0, stream_id);
+  session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is greater than
+// the stream ID most recently advertised in a MAX_STREAM_ID frame. Expect a
+// connection close with an error.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedTooBig) {
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+  EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+  QuicStreamId stream_id =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id() +
+      kV99StreamIdIncrement;
+  QuicStreamIdBlockedFrame frame(0, stream_id);
+  session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Same basic tests as above, but calls
+// QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId directly, avoiding the
+// call chain. The intent is that if there is a problem, the following tests
+// will point to either the stream ID manager or the call chain. They also
+// provide specific, small scale, tests of a public QuicStreamIdManager method.
+// First test make sure that streams with ids below the limit are accepted.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidBelowLimit) {
+  QuicStreamId stream_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id() -
+      kV99StreamIdIncrement;
+  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+  EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Accept a stream with an ID that equals the limit.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidAtLimit) {
+  QuicStreamId stream_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id();
+  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+  EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Close the connection if the id exceeds the limit.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdInValidAboveLimit) {
+  QuicStreamId stream_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+      kV99StreamIdIncrement;
+  QuicString error_details =
+      GetParam() ? "Stream id 401 above 397" : "Stream id 403 above 399";
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+  EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Test that a client will reject a MAX_STREAM_ID that specifies a
+// server-initiated stream ID.
+TEST_P(QuicStreamIdManagerTestClient, RejectServerMaxStreamId) {
+  QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+  // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+  // current MAX.
+  id += (kV99StreamIdIncrement * 2);
+
+  // Make it an odd (server-initiated) ID.
+  id |= 0x1;
+  EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+  // Make the frame and process it; should result in the connection being
+  // closed.
+  QuicMaxStreamIdFrame frame(0, id);
+  EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _));
+  session_->OnMaxStreamIdFrame(frame);
+}
+
+// Test that a client will reject a STREAM_ID_BLOCKED that specifies a
+// client-initiated stream ID. STREAM_ID_BLOCKED from a server should specify an
+// odd (server-initiated_ ID). Generate one with an odd ID and check that the
+// connection is closed.
+TEST_P(QuicStreamIdManagerTestClient, RejectServerStreamIdBlocked) {
+  QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+  // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+  // current MAX.
+  id += (kV99StreamIdIncrement * 2);
+  // Make sure it's odd, like a client-initiated ID.
+  id &= ~0x01;
+  EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+  // Generate and process the frame; connection should be closed.
+  QuicStreamIdBlockedFrame frame(0, id);
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+  session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Test functionality for reception of a MAX STREAM ID frame. This code is
+// client/server-agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientOnMaxStreamIdFrame) {
+  // Get the current maximum allowed outgoing stream ID.
+  QuicStreamId initial_stream_id =
+      stream_id_manager_->max_allowed_outgoing_stream_id();
+  QuicMaxStreamIdFrame frame;
+
+  // If the stream ID in the frame is < the current maximum then
+  // the frame should be ignored.
+  frame.max_stream_id = initial_stream_id - kV99StreamIdIncrement;
+  EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame));
+  EXPECT_EQ(initial_stream_id,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+
+  // A stream ID greater than the current limit should increase the limit.
+  frame.max_stream_id = initial_stream_id + kV99StreamIdIncrement;
+  EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame));
+  EXPECT_EQ(initial_stream_id + kV99StreamIdIncrement,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// Test functionality for reception of a STREAM ID BLOCKED frame.
+// This code is client/server-agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerOnStreamIdBlockedFrame) {
+  // Get the current maximum allowed incoming stream ID.
+  QuicStreamId advertised_stream_id =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+  QuicStreamIdBlockedFrame frame;
+
+  // If the peer is saying it's blocked on the stream ID that
+  // we've advertised, it's a noop since the peer has the correct information.
+  frame.stream_id = advertised_stream_id;
+  EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+
+  // If the peer is saying it's blocked on a stream ID that is larger
+  // than what we've advertised, the connection should get closed.
+  frame.stream_id = advertised_stream_id + kV99StreamIdIncrement;
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+  EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+
+  // If the peer is saying it's blocked on a stream ID that is less than
+  // what we've advertised, we send a MAX STREAM ID frame and update
+  // the advertised value.
+  // First, need to bump up the actual max so there is room for the MAX
+  // STREAM_ID frame to send a larger ID.
+  QuicStreamId actual_stream_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id();
+  stream_id_manager_->OnStreamClosed(
+      stream_id_manager_->first_incoming_dynamic_stream_id());
+  EXPECT_EQ(actual_stream_id + kV99StreamIdIncrement,
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+  EXPECT_GT(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+  // Now simulate receiving a STTREAM_ID_BLOCKED frame...
+  // Changing the actual maximum, above, forces a MAX STREAM ID frame to be
+  // sent, so the logic for that (SendMaxStreamIdFrame(), etc) is tested.
+  frame.stream_id = advertised_stream_id;
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(1)
+      .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+  EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+  EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+  EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+  EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(),
+            session_->save_frame().max_stream_id_frame.max_stream_id);
+
+  // Ensure a client initiated stream ID is rejected.
+  frame.stream_id = GetParam() ? GetNthClientInitiatedBidirectionalId(1)
+                               : GetNthClientInitiatedUnidirectionalId(1);
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+  EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+}
+
+// Test GetNextOutgoingStream. This is client/server agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerGetNextOutgoingFrame) {
+  // Number of streams we can open and the first one we should get when
+  // opening...
+  int number_of_streams = kDefaultMaxStreamsPerConnection;
+  QuicStreamId stream_id =
+      GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+                 : session_->next_outgoing_unidirectional_stream_id();
+
+  while (number_of_streams) {
+    EXPECT_TRUE(stream_id_manager_->CanOpenNextOutgoingStream());
+    EXPECT_EQ(stream_id, stream_id_manager_->GetNextOutgoingStreamId());
+    stream_id += kV99StreamIdIncrement;
+    number_of_streams--;
+  }
+  EXPECT_EQ(stream_id - kV99StreamIdIncrement,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+
+  // If we try to check that the next outgoing stream id is available it should
+  // A) fail and B) generate a STREAM_ID_BLOCKED frame.
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(1)
+      .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+  EXPECT_FALSE(stream_id_manager_->CanOpenNextOutgoingStream());
+  EXPECT_EQ(STREAM_ID_BLOCKED_FRAME, session_->save_frame().type);
+  EXPECT_EQ(stream_id_manager_->max_allowed_outgoing_stream_id(),
+            session_->save_frame().max_stream_id_frame.max_stream_id);
+  // If we try to get the next id (above the limit), it should cause a quic-bug.
+  EXPECT_QUIC_BUG(
+      stream_id_manager_->GetNextOutgoingStreamId(),
+      "Attempt allocate a new outgoing stream ID would exceed the limit");
+}
+
+// Ensure that MaybeIncreaseLargestPeerStreamId works properly. This is
+// server/client agnostic.
+TEST_P(QuicStreamIdManagerTestClient,
+       StreamIdManagerServerMaybeIncreaseLargestPeerStreamId) {
+  EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+      stream_id_manager_->actual_max_allowed_incoming_stream_id()));
+  QuicStreamId server_initiated_stream_id =
+      GetParam() ? GetNthServerInitiatedBidirectionalId(0)
+                 : GetNthServerInitiatedUnidirectionalId(0);
+  EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+      server_initiated_stream_id));
+  // A bad stream ID results in a closed connection.
+  EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+  EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+      stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+      kV99StreamIdIncrement));
+}
+
+// Test the MAX STREAM ID Window functionality.
+// Free up Stream ID space. Do not expect to see a MAX_STREAM_ID
+// until |window| stream ids are available.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerServerMaxStreamId) {
+  // Test that a MAX_STREAM_ID frame is generated when the peer has less than
+  // |max_stream_id_window_| streams left that it can initiate.
+
+  // First, open, and then close, max_stream_id_window_ streams.  This will
+  // max_stream_id_window_ streams available for the peer -- no MAX_STREAM_ID
+  // should be sent. The -1 is because the check in
+  // QuicStreamIdManager::MaybeSendMaxStreamIdFrame sends a MAX_STREAM_ID if the
+  // number of available streams at the peer is <= |max_stream_id_window_|
+  int stream_count = stream_id_manager_->max_stream_id_window() - 1;
+
+  QuicStreamId advertised_max =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+  QuicStreamId expected_actual_max_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id();
+
+  // Should not get a control-frame transmission since the peer should have
+  // "plenty" of stream IDs to use.
+  EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+  // This test runs as a client, so the first stream to release is 2, a
+  // server-initiated stream.
+  QuicStreamId stream_id = GetParam()
+                               ? GetNthServerInitiatedBidirectionalId(0)
+                               : GetNthServerInitiatedUnidirectionalId(0);
+  size_t old_available_incoming_streams =
+      stream_id_manager_->available_incoming_streams();
+
+  while (stream_count) {
+    EXPECT_TRUE(
+        stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+
+    old_available_incoming_streams--;
+    EXPECT_EQ(old_available_incoming_streams,
+              stream_id_manager_->available_incoming_streams());
+
+    stream_count--;
+    stream_id += kV99StreamIdIncrement;
+  }
+
+  // Now close them, still should get no MAX_STREAM_ID
+  stream_count = stream_id_manager_->max_stream_id_window();
+  stream_id = GetParam() ? GetNthServerInitiatedBidirectionalId(0)
+                         : GetNthServerInitiatedUnidirectionalId(0);
+  while (stream_count) {
+    stream_id_manager_->OnStreamClosed(stream_id);
+    stream_count--;
+    stream_id += kV99StreamIdIncrement;
+    expected_actual_max_id += kV99StreamIdIncrement;
+    EXPECT_EQ(expected_actual_max_id,
+              stream_id_manager_->actual_max_allowed_incoming_stream_id());
+    // Advertised maximum should remain the same.
+    EXPECT_EQ(advertised_max,
+              stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+  }
+
+  // This should not change.
+  EXPECT_EQ(old_available_incoming_streams,
+            stream_id_manager_->available_incoming_streams());
+
+  // Now whenever we close a stream we should get a MAX_STREAM_ID frame.
+  // Above code closed all the open streams, so we have to open/close
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(1)
+      .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+  EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+  stream_id_manager_->OnStreamClosed(stream_id);
+  stream_id += kV99StreamIdIncrement;
+
+  // Check that the MAX STREAM ID was sent and has the correct values.
+  EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+  EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(),
+            session_->save_frame().max_stream_id_frame.max_stream_id);
+}
+
+// Test that registering static stream IDs causes the stream ID limit to rise
+// accordingly. This is server/client agnostic.
+TEST_P(QuicStreamIdManagerTestClient, TestStaticStreamAdjustment) {
+  QuicStreamId first_dynamic =
+      stream_id_manager_->first_incoming_dynamic_stream_id();
+  QuicStreamId expected_max_incoming =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id();
+
+  // First test will register the first dynamic stream id as being for a static
+  // stream.  This takes one stream ID out of the low-end of the dynamic range
+  // so therefore the high end should go up by 1 ID.
+  expected_max_incoming += kV99StreamIdIncrement;
+  stream_id_manager_->RegisterStaticStream(first_dynamic);
+  EXPECT_EQ(expected_max_incoming,
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+
+  // Now be extreme, increase static by 100 stream ids.  A discontinuous
+  // jump is not allowed; make sure.
+  first_dynamic += kV99StreamIdIncrement * 100;
+  expected_max_incoming += kV99StreamIdIncrement * 100;
+  QuicString bug_detail =
+      GetParam() ? "allocate 5 got 401" : "allocate 7 got 403";
+  EXPECT_QUIC_BUG(
+      stream_id_manager_->RegisterStaticStream(first_dynamic),
+      "Error in incoming static stream allocation, expected to " + bug_detail);
+}
+
+// Following tests all are server-specific. They depend, in some way, on
+// server-specific attributes, such as the initial stream ID.
+
+class QuicStreamIdManagerTestServer : public QuicStreamIdManagerTestBase {
+ protected:
+  QuicStreamIdManagerTestServer()
+      : QuicStreamIdManagerTestBase(Perspective::IS_SERVER) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool());
+
+// This test checks that the initialization for the maximum allowed outgoing
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedOutgoing) {
+  const size_t kIncomingStreamCount = 123;
+  stream_id_manager_->SetMaxOpenOutgoingStreams(kIncomingStreamCount);
+  EXPECT_EQ(kIncomingStreamCount,
+            stream_id_manager_->max_allowed_outgoing_streams());
+
+  // Check that the max outgoing stream id is properly calculated
+  EXPECT_EQ(stream_id_manager_->GetNextOutgoingStreamId() +
+                ((kIncomingStreamCount - 1) * kV99StreamIdIncrement),
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed incoming
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedIncoming) {
+  const size_t kIncomingStreamCount = 245;
+  stream_id_manager_->SetMaxOpenIncomingStreams(kIncomingStreamCount);
+  EXPECT_EQ(kIncomingStreamCount,
+            stream_id_manager_->max_allowed_incoming_streams());
+
+  // Check that the window is 1/2 (integer math) of the stream count.
+  EXPECT_EQ((kIncomingStreamCount / 2),
+            stream_id_manager_->max_stream_id_window());
+
+  // Actual- and advertised- maxima start out equal.
+  EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+  // First stream ID the client should use should be 3, this means that the max
+  // stream id is 491 -- ((number of stream ids-1) * 2) + first available id.
+  EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() +
+                ((kIncomingStreamCount - 1) * kV99StreamIdIncrement),
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+}
+
+// Test that a MAX_STREAM_ID frame is generated when half the stream ids become
+// available. This has a useful side effect of testing that when streams are
+// closed, the number of available stream ids increases.
+TEST_P(QuicStreamIdManagerTestServer, MaxStreamIdSlidingWindow) {
+  // Ignore OnStreamReset calls.
+  EXPECT_CALL(*connection_, OnStreamReset(_, _)).WillRepeatedly(Return());
+  // Capture control frames for analysis.
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+  // Simulate config being negotiated, causing the limits all to be initialized.
+  session_->OnConfigNegotiated();
+  QuicStreamId first_advert =
+      stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+
+  // Open/close enough streams to shrink the window without causing a MAX STREAM
+  // ID to be generated. The window will open (and a MAX STREAM ID generated)
+  // when max_stream_id_window() stream IDs have been made available. The loop
+  // will make that many stream IDs available, so the last CloseStream should
+  // cause a MAX STREAM ID frame to be generated.
+  int i = static_cast<int>(stream_id_manager_->max_stream_id_window());
+  QuicStreamId id = stream_id_manager_->first_incoming_dynamic_stream_id();
+  while (i) {
+    QuicStream* stream = session_->GetOrCreateStream(id);
+    EXPECT_NE(nullptr, stream);
+    // have to set the stream's fin-received flag to true so that it
+    // does not go into the has-not-received-byte-offset state, leading
+    // to the stream being added to the locally_closed_streams_highest_offset_
+    // map, and therefore not counting as truly being closed. The test requires
+    // that the stream truly close, so that new streams become available,
+    // causing the MAX_STREAM_ID to be sent.
+    stream->set_fin_received(true);
+    EXPECT_EQ(id, stream->id());
+    if (GetParam()) {
+      // Only send reset for incoming bidirectional streams.
+      EXPECT_CALL(*session_, SendRstStream(_, _, _));
+    }
+    CloseStream(stream->id());
+    i--;
+    id += kV99StreamIdIncrement;
+  }
+  EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+  QuicStreamId second_advert =
+      session_->save_frame().max_stream_id_frame.max_stream_id;
+  EXPECT_EQ(first_advert + (stream_id_manager_->max_stream_id_window() *
+                            kV99StreamIdIncrement),
+            second_advert);
+}
+
+// Tast that an attempt to create an outgoing stream does not exceed the limit
+// and that it generates an appropriate STREAM_ID_BLOCKED frame.
+TEST_P(QuicStreamIdManagerTestServer, NewStreamDoesNotExceedLimit) {
+  size_t stream_count = stream_id_manager_->max_allowed_outgoing_streams();
+  EXPECT_NE(0u, stream_count);
+  TestQuicStream* stream;
+  while (stream_count) {
+    stream = GetParam() ? session_->CreateOutgoingBidirectionalStream()
+                        : session_->CreateOutgoingUnidirectionalStream();
+    EXPECT_NE(stream, nullptr);
+    stream_count--;
+  }
+  // Quis Custodiet Ipsos Custodes.
+  EXPECT_EQ(stream->id(), stream_id_manager_->max_allowed_outgoing_stream_id());
+  // Create another, it should fail. Should also send a STREAM_ID_BLOCKED
+  // control frame.
+  EXPECT_CALL(*connection_, SendControlFrame(_));
+  stream = GetParam() ? session_->CreateOutgoingBidirectionalStream()
+                      : session_->CreateOutgoingUnidirectionalStream();
+  EXPECT_EQ(nullptr, stream);
+}
+
+// Test that a server will reject a MAX_STREAM_ID that specifies a
+// client-initiated stream ID.
+TEST_P(QuicStreamIdManagerTestServer, RejectClientMaxStreamId) {
+  QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+  // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+  // current MAX.
+  id += (kV99StreamIdIncrement * 2);
+
+  // Turn it into a client-initiated ID (even).
+  id &= ~0x1;
+  EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+  // Generate a MAX_STREAM_ID frame and process it; the connection should close.
+  QuicMaxStreamIdFrame frame(0, id);
+  EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _));
+  session_->OnMaxStreamIdFrame(frame);
+}
+
+// Test that a server will reject a STREAM_ID_BLOCKED that specifies a
+// server-initiated stream ID. STREAM_ID_BLOCKED from a client should specify an
+// even (client-initiated_ ID) generate one with an odd ID and check that the
+// connection is closed.
+TEST_P(QuicStreamIdManagerTestServer, RejectClientStreamIdBlocked) {
+  QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+  // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+  // current MAX.
+  id += (kV99StreamIdIncrement * 2);
+
+  // Make the ID odd, so it looks like the client is trying to specify a
+  // server-initiated ID.
+  id |= 0x1;
+  EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+  // Generate a STREAM_ID_BLOCKED frame and process it; the connection should
+  // close.
+  QuicStreamIdBlockedFrame frame(0, id);
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+  session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Check that the parameters used by the stream ID manager are properly
+// initialized
+TEST_P(QuicStreamIdManagerTestServer, StreamIdManagerServerInitialization) {
+  // These fields are inited via the QuicSession constructor to default
+  // values defined as a constant.
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+            stream_id_manager_->max_allowed_incoming_streams());
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+            stream_id_manager_->max_allowed_outgoing_streams());
+
+  // The window for advertising updates to the MAX STREAM ID is half the number
+  // of stream allowed.
+  EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor,
+            stream_id_manager_->max_stream_id_window());
+
+  // This test runs as a server, so it initiates (that is to say, outgoing)
+  // even-numbered stream IDs. The -1 in the calculation is because the value
+  // being tested is the maximum allowed stream ID, not the first unallowed
+  // stream id.
+  const QuicStreamId kExpectedMaxOutgoingStreamId =
+      (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+                  : session_->next_outgoing_unidirectional_stream_id()) +
+      ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement);
+  EXPECT_EQ(kExpectedMaxOutgoingStreamId,
+            stream_id_manager_->max_allowed_outgoing_stream_id());
+
+  // Same for IDs of incoming streams... But they are client initiated, so are
+  // even.
+  const QuicStreamId kExpectedMaxIncomingStreamId =
+      GetParam() ? GetNthClientInitiatedBidirectionalId(
+                       kDefaultMaxStreamsPerConnection - 1)
+                 : GetNthClientInitiatedUnidirectionalId(
+                       kDefaultMaxStreamsPerConnection - 1);
+  EXPECT_EQ(kExpectedMaxIncomingStreamId,
+            stream_id_manager_->actual_max_allowed_incoming_stream_id());
+  EXPECT_EQ(kExpectedMaxIncomingStreamId,
+            stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+}
+
+TEST_P(QuicStreamIdManagerTestServer, AvailableStreams) {
+  stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+      GetParam() ? GetNthClientInitiatedBidirectionalId(3)
+                 : GetNthClientInitiatedUnidirectionalId(3));
+  EXPECT_TRUE(stream_id_manager_->IsAvailableStream(
+      GetParam() ? GetNthClientInitiatedBidirectionalId(1)
+                 : GetNthClientInitiatedUnidirectionalId(1)));
+  EXPECT_TRUE(stream_id_manager_->IsAvailableStream(
+      GetParam() ? GetNthClientInitiatedBidirectionalId(2)
+                 : GetNthClientInitiatedUnidirectionalId(2)));
+}
+
+// Tests that if MaybeIncreaseLargestPeerStreamId is given an extremely
+// large stream ID (larger than the limit) it is rejected.
+// This is a regression for Chromium bugs 909987 and 910040
+TEST_P(QuicStreamIdManagerTestServer, ExtremeMaybeIncreaseLargestPeerStreamId) {
+  QuicStreamId too_big_stream_id =
+      stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+      kV99StreamIdIncrement * 20;
+
+  QuicString error_details =
+      GetParam() ? "Stream id 480 above 400" : "Stream id 478 above 398";
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+
+  EXPECT_FALSE(
+      stream_id_manager_->MaybeIncreaseLargestPeerStreamId(too_big_stream_id));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic