| Index: net/filter/filter_source_stream_unittest.cc
|
| diff --git a/net/filter/filter_source_stream_unittest.cc b/net/filter/filter_source_stream_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..43c6b6c9d61179121d6d2f4b2ff1d4f7f58d1319
|
| --- /dev/null
|
| +++ b/net/filter/filter_source_stream_unittest.cc
|
| @@ -0,0 +1,512 @@
|
| +// Copyright 2016 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 <algorithm>
|
| +#include <string>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/macros.h"
|
| +#include "base/numerics/safe_conversions.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/test_completion_callback.h"
|
| +#include "net/filter/filter_source_stream.h"
|
| +#include "net/filter/mock_source_stream.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +const size_t kDefaultBufferSize = 4096;
|
| +const size_t kSmallBufferSize = 1;
|
| +
|
| +class TestFilterSourceStreamBase : public FilterSourceStream {
|
| + public:
|
| + TestFilterSourceStreamBase(std::unique_ptr<SourceStream> upstream)
|
| + : FilterSourceStream(SourceStream::TYPE_NONE, std::move(upstream)) {}
|
| + ~TestFilterSourceStreamBase() override { DCHECK(buffer_.empty()); }
|
| + std::string GetTypeAsString() const override { return type_string_; }
|
| +
|
| + void set_type_string(const std::string& type_string) {
|
| + type_string_ = type_string;
|
| + }
|
| +
|
| + protected:
|
| + // Writes contents of |buffer_| to |output_buffer| and returns the number of
|
| + // bytes written or an error code. Additionally removes consumed data from
|
| + // |buffer_|.
|
| + int WriteBufferToOutput(IOBuffer* output_buffer, int output_buffer_size) {
|
| + size_t bytes_to_filter =
|
| + std::min(buffer_.length(), static_cast<size_t>(output_buffer_size));
|
| + memcpy(output_buffer->data(), buffer_.data(), bytes_to_filter);
|
| + buffer_.erase(0, bytes_to_filter);
|
| + return base::checked_cast<int>(bytes_to_filter);
|
| + }
|
| +
|
| + // Buffer used by subclasses to hold data that is yet to be passed to the
|
| + // caller.
|
| + std::string buffer_;
|
| +
|
| + private:
|
| + std::string type_string_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestFilterSourceStreamBase);
|
| +};
|
| +
|
| +// A FilterSourceStream that needs all input data before it can return non-zero
|
| +// bytes read.
|
| +class NeedsAllInputFilterSourceStream : public TestFilterSourceStreamBase {
|
| + public:
|
| + NeedsAllInputFilterSourceStream(std::unique_ptr<SourceStream> upstream,
|
| + size_t expected_input_bytes)
|
| + : TestFilterSourceStreamBase(std::move(upstream)),
|
| + expected_input_bytes_(expected_input_bytes) {}
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + buffer_.append(input_buffer->data(), input_buffer->BytesRemaining());
|
| + EXPECT_GE(expected_input_bytes_, input_buffer->BytesRemaining());
|
| + expected_input_bytes_ -= input_buffer->BytesRemaining();
|
| + input_buffer->DidConsume(input_buffer->BytesRemaining());
|
| + if (expected_input_bytes_ > 0) {
|
| + // Keep returning 0 bytes read until all |expected_input_bytes| have
|
| + // been read from |upstream|.
|
| + return 0;
|
| + }
|
| + return WriteBufferToOutput(output_buffer, output_buffer_size);
|
| + }
|
| +
|
| + private:
|
| + // Expected remaining bytes to be received from |upstream|.
|
| + int expected_input_bytes_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(NeedsAllInputFilterSourceStream);
|
| +};
|
| +
|
| +// A FilterSourceStream that repeat every input byte by |multiplier| amount of
|
| +// times.
|
| +class MultiplySourceStream : public TestFilterSourceStreamBase {
|
| + public:
|
| + MultiplySourceStream(std::unique_ptr<SourceStream> upstream, int multiplier)
|
| + : TestFilterSourceStreamBase(std::move(upstream)),
|
| + multiplier_(multiplier) {}
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + for (int i = 0; i < input_buffer->BytesRemaining(); i++) {
|
| + for (int j = 0; j < multiplier_; j++)
|
| + buffer_.append(input_buffer->data() + i, 1);
|
| + }
|
| + input_buffer->DidConsume(input_buffer->BytesRemaining());
|
| + return WriteBufferToOutput(output_buffer, output_buffer_size);
|
| + }
|
| +
|
| + private:
|
| + int multiplier_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MultiplySourceStream);
|
| +};
|
| +
|
| +// A FilterSourceStream passes through data unchanged to consumer.
|
| +class PassThroughFilterSourceStream : public TestFilterSourceStreamBase {
|
| + public:
|
| + explicit PassThroughFilterSourceStream(std::unique_ptr<SourceStream> upstream)
|
| + : TestFilterSourceStreamBase(std::move(upstream)) {}
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + buffer_.append(input_buffer->data(), input_buffer->BytesRemaining());
|
| + input_buffer->DidConsume(input_buffer->BytesRemaining());
|
| + return WriteBufferToOutput(output_buffer, output_buffer_size);
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(PassThroughFilterSourceStream);
|
| +};
|
| +
|
| +// A FilterSourceStream passes throttle input data such that it returns them to
|
| +// caller only one bytes at a time.
|
| +class ThrottleSourceStream : public TestFilterSourceStreamBase {
|
| + public:
|
| + explicit ThrottleSourceStream(std::unique_ptr<SourceStream> upstream)
|
| + : TestFilterSourceStreamBase(std::move(upstream)) {}
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + buffer_.append(input_buffer->data(), input_buffer->BytesRemaining());
|
| + input_buffer->DidConsume(input_buffer->BytesRemaining());
|
| + int bytes_to_read = std::min(1, static_cast<int>(buffer_.size()));
|
| + memcpy(output_buffer->data(), buffer_.data(), bytes_to_read);
|
| + buffer_.erase(0, bytes_to_read);
|
| + return bytes_to_read;
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(ThrottleSourceStream);
|
| +};
|
| +
|
| +// A FilterSourceStream that consumes all input data but return no output.
|
| +class NoOutputSourceStream : public TestFilterSourceStreamBase {
|
| + public:
|
| + NoOutputSourceStream(std::unique_ptr<SourceStream> upstream,
|
| + size_t expected_input_size)
|
| + : TestFilterSourceStreamBase(std::move(upstream)),
|
| + expected_input_size_(expected_input_size),
|
| + consumed_all_input_(false) {}
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + expected_input_size_ -= input_buffer->BytesRemaining();
|
| + input_buffer->DidConsume(input_buffer->BytesRemaining());
|
| + EXPECT_LE(0, expected_input_size_);
|
| + consumed_all_input_ = (expected_input_size_ == 0);
|
| + return OK;
|
| + }
|
| +
|
| + bool consumed_all_input() const { return consumed_all_input_; }
|
| +
|
| + private:
|
| + // Expected remaining bytes to be received from |upstream|.
|
| + int expected_input_size_;
|
| + bool consumed_all_input_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(NoOutputSourceStream);
|
| +};
|
| +
|
| +// A FilterSourceStream return an error code in FilterData().
|
| +class ErrorFilterSourceStream : public FilterSourceStream {
|
| + public:
|
| + explicit ErrorFilterSourceStream(std::unique_ptr<SourceStream> upstream)
|
| + : FilterSourceStream(SourceStream::TYPE_NONE, std::move(upstream)) {}
|
| +
|
| + int FilterData(IOBuffer* output_buffer,
|
| + int output_buffer_size,
|
| + DrainableIOBuffer* input_buffer) override {
|
| + return ERR_CONTENT_DECODING_FAILED;
|
| + }
|
| + std::string GetTypeAsString() const override { return ""; }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(ErrorFilterSourceStream);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class FilterSourceStreamTest
|
| + : public ::testing::TestWithParam<MockSourceStream::Mode> {
|
| + protected:
|
| + // If MockSourceStream::Mode is ASYNC, completes |num_reads| from
|
| + // |mock_stream| and wait for |callback| to complete. If Mode is not ASYNC,
|
| + // does nothing and returns |previous_result|.
|
| + int CompleteReadIfAsync(int previous_result,
|
| + TestCompletionCallback* callback,
|
| + MockSourceStream* mock_stream,
|
| + size_t num_reads) {
|
| + if (GetParam() == MockSourceStream::ASYNC) {
|
| + EXPECT_EQ(ERR_IO_PENDING, previous_result);
|
| + while (num_reads > 0) {
|
| + mock_stream->CompleteNextRead();
|
| + num_reads--;
|
| + }
|
| + return callback->WaitForResult();
|
| + }
|
| + return previous_result;
|
| + }
|
| +};
|
| +
|
| +INSTANTIATE_TEST_CASE_P(FilterSourceStreamTests,
|
| + FilterSourceStreamTest,
|
| + ::testing::Values(MockSourceStream::SYNC,
|
| + MockSourceStream::ASYNC));
|
| +
|
| +// Tests that a FilterSourceStream subclass (NeedsAllInputFilterSourceStream)
|
| +// can return 0 bytes for FilterData()s when it has not consumed EOF from the
|
| +// upstream. In this case, FilterSourceStream should continue reading from
|
| +// upstream to complete filtering.
|
| +TEST_P(FilterSourceStreamTest, FilterDataReturnNoBytesExceptLast) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input("hello, world!");
|
| + size_t read_size = 2;
|
| + size_t num_reads = 0;
|
| + // Add a sequence of small reads.
|
| + for (size_t offset = 0; offset < input.length(); offset += read_size) {
|
| + source->AddReadResult(input.data() + offset,
|
| + std::min(read_size, input.length() - offset), OK,
|
| + GetParam());
|
| + num_reads++;
|
| + }
|
| + MockSourceStream* mock_stream = source.get();
|
| + NeedsAllInputFilterSourceStream stream(std::move(source), input.length());
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + TestCompletionCallback callback;
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, num_reads);
|
| + ASSERT_EQ(static_cast<int>(input.length()), rv);
|
| + EXPECT_EQ(input, std::string(output_buffer->data(), rv));
|
| +}
|
| +
|
| +// Tests that FilterData() returns 0 byte read because the upstream gives an
|
| +// EOF.
|
| +TEST_P(FilterSourceStreamTest, FilterDataReturnNoByte) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input;
|
| + source->AddReadResult(input.data(), 0, OK, GetParam());
|
| + MockSourceStream* mock_stream = source.get();
|
| + PassThroughFilterSourceStream stream(std::move(source));
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + TestCompletionCallback callback;
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, 1);
|
| + EXPECT_EQ(OK, rv);
|
| +}
|
| +
|
| +// Tests that FilterData() returns 0 byte filtered even though the upstream
|
| +// produces data.
|
| +TEST_P(FilterSourceStreamTest, FilterDataOutputNoData) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + size_t read_size = 2;
|
| + size_t num_reads = 0;
|
| + // Add a sequence of small reads.
|
| + for (size_t offset = 0; offset < input.length(); offset += read_size) {
|
| + source->AddReadResult(input.data() + offset,
|
| + std::min(read_size, input.length() - offset), OK,
|
| + GetParam());
|
| + num_reads++;
|
| + }
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + num_reads++;
|
| + MockSourceStream* mock_stream = source.get();
|
| + NoOutputSourceStream stream(std::move(source), input.length());
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + TestCompletionCallback callback;
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, num_reads);
|
| + EXPECT_EQ(OK, rv);
|
| + EXPECT_TRUE(stream.consumed_all_input());
|
| +}
|
| +
|
| +// Tests that FilterData() returns non-zero bytes because the upstream
|
| +// returns data.
|
| +TEST_P(FilterSourceStreamTest, FilterDataReturnData) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + size_t read_size = 2;
|
| + // Add a sequence of small reads.
|
| + for (size_t offset = 0; offset < input.length(); offset += read_size) {
|
| + source->AddReadResult(input.data() + offset,
|
| + std::min(read_size, input.length() - offset), OK,
|
| + GetParam());
|
| + }
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + MockSourceStream* mock_stream = source.get();
|
| + PassThroughFilterSourceStream stream(std::move(source));
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + TestCompletionCallback callback;
|
| + std::string actual_output;
|
| + while (true) {
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + if (rv == OK)
|
| + break;
|
| + ASSERT_GE(static_cast<int>(read_size), rv);
|
| + ASSERT_GT(rv, OK);
|
| + actual_output.append(output_buffer->data(), rv);
|
| + }
|
| + EXPECT_EQ(input, actual_output);
|
| +}
|
| +
|
| +// Tests that FilterData() returns more data than what it consumed.
|
| +TEST_P(FilterSourceStreamTest, FilterDataReturnMoreData) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + size_t read_size = 2;
|
| + // Add a sequence of small reads.
|
| + for (size_t offset = 0; offset < input.length(); offset += read_size) {
|
| + source->AddReadResult(input.data() + offset,
|
| + std::min(read_size, input.length() - offset), OK,
|
| + GetParam());
|
| + }
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + MockSourceStream* mock_stream = source.get();
|
| + int multiplier = 2;
|
| + MultiplySourceStream stream(std::move(source), multiplier);
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + TestCompletionCallback callback;
|
| + std::string actual_output;
|
| + while (true) {
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + if (rv == OK)
|
| + break;
|
| + ASSERT_GE(static_cast<int>(read_size) * multiplier, rv);
|
| + ASSERT_GT(rv, OK);
|
| + actual_output.append(output_buffer->data(), rv);
|
| + }
|
| + EXPECT_EQ("hheelllloo,, wwoorrlldd!!", actual_output);
|
| +}
|
| +
|
| +// Tests that FilterData() returns non-zero bytes and output buffer size is
|
| +// smaller than the number of bytes read from the upstream.
|
| +TEST_P(FilterSourceStreamTest, FilterDataOutputSpace) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + size_t read_size = 2;
|
| + // Add a sequence of small reads.
|
| + for (size_t offset = 0; offset < input.length(); offset += read_size) {
|
| + source->AddReadResult(input.data() + offset,
|
| + std::min(read_size, input.length() - offset), OK,
|
| + GetParam());
|
| + }
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + // Use an extremely small buffer size, so FilterData will need more output
|
| + // space.
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kSmallBufferSize);
|
| + MockSourceStream* mock_stream = source.get();
|
| + PassThroughFilterSourceStream stream(std::move(source));
|
| + TestCompletionCallback callback;
|
| + std::string actual_output;
|
| + while (true) {
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + if (rv == ERR_IO_PENDING)
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + if (rv == OK)
|
| + break;
|
| + ASSERT_GT(rv, OK);
|
| + ASSERT_GE(kSmallBufferSize, static_cast<size_t>(rv));
|
| + actual_output.append(output_buffer->data(), rv);
|
| + }
|
| + EXPECT_EQ(input, actual_output);
|
| +}
|
| +
|
| +// Tests that FilterData() returns an error code, which is then surfaced as
|
| +// the result of calling Read().
|
| +TEST_P(FilterSourceStreamTest, FilterDataReturnError) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input;
|
| + source->AddReadResult(input.data(), 0, OK, GetParam());
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + MockSourceStream* mock_stream = source.get();
|
| + ErrorFilterSourceStream stream(std::move(source));
|
| + TestCompletionCallback callback;
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
|
| + // Reading from |stream| again should return the same error.
|
| + rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
|
| +}
|
| +
|
| +TEST_P(FilterSourceStreamTest, FilterChaining) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + source->AddReadResult(input.data(), input.length(), OK, GetParam());
|
| + MockSourceStream* mock_stream = source.get();
|
| + std::unique_ptr<PassThroughFilterSourceStream> pass_through_source(
|
| + new PassThroughFilterSourceStream(std::move(source)));
|
| + pass_through_source->set_type_string("FIRST_PASS_THROUGH");
|
| + std::unique_ptr<NeedsAllInputFilterSourceStream> needs_all_input_source(
|
| + new NeedsAllInputFilterSourceStream(std::move(pass_through_source),
|
| + input.length()));
|
| + needs_all_input_source->set_type_string("NEEDS_ALL");
|
| + std::unique_ptr<PassThroughFilterSourceStream> second_pass_through_source(
|
| + new PassThroughFilterSourceStream(std::move(needs_all_input_source)));
|
| + second_pass_through_source->set_type_string("SECOND_PASS_THROUGH");
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| +
|
| + TestCompletionCallback callback;
|
| + int bytes_read = second_pass_through_source->Read(
|
| + output_buffer.get(), output_buffer->size(), callback.callback());
|
| +
|
| + bytes_read =
|
| + CompleteReadIfAsync(bytes_read, &callback, mock_stream, /*num_reads=*/1);
|
| + ASSERT_EQ(input.length(), static_cast<size_t>(bytes_read));
|
| + EXPECT_EQ(input, std::string(output_buffer->data(), bytes_read));
|
| + // Type string (from left to right) should be the order of data flow.
|
| + EXPECT_EQ("FIRST_PASS_THROUGH,NEEDS_ALL,SECOND_PASS_THROUGH",
|
| + second_pass_through_source->Description());
|
| +}
|
| +
|
| +// Tests that FilterData() returns multiple times for a single MockStream
|
| +// read, because there is not enough output space.
|
| +TEST_P(FilterSourceStreamTest, OutputSpaceForOneRead) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + source->AddReadResult(input.data(), input.length(), OK, GetParam());
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + // Use an extremely small buffer size (1 byte), so FilterData will need more
|
| + // output space.
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kSmallBufferSize);
|
| + MockSourceStream* mock_stream = source.get();
|
| + PassThroughFilterSourceStream stream(std::move(source));
|
| + TestCompletionCallback callback;
|
| + std::string actual_output;
|
| + while (true) {
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + if (rv == ERR_IO_PENDING)
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + if (rv == OK)
|
| + break;
|
| + ASSERT_GT(rv, OK);
|
| + ASSERT_GE(kSmallBufferSize, static_cast<size_t>(rv));
|
| + actual_output.append(output_buffer->data(), rv);
|
| + }
|
| + EXPECT_EQ(input, actual_output);
|
| +}
|
| +
|
| +// Tests that FilterData() returns multiple times for a single MockStream
|
| +// read, because the filter returns one byte at a time.
|
| +TEST_P(FilterSourceStreamTest, ThrottleSourceStream) {
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + std::string input = "hello, world!";
|
| + source->AddReadResult(input.data(), input.length(), OK, GetParam());
|
| + // Add a 0 byte read to signal EOF.
|
| + source->AddReadResult(input.data() + input.length(), 0, OK, GetParam());
|
| + scoped_refptr<IOBufferWithSize> output_buffer =
|
| + new IOBufferWithSize(kDefaultBufferSize);
|
| + MockSourceStream* mock_stream = source.get();
|
| + ThrottleSourceStream stream(std::move(source));
|
| + TestCompletionCallback callback;
|
| + std::string actual_output;
|
| + while (true) {
|
| + int rv = stream.Read(output_buffer.get(), output_buffer->size(),
|
| + callback.callback());
|
| + if (rv == ERR_IO_PENDING)
|
| + rv = CompleteReadIfAsync(rv, &callback, mock_stream, /*num_reads=*/1);
|
| + if (rv == OK)
|
| + break;
|
| + ASSERT_GT(rv, OK);
|
| + // ThrottleSourceStream returns 1 byte at a time.
|
| + ASSERT_GE(1u, static_cast<size_t>(rv));
|
| + actual_output.append(output_buffer->data(), rv);
|
| + }
|
| + EXPECT_EQ(input, actual_output);
|
| +}
|
| +
|
| +} // namespace net
|
|
|