Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(119)

Unified Diff: net/filter/filter_source_stream_unittest.cc

Issue 2251853002: Add net::SourceStream and net::FilterSourceStream (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix a typo Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..5d6b609517fa23c309a1b68668c02a887f6adbc0
--- /dev/null
+++ b/net/filter/filter_source_stream_unittest.cc
@@ -0,0 +1,308 @@
+// 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;
+
+// A FilterSourceStream that needs all input data before it can return non-zero
+// bytes read.
+class NeedAllInputFilterSourceStream : public FilterSourceStream {
+ public:
+ NeedAllInputFilterSourceStream(std::unique_ptr<SourceStream> upstream,
+ size_t expected_remaining_bytes)
+ : FilterSourceStream(SourceStream::TYPE_NONE, std::move(upstream)),
+ expected_remaining_bytes_(expected_remaining_bytes) {}
+
+ int FilterData(IOBuffer* output_buffer,
+ size_t output_buffer_size,
+ DrainableIOBuffer* input_buffer) override {
+ buffer_.append(input_buffer->data(), input_buffer->BytesRemaining());
+ expected_remaining_bytes_ -= input_buffer->BytesRemaining();
+ input_buffer->DidConsume(input_buffer->BytesRemaining());
+ if (expected_remaining_bytes_ > 0) {
+ // Keep returning 0 byte read until all |expected_remaining_bytes| have
+ // been read from |upstream|.
+ return OK;
+ }
+ EXPECT_EQ(0u, expected_remaining_bytes_);
mmenke 2016/08/18 16:18:42 This seems weird - maybe EXPECT_LE(expected_remain
xunjieli 2016/08/19 14:31:40 Done.
+ size_t bytes_to_filter = std::min(buffer_.length(), 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);
+ }
+
+ std::string GetTypeAsString() const override { return ""; }
mmenke 2016/08/18 16:18:42 It's a bit silly, but suggest a couple test cases
xunjieli 2016/08/19 14:31:40 Done.
+
+ private:
+ // Buffer data that is yet to be passed through to the caller.
+ std::string buffer_;
+ // Expected remaining bytes to be received from |upstream|.
+ size_t expected_remaining_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(NeedAllInputFilterSourceStream);
+};
+
+// A FilterSourceStream passes through data unchanged to consumer.
+class PassThroughFilterSourceStream : public FilterSourceStream {
+ public:
+ PassThroughFilterSourceStream(std::unique_ptr<SourceStream> upstream)
mmenke 2016/08/18 16:18:42 explicit
xunjieli 2016/08/19 14:31:40 Done.
+ : FilterSourceStream(SourceStream::TYPE_NONE, std::move(upstream)) {}
+
+ int FilterData(IOBuffer* output_buffer,
+ size_t output_buffer_size,
+ DrainableIOBuffer* input_buffer) override {
+ int bytes_flushed = Flush(output_buffer, output_buffer_size);
+ output_buffer_size -= bytes_flushed;
+ size_t bytes_to_filter =
+ base::checked_cast<size_t>(input_buffer->BytesRemaining());
+ if (output_buffer_size < bytes_to_filter) {
+ buffer_.append(input_buffer->data() + output_buffer_size,
+ bytes_to_filter - output_buffer_size);
+ bytes_to_filter = output_buffer_size;
+ }
+ memcpy(output_buffer->data() + bytes_flushed, input_buffer->data(),
+ bytes_to_filter);
+ input_buffer->DidConsume(input_buffer->BytesRemaining());
+ return bytes_flushed + base::checked_cast<int>(bytes_to_filter);
mmenke 2016/08/18 16:18:42 This seems much more complicated than it needs to
xunjieli 2016/08/19 14:31:40 Done. You are right! I mush have been day dreaming
+ }
+
+ std::string GetTypeAsString() const override { return ""; }
+
+ private:
+ int Flush(IOBuffer* output_buffer, size_t output_buffer_size) {
+ int to_flush = std::min(buffer_.length(), output_buffer_size);
+ memcpy(output_buffer->data(), buffer_.data(), to_flush);
+ buffer_.clear();
+ return to_flush;
+ }
+
+ // Buffer data that is yet to be passed through to the caller.
+ std::string buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PassThroughFilterSourceStream);
+};
+
+// A FilterSourceStream return an error code in FilterData().
+class ErrorFilterSourceStream : public FilterSourceStream {
+ public:
+ ErrorFilterSourceStream(std::unique_ptr<SourceStream> upstream)
mmenke 2016/08/18 16:18:42 explicit
xunjieli 2016/08/19 14:31:40 Done.
+ : FilterSourceStream(SourceStream::TYPE_NONE, std::move(upstream)) {}
+
+ int FilterData(IOBuffer* output_buffer,
+ size_t output_buffer_size,
+ DrainableIOBuffer* input_buffer) override {
+ return ERR_CONTENT_DECODING_FAILED;
mmenke 2016/08/18 16:18:42 Should we check the case where the second FilterDa
xunjieli 2016/08/19 14:31:40 I added a second read to FilterDataReturnError tes
+ }
+
+ std::string GetTypeAsString() const override { return ""; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ErrorFilterSourceStream);
+};
mmenke 2016/08/18 16:18:42 Suggestions: * A test that fills both the drai
mmenke 2016/08/18 16:59:20 Oops, ignore this one - forgot to delete it, you'r
xunjieli 2016/08/19 14:31:40 Acknowledged.
+
+} // namespace
+
+// Tests that a FilterSourceStream subclass (NeedAllInputFilterSourceStream)
+// 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(FilterSourceStreamTest, FilterDataReturnNoBytesExceptLast) {
mmenke 2016/08/18 16:18:42 Suggest an async version of this test - mainly thi
xunjieli 2016/08/19 14:31:40 Done.
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ std::string input("hello, world!");
+ int read_size = 2;
+ int num_bytes_remaining = input.length();
+ // Add a sequence of small reads.
+ while (num_bytes_remaining > 0) {
+ int bytes_added = std::min(read_size, num_bytes_remaining);
+ source->AddReadResult(input.data() + input.length() - num_bytes_remaining,
mmenke 2016/08/18 16:18:42 Maybe count up using |offset| or |position| or som
xunjieli 2016/08/19 14:31:40 Great idea! Done.
+ bytes_added, OK, /*synchronous=*/true);
mmenke 2016/08/18 16:18:42 optional: May want to make the last parameter an
xunjieli 2016/08/19 14:31:40 Done.
+ num_bytes_remaining -= bytes_added;
+ }
+ NeedAllInputFilterSourceStream 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());
+ EXPECT_EQ(static_cast<int>(input.length()), rv);
mmenke 2016/08/18 16:18:42 ASSERT_EQ (To prevent, or reduce the chances of, B
xunjieli 2016/08/19 14:31:40 Done.
+ EXPECT_EQ(0, memcmp(input.data(), output_buffer->data(), rv));
mmenke 2016/08/18 16:18:42 This provides better output on failure if you use
xunjieli 2016/08/19 14:31:40 Done.
+}
+
+// Tests that FilterData() returns 0 because the upstream gives an EOF
+// synchronously.
+TEST(FilterSourceStreamTest, FilterDataReturnNoBytesSync) {
mmenke 2016/08/18 16:18:42 Hrm...Should have a version of this test where the
xunjieli 2016/08/19 14:31:40 Done.
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ std::string input;
+ source->AddReadResult(input.data(), 0, OK, /*synchronous=*/true);
+ 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());
+ EXPECT_EQ(OK, rv);
+}
+
+// Tests that FilterData() returns 0 because the upstream gives an EOF
+// asynchronously.
+TEST(FilterSourceStreamTest, FilterDataReturnNoBytesSyncAsync) {
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ MockSourceStream* source_ptr = source.get();
+ std::string input;
+ source->AddReadResult(input.data(), 0, OK, /*synchronous=*/false);
+ scoped_refptr<IOBufferWithSize> output_buffer =
+ new IOBufferWithSize(kDefaultBufferSize);
+ TestCompletionCallback callback;
+ PassThroughFilterSourceStream stream(std::move(source));
+ int rv = stream.Read(output_buffer.get(), output_buffer->size(),
+ callback.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ source_ptr->CompleteNextRead();
+ EXPECT_EQ(OK, callback.WaitForResult());
+}
+
+// Tests that FilterData() returns non-zero bytes because the upstream
+// returns data synchronously.
+TEST(FilterSourceStreamTest, FilterDataReturnDataSync) {
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ std::string input = "hello, world!";
+ int read_size = 2;
+ int num_bytes_remaining = input.length();
+ // Add a sequence of small reads.
+ while (num_bytes_remaining > 0) {
+ int bytes_added = std::min(read_size, num_bytes_remaining);
+ source->AddReadResult(input.data() + input.length() - num_bytes_remaining,
+ bytes_added, OK, /*synchronous=*/true);
+ num_bytes_remaining -= bytes_added;
+ }
+ // Add a 0 byte read to signal EOF.
+ source->AddReadResult(input.data() + input.length(), 0, OK,
+ /*synchronous=*/true);
+
+ PassThroughFilterSourceStream stream(std::move(source));
+ scoped_refptr<IOBufferWithSize> output_buffer =
+ new IOBufferWithSize(kDefaultBufferSize);
+ TestCompletionCallback callback;
+ std::string actual_output;
+ int rv = OK;
+ do {
mmenke 2016/08/18 16:18:42 I recommend against using do/while loops - they te
xunjieli 2016/08/19 14:31:40 Done.
+ rv = stream.Read(output_buffer.get(), output_buffer->size(),
+ callback.callback());
+ EXPECT_GE(read_size, rv);
+ EXPECT_GE(rv, OK);
+ actual_output.append(output_buffer->data(), rv);
+ } while (rv != OK);
+ EXPECT_EQ(actual_output, input);
+}
+
+// Tests that FilterData() returns non-zero bytes because the upstream
+// returns data asynchronously.
+TEST(FilterSourceStreamTest, FilterDataReturnDataAsync) {
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ MockSourceStream* source_ptr = source.get();
+ std::string input = "hello, world!";
+ int read_size = 2;
+ int num_bytes_remaining = input.length();
+ // Add a sequence of small reads.
+ while (num_bytes_remaining > 0) {
+ int bytes_added = std::min(read_size, num_bytes_remaining);
+ source->AddReadResult(input.data() + input.length() - num_bytes_remaining,
+ bytes_added, OK, /*synchronous=*/false);
+ num_bytes_remaining -= bytes_added;
+ }
+ // Add a 0 byte read to signal EOF.
+ source->AddReadResult(input.data() + input.length(), 0, OK,
+ /*synchronous=*/false);
+
+ scoped_refptr<IOBufferWithSize> output_buffer =
+ new IOBufferWithSize(kDefaultBufferSize);
+ PassThroughFilterSourceStream stream(std::move(source));
+ TestCompletionCallback callback;
+ std::string actual_output;
+ int rv = OK;
+ while (true) {
+ rv = stream.Read(output_buffer.get(), output_buffer->size(),
+ callback.callback());
+ if (rv == OK)
+ break;
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ source_ptr->CompleteNextRead();
+ rv = callback.WaitForResult();
+ EXPECT_GE(read_size, rv);
+ EXPECT_GE(rv, OK);
+ actual_output.append(output_buffer->data(), rv);
+ };
+ EXPECT_EQ(actual_output, input);
+}
+
+// Tests that FilterData() returns non-zero bytes and output buffer size is
+// smaller than the number of bytes read from the upstream.
+TEST(FilterSourceStreamTest, FilterDataNeedsOutputSpace) {
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ std::string input = "hello, world!";
+ int read_size = 2;
+ int num_bytes_remaining = input.length();
+ // Add a sequence of small reads.
+ while (num_bytes_remaining > 0) {
+ int bytes_added = std::min(read_size, num_bytes_remaining);
+ source->AddReadResult(input.data() + input.length() - num_bytes_remaining,
+ bytes_added, OK, /*synchronous=*/true);
+ num_bytes_remaining -= bytes_added;
+ }
+ // Add a 0 byte read to signal EOF.
+ source->AddReadResult(input.data() + input.length(), 0, OK,
+ /*synchronous=*/true);
+
+ // Use an extremely small buffer size, so FilterData will need more output
+ // space.
+ scoped_refptr<IOBufferWithSize> output_buffer =
+ new IOBufferWithSize(kSmallBufferSize);
+ PassThroughFilterSourceStream stream(std::move(source));
+ TestCompletionCallback callback;
+ std::string actual_output;
+ int rv = OK;
+ do {
+ rv = stream.Read(output_buffer.get(), output_buffer->size(),
+ callback.callback());
+ EXPECT_GE(rv, OK);
+ EXPECT_GE(kSmallBufferSize, static_cast<size_t>(rv));
+ actual_output.append(output_buffer->data(), rv);
+ } while (rv != OK);
+ EXPECT_EQ(actual_output, input);
+}
+
+// Tests that FilterData() returns an error code, which is then surfaced as the
+// result of calling Read().
+TEST(FilterSourceStreamTest, FilterDataReturnError) {
+ std::unique_ptr<MockSourceStream> source(new MockSourceStream);
+ std::string input;
+ source->AddReadResult(input.data(), 0, OK, /*synchronous=*/true);
+ scoped_refptr<IOBufferWithSize> output_buffer =
+ new IOBufferWithSize(kDefaultBufferSize);
+ ErrorFilterSourceStream stream(std::move(source));
+ TestCompletionCallback callback;
+ int rv = stream.Read(output_buffer.get(), output_buffer->size(),
+ callback.callback());
+ EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
+}
mmenke 2016/08/18 16:18:42 Suggestion: Two tests where one read from the moc
xunjieli 2016/08/19 14:31:40 Done. I've added NeedsOutputSpaceForOneRead Throt
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698