blob: 8f2b59093df4fb1ef708dc74c8ececeb0acc82b6 [file] [log] [blame]
// 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 "quic/core/uber_quic_stream_id_manager.h"
#include "quic/core/quic_utils.h"
#include "quic/core/quic_versions.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/quic_stream_id_manager_peer.h"
#include "quic/test_tools/quic_test_utils.h"
using testing::_;
using testing::StrictMock;
namespace quic {
namespace test {
namespace {
struct TestParams {
explicit TestParams(ParsedQuicVersion version, Perspective perspective)
: version(version), perspective(perspective) {}
ParsedQuicVersion version;
Perspective perspective;
};
// Used by ::testing::PrintToStringParamName().
std::string PrintToString(const TestParams& p) {
return absl::StrCat(
ParsedQuicVersionToString(p.version), "_",
(p.perspective == Perspective::IS_CLIENT ? "client" : "server"));
}
std::vector<TestParams> GetTestParams() {
std::vector<TestParams> params;
for (const ParsedQuicVersion& version : AllSupportedVersions()) {
if (!version.HasIetfQuicFrames()) {
continue;
}
params.push_back(TestParams(version, Perspective::IS_CLIENT));
params.push_back(TestParams(version, Perspective::IS_SERVER));
}
return params;
}
class MockDelegate : public QuicStreamIdManager::DelegateInterface {
public:
MOCK_METHOD(void,
SendMaxStreams,
(QuicStreamCount stream_count, bool unidirectional),
(override));
};
class UberQuicStreamIdManagerTest : public QuicTestWithParam<TestParams> {
protected:
UberQuicStreamIdManagerTest()
: manager_(perspective(),
version(),
&delegate_,
0,
0,
kDefaultMaxStreamsPerConnection,
kDefaultMaxStreamsPerConnection) {}
QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
Perspective::IS_CLIENT) +
QuicUtils::StreamIdDelta(transport_version()) * n;
}
QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
Perspective::IS_CLIENT) +
QuicUtils::StreamIdDelta(transport_version()) * n;
}
QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
return QuicUtils::GetFirstBidirectionalStreamId(transport_version(),
Perspective::IS_SERVER) +
QuicUtils::StreamIdDelta(transport_version()) * n;
}
QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
return QuicUtils::GetFirstUnidirectionalStreamId(transport_version(),
Perspective::IS_SERVER) +
QuicUtils::StreamIdDelta(transport_version()) * n;
}
QuicStreamId GetNthPeerInitiatedBidirectionalStreamId(int n) {
return ((perspective() == Perspective::IS_SERVER)
? GetNthClientInitiatedBidirectionalId(n)
: GetNthServerInitiatedBidirectionalId(n));
}
QuicStreamId GetNthPeerInitiatedUnidirectionalStreamId(int n) {
return ((perspective() == Perspective::IS_SERVER)
? GetNthClientInitiatedUnidirectionalId(n)
: GetNthServerInitiatedUnidirectionalId(n));
}
QuicStreamId GetNthSelfInitiatedBidirectionalStreamId(int n) {
return ((perspective() == Perspective::IS_CLIENT)
? GetNthClientInitiatedBidirectionalId(n)
: GetNthServerInitiatedBidirectionalId(n));
}
QuicStreamId GetNthSelfInitiatedUnidirectionalStreamId(int n) {
return ((perspective() == Perspective::IS_CLIENT)
? GetNthClientInitiatedUnidirectionalId(n)
: GetNthServerInitiatedUnidirectionalId(n));
}
QuicStreamId StreamCountToId(QuicStreamCount stream_count,
Perspective perspective,
bool bidirectional) {
return ((bidirectional) ? QuicUtils::GetFirstBidirectionalStreamId(
transport_version(), perspective)
: QuicUtils::GetFirstUnidirectionalStreamId(
transport_version(), perspective)) +
((stream_count - 1) * QuicUtils::StreamIdDelta(transport_version()));
}
ParsedQuicVersion version() { return GetParam().version; }
QuicTransportVersion transport_version() {
return version().transport_version;
}
Perspective perspective() { return GetParam().perspective; }
testing::StrictMock<MockDelegate> delegate_;
UberQuicStreamIdManager manager_;
};
INSTANTIATE_TEST_SUITE_P(Tests,
UberQuicStreamIdManagerTest,
::testing::ValuesIn(GetTestParams()),
::testing::PrintToStringParamName());
TEST_P(UberQuicStreamIdManagerTest, Initialization) {
EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0),
manager_.next_outgoing_bidirectional_stream_id());
EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(0),
manager_.next_outgoing_unidirectional_stream_id());
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) {
const size_t kNumMaxOutgoingStream = 123;
// Set the uni- and bi- directional limits to different values to ensure
// that they are managed separately.
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(
kNumMaxOutgoingStream));
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(
kNumMaxOutgoingStream + 1));
EXPECT_EQ(kNumMaxOutgoingStream,
manager_.max_outgoing_bidirectional_streams());
EXPECT_EQ(kNumMaxOutgoingStream + 1,
manager_.max_outgoing_unidirectional_streams());
// Check that, for each directionality, we can open the correct number of
// streams.
int i = kNumMaxOutgoingStream;
while (i) {
EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
manager_.GetNextOutgoingBidirectionalStreamId();
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
i--;
}
// One more unidirectional
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
// Both should be exhausted...
EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) {
const size_t kNumMaxIncomingStreams = 456;
manager_.SetMaxOpenIncomingUnidirectionalStreams(kNumMaxIncomingStreams);
// Do +1 for bidirectional to ensure that uni- and bi- get properly set.
manager_.SetMaxOpenIncomingBidirectionalStreams(kNumMaxIncomingStreams + 1);
EXPECT_EQ(kNumMaxIncomingStreams + 1,
manager_.GetMaxAllowdIncomingBidirectionalStreams());
EXPECT_EQ(kNumMaxIncomingStreams,
manager_.GetMaxAllowdIncomingUnidirectionalStreams());
EXPECT_EQ(manager_.max_incoming_bidirectional_streams(),
manager_.advertised_max_incoming_bidirectional_streams());
EXPECT_EQ(manager_.max_incoming_unidirectional_streams(),
manager_.advertised_max_incoming_unidirectional_streams());
// Make sure that we can create kNumMaxIncomingStreams incoming unidirectional
// streams and kNumMaxIncomingStreams+1 incoming bidirectional streams.
size_t i;
for (i = 0; i < kNumMaxIncomingStreams; i++) {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedUnidirectionalStreamId(i), nullptr));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i), nullptr));
}
// Should be able to open the next bidirectional stream
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i), nullptr));
// We should have exhausted the counts, the next streams should fail
std::string error_details;
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedUnidirectionalStreamId(i), &error_details));
EXPECT_EQ(error_details,
absl::StrCat(
"Stream id ", GetNthPeerInitiatedUnidirectionalStreamId(i),
" would exceed stream count limit ", kNumMaxIncomingStreams));
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(i + 1), &error_details));
EXPECT_EQ(error_details,
absl::StrCat("Stream id ",
GetNthPeerInitiatedBidirectionalStreamId(i + 1),
" would exceed stream count limit ",
kNumMaxIncomingStreams + 1));
}
TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) {
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(10));
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(10));
EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(0),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthSelfInitiatedBidirectionalStreamId(1),
manager_.GetNextOutgoingBidirectionalStreamId());
EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(0),
manager_.GetNextOutgoingUnidirectionalStreamId());
EXPECT_EQ(GetNthSelfInitiatedUnidirectionalStreamId(1),
manager_.GetNextOutgoingUnidirectionalStreamId());
}
TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedBidirectionalStreamId(3), nullptr));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthPeerInitiatedBidirectionalStreamId(2)));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
GetNthPeerInitiatedUnidirectionalStreamId(3), nullptr));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(1)));
EXPECT_TRUE(
manager_.IsAvailableStream(GetNthPeerInitiatedUnidirectionalStreamId(2)));
}
TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) {
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
StreamCountToId(manager_.max_incoming_bidirectional_streams(),
QuicUtils::InvertPerspective(perspective()),
/* bidirectional=*/true),
nullptr));
EXPECT_TRUE(manager_.MaybeIncreaseLargestPeerStreamId(
StreamCountToId(manager_.max_incoming_bidirectional_streams(),
QuicUtils::InvertPerspective(perspective()),
/* bidirectional=*/false),
nullptr));
std::string expected_error_details =
perspective() == Perspective::IS_SERVER
? "Stream id 400 would exceed stream count limit 100"
: "Stream id 401 would exceed stream count limit 100";
std::string error_details;
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1,
QuicUtils::InvertPerspective(perspective()),
/* bidirectional=*/true),
&error_details));
EXPECT_EQ(expected_error_details, error_details);
expected_error_details =
perspective() == Perspective::IS_SERVER
? "Stream id 402 would exceed stream count limit 100"
: "Stream id 403 would exceed stream count limit 100";
EXPECT_FALSE(manager_.MaybeIncreaseLargestPeerStreamId(
StreamCountToId(manager_.max_incoming_bidirectional_streams() + 1,
QuicUtils::InvertPerspective(perspective()),
/* bidirectional=*/false),
&error_details));
EXPECT_EQ(expected_error_details, error_details);
}
TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) {
QuicStreamCount stream_count =
manager_.advertised_max_incoming_bidirectional_streams() - 1;
QuicStreamsBlockedFrame frame(kInvalidControlFrameId, stream_count,
/*unidirectional=*/false);
EXPECT_CALL(delegate_,
SendMaxStreams(manager_.max_incoming_bidirectional_streams(),
frame.unidirectional))
.Times(0);
EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr));
stream_count = manager_.advertised_max_incoming_unidirectional_streams() - 1;
frame.stream_count = stream_count;
frame.unidirectional = true;
EXPECT_CALL(delegate_,
SendMaxStreams(manager_.max_incoming_unidirectional_streams(),
frame.unidirectional))
.Times(0);
EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr));
}
TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreamsPlusFrame) {
const size_t kNumMaxOutgoingStream = 123;
// Set the uni- and bi- directional limits to different values to ensure
// that they are managed separately.
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingBidirectionalStreams(
kNumMaxOutgoingStream));
EXPECT_TRUE(manager_.MaybeAllowNewOutgoingUnidirectionalStreams(
kNumMaxOutgoingStream + 1));
EXPECT_EQ(kNumMaxOutgoingStream,
manager_.max_outgoing_bidirectional_streams());
EXPECT_EQ(kNumMaxOutgoingStream + 1,
manager_.max_outgoing_unidirectional_streams());
// Check that, for each directionality, we can open the correct number of
// streams.
int i = kNumMaxOutgoingStream;
while (i) {
EXPECT_TRUE(manager_.CanOpenNextOutgoingBidirectionalStream());
manager_.GetNextOutgoingBidirectionalStreamId();
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
i--;
}
// One more unidirectional
EXPECT_TRUE(manager_.CanOpenNextOutgoingUnidirectionalStream());
manager_.GetNextOutgoingUnidirectionalStreamId();
// Both should be exhausted...
EXPECT_FALSE(manager_.CanOpenNextOutgoingUnidirectionalStream());
EXPECT_FALSE(manager_.CanOpenNextOutgoingBidirectionalStream());
}
} // namespace
} // namespace test
} // namespace quic