| Index: net/quic/quic_stream_sequencer_test.cc
|
| diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e24d30901eb11c172289e37e6ce43350a65af639
|
| --- /dev/null
|
| +++ b/net/quic/quic_stream_sequencer_test.cc
|
| @@ -0,0 +1,413 @@
|
| +// Copyright (c) 2012 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/quic/quic_stream_sequencer.h"
|
| +
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/rand_util.h"
|
| +#include "net/quic/reliable_quic_stream.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using base::StringPiece;
|
| +using std::min;
|
| +using std::pair;
|
| +using std::vector;
|
| +using testing::_;
|
| +using testing::AnyNumber;
|
| +using testing::InSequence;
|
| +using testing::Return;
|
| +using testing::StrEq;
|
| +
|
| +namespace net {
|
| +
|
| +class QuicStreamSequencerPeer : public QuicStreamSequencer {
|
| + public:
|
| + explicit QuicStreamSequencerPeer(ReliableQuicStream* stream)
|
| + : QuicStreamSequencer(stream) {
|
| + }
|
| +
|
| + QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream)
|
| + : QuicStreamSequencer(max_mem, stream) {}
|
| +
|
| + virtual bool OnFrame(QuicStreamOffset byte_offset,
|
| + const char* data,
|
| + uint32 data_len) {
|
| + QuicStreamFrame frame;
|
| + frame.stream_id = 1;
|
| + frame.offset = byte_offset;
|
| + frame.data = StringPiece(data, data_len);
|
| + return OnStreamFrame(frame);
|
| + }
|
| +
|
| + void SetMemoryLimit(size_t limit) {
|
| + max_frame_memory_ = limit;
|
| + }
|
| +
|
| + ReliableQuicStream* stream() { return stream_; }
|
| + uint64 num_bytes_consumed() { return num_bytes_consumed_; }
|
| + FrameMap* frames() { return &frames_; }
|
| + int32 max_frame_memory() { return max_frame_memory_; }
|
| + QuicStreamOffset close_offset() { return close_offset_; }
|
| +};
|
| +
|
| +class MockStream : public ReliableQuicStream {
|
| + public:
|
| + MockStream(QuicSession* session, QuicStreamId id)
|
| + : ReliableQuicStream(id, session) {
|
| + }
|
| +
|
| + MOCK_METHOD1(TerminateFromPeer, void(bool half_close));
|
| + MOCK_METHOD2(ProcessData, uint32(const char* data, uint32 data_len));
|
| + MOCK_METHOD1(Close, void(QuicErrorCode error));
|
| +};
|
| +
|
| +namespace {
|
| +
|
| +static const char kPayload[] =
|
| + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
| +
|
| +class QuicStreamSequencerTest : public ::testing::Test {
|
| + protected:
|
| + QuicStreamSequencerTest()
|
| + : session_(NULL),
|
| + stream_(session_, 1),
|
| + sequencer_(new QuicStreamSequencerPeer(&stream_)) {
|
| + }
|
| +
|
| + QuicSession* session_;
|
| + testing::StrictMock<MockStream> stream_;
|
| + scoped_ptr<QuicStreamSequencerPeer> sequencer_;
|
| +};
|
| +
|
| +TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3))
|
| + .WillOnce(Return(3));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(0u, sequencer_->frames()->size());
|
| + EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
|
| + // Nack this - it matches a past sequence number and we should not see it
|
| + // again.
|
| + EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3));
|
| + EXPECT_EQ(0u, sequencer_->frames()->size());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) {
|
| + /*
|
| + EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)),
|
| + "Setting max frame memory to 2. "
|
| + "Some frames will be impossible to handle.");
|
| +
|
| + EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc", 3), "");
|
| + */
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) {
|
| + sequencer_->SetMemoryLimit(3);
|
| +
|
| + EXPECT_FALSE(sequencer_->OnFrame(3, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| + // Ignore this - it matches a buffered frame.
|
| + // Right now there's no checking that the payload is consistent.
|
| + EXPECT_FALSE(sequencer_->OnFrame(0, "def", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3))
|
| + .WillOnce(Return(3));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(0u, sequencer_->frames()->size());
|
| + EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3))
|
| + .WillOnce(Return(2));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| + EXPECT_EQ(2u, sequencer_->num_bytes_consumed());
|
| + EXPECT_EQ("c", sequencer_->frames()->find(2)->second);
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3))
|
| + .WillOnce(Return(0));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| + EXPECT_EQ("abc", sequencer_->frames()->find(0)->second);
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
|
| + EXPECT_TRUE(sequencer_->OnFrame(3, "abc", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| + EXPECT_EQ("abc", sequencer_->frames()->find(3)->second);
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
|
| + // Buffer the first
|
| + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi", 3));
|
| + EXPECT_EQ(1u, sequencer_->frames()->size());
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| + // Buffer the second
|
| + EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3));
|
| + EXPECT_EQ(2u, sequencer_->frames()->size());
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3));
|
| +
|
| + // Ack right away
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(9u, sequencer_->num_bytes_consumed());
|
| +
|
| + EXPECT_EQ(0u, sequencer_->frames()->size());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) {
|
| + sequencer_->SetMemoryLimit(9);
|
| +
|
| + // Too far to buffer.
|
| + EXPECT_FALSE(sequencer_->OnFrame(9, "jkl", 3));
|
| +
|
| + // We can afford to buffer this.
|
| + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi", 3));
|
| + EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData("abc", 3)).WillOnce(Return(3));
|
| +
|
| + // Ack right away
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| + EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
|
| +
|
| + // We should be willing to buffer this now.
|
| + EXPECT_TRUE(sequencer_->OnFrame(9, "jkl", 3));
|
| + EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
|
| +
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("jkl"), 3)).WillOnce(Return(3));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3));
|
| + EXPECT_EQ(12u, sequencer_->num_bytes_consumed());
|
| + EXPECT_EQ(0u, sequencer_->frames()->size());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicCloseOrdered) {
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +
|
| + EXPECT_CALL(stream_, TerminateFromPeer(false));
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicHalfOrdered) {
|
| + InSequence s;
|
| +
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +
|
| + EXPECT_CALL(stream_, TerminateFromPeer(true));
|
| + sequencer_->CloseStreamAtOffset(3, true);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicCloseUnordered) {
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(false));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicHalfUnorderedWithFlush) {
|
| + sequencer_->CloseStreamAtOffset(6, true);
|
| + EXPECT_EQ(6u, sequencer_->close_offset());
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(true));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicCloseUnorderedWithFlush) {
|
| + sequencer_->CloseStreamAtOffset(6, false);
|
| + EXPECT_EQ(6u, sequencer_->close_offset());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(false));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(3, "def", 3));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) {
|
| + sequencer_->CloseStreamAtOffset(3, true);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(true));
|
| +
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, CloseStreamBeforeCloseEqual) {
|
| + sequencer_->CloseStreamAtOffset(3, true);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(false));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, CloseBeforeTermianteEqual) {
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + sequencer_->CloseStreamAtOffset(3, true);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + InSequence s;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3));
|
| + EXPECT_CALL(stream_, TerminateFromPeer(false));
|
| + EXPECT_TRUE(sequencer_->OnFrame(0, "abc", 3));
|
| +}
|
| +
|
| +TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS));
|
| + sequencer_->CloseStreamAtOffset(5, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS));
|
| + sequencer_->CloseStreamAtOffset(1, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +
|
| + sequencer_->CloseStreamAtOffset(3, false);
|
| + EXPECT_EQ(3u, sequencer_->close_offset());
|
| +}
|
| +
|
| +class QuicSequencerRandomTest : public QuicStreamSequencerTest {
|
| + public:
|
| + typedef pair<int, string> Frame;
|
| + typedef vector<Frame> FrameList;
|
| +
|
| + void CreateFrames() {
|
| + int payload_size = arraysize(kPayload) - 1;
|
| + int remaining_payload = payload_size;
|
| + while (remaining_payload != 0) {
|
| + int size = min(OneToN(6), remaining_payload);
|
| + int idx = payload_size - remaining_payload;
|
| + list_.push_back(make_pair(idx, string(kPayload + idx, size)));
|
| + remaining_payload -= size;
|
| + }
|
| + }
|
| +
|
| + QuicSequencerRandomTest() {
|
| + //int32 seed = ACMRandom::HostnamePidTimeSeed();
|
| + //LOG(INFO) << "**** The current seed is " << seed << " ****";
|
| + //random_.reset(new ACMRandom(seed));
|
| +
|
| + CreateFrames();
|
| + }
|
| +
|
| + int OneToN(int n) {
|
| + return base::RandInt(1, n);
|
| + }
|
| +
|
| + int MaybeProcessMaybeBuffer(const char* data, uint32 len) {
|
| + int to_process = len;
|
| + if (base::RandUint64() % 2 != 0) {
|
| + to_process = base::RandInt(0, len);
|
| + }
|
| + output_.append(data, to_process);
|
| + LOG(ERROR) << output_;
|
| + return to_process;
|
| + }
|
| +
|
| + string output_;
|
| + //scoped_ptr<ACMRandom> random_;
|
| + FrameList list_;
|
| +};
|
| +
|
| +// All frames are processed as soon as we have sequential data.
|
| +// Infinite buffering, so all frames are acked right away.
|
| +TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
|
| + InSequence s;
|
| + for (size_t i = 0; i < list_.size(); ++i) {
|
| + string* data = &list_[i].second;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq(*data), data->size()))
|
| + .WillOnce(Return(data->size()));
|
| + }
|
| +
|
| + while (list_.size() != 0) {
|
| + int idx = OneToN(list_.size()) - 1;
|
| + LOG(ERROR) << "Sending index " << idx << " " << list_[idx].second.c_str();
|
| + EXPECT_TRUE(sequencer_->OnFrame(
|
| + list_[idx].first, list_[idx].second.c_str(),
|
| + list_[idx].second.size()));
|
| + list_.erase(list_.begin() + idx);
|
| + }
|
| +}
|
| +
|
| +// All frames are processed as soon as we have sequential data.
|
| +// Buffering, so some frames are rejected.
|
| +TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) {
|
| + sequencer_->SetMemoryLimit(26);
|
| +
|
| + InSequence s;
|
| + for (size_t i = 0; i < list_.size(); ++i) {
|
| + string* data = &list_[i].second;
|
| + EXPECT_CALL(stream_, ProcessData(StrEq(*data), data->size()))
|
| + .WillOnce(Return(data->size()));
|
| + }
|
| +
|
| + while (list_.size() != 0) {
|
| + int idx = OneToN(list_.size()) - 1;
|
| + LOG(ERROR) << "Sending index " << idx << " " << list_[idx].second.c_str();
|
| + bool acked = sequencer_->OnFrame(
|
| + list_[idx].first, list_[idx].second.c_str(),
|
| + list_[idx].second.size());
|
| + if (acked) {
|
| + list_.erase(list_.begin() + idx);
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace net
|
|
|