| Index: net/filter/brotli_stream_source_unittest.cc
|
| diff --git a/net/filter/brotli_stream_source_unittest.cc b/net/filter/brotli_stream_source_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1da4d08dd15621d479f7c667c1fc1d9aa57cd893
|
| --- /dev/null
|
| +++ b/net/filter/brotli_stream_source_unittest.cc
|
| @@ -0,0 +1,269 @@
|
| +// 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 <string>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bit_cast.h"
|
| +#include "base/callback.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/path_service.h"
|
| +#include "base/run_loop.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "net/filter/brotli_stream_source.h"
|
| +#include "net/filter/mock_stream_source.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "testing/platform_test.h"
|
| +#include "testing/platform_test.h"
|
| +
|
| +namespace {
|
| +size_t kDefaultBufferSize = 4096;
|
| +size_t kSmallBufferSize = 128;
|
| +
|
| +using net::Error;
|
| +using net::BrotliStreamSource;
|
| +using net::MockStreamSource;
|
| +
|
| +// How many bytes to leave unused at the end of |source_data_|. This margin is
|
| +// present so that tests that need to append data after the zlib EOF do not run
|
| +// out of room in the output buffer.
|
| +// const size_t kEOFMargin = 64;
|
| +
|
| +class BrotliStreamSourceTest : public PlatformTest {
|
| + protected:
|
| + void SetUp() override {
|
| + PlatformTest::SetUp();
|
| +
|
| + // Get the path of data directory.
|
| + base::FilePath data_dir;
|
| + PathService::Get(base::DIR_SOURCE_ROOT, &data_dir);
|
| + data_dir = data_dir.AppendASCII("net");
|
| + data_dir = data_dir.AppendASCII("data");
|
| + data_dir = data_dir.AppendASCII("filter_unittests");
|
| +
|
| + // Read data from the original file into buffer.
|
| + base::FilePath file_path;
|
| + file_path = data_dir.AppendASCII("google.txt");
|
| + ASSERT_TRUE(base::ReadFileToString(file_path, &source_data_));
|
| + ASSERT_GE(kDefaultBufferSize, source_data_.size());
|
| +
|
| + // Read data from the encoded file into buffer.
|
| + base::FilePath encoded_file_path;
|
| + encoded_file_path = data_dir.AppendASCII("google.br");
|
| + ASSERT_TRUE(base::ReadFileToString(encoded_file_path, &encoded_buffer_));
|
| + ASSERT_GE(kDefaultBufferSize, encoded_buffer_.size());
|
| +
|
| + scoped_ptr<MockStreamSource> brotli_source(new MockStreamSource);
|
| + brotli_source_ = brotli_source.get();
|
| + brotli_stream_.reset(new BrotliStreamSource(std::move(brotli_source)));
|
| + // gzip_stream_->Init(GzipStreamSource::GZIP_STREAM_SOURCE_GZIP, true);
|
| + loop_.reset(new base::RunLoop);
|
| + }
|
| +
|
| + void ReadStream() {
|
| + last_error_ = brotli_stream_->Read(out_buffer(), out_data_size(),
|
| + &last_bytes_read_, callback());
|
| + ASSERT_LE(last_bytes_read_, out_data_size());
|
| + }
|
| +
|
| + void OnReadComplete(Error error, size_t bytes_read) {
|
| + last_error_ = error;
|
| + last_bytes_read_ = bytes_read;
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void WaitUntilCallback() {
|
| + loop_->Run();
|
| + loop_.reset(new base::RunLoop);
|
| + }
|
| + net::IOBuffer* out_buffer() { return out_buffer_.get(); }
|
| + char* out_data() { return out_buffer_->data(); }
|
| + size_t out_data_size() { return out_buffer_->size(); }
|
| +
|
| + Error last_error() { return last_error_; }
|
| + size_t last_bytes_read() { return last_bytes_read_; }
|
| +
|
| + net::StreamSource::OnReadCompleteCallback callback() {
|
| + return base::Bind(&BrotliStreamSourceTest::OnReadComplete,
|
| + base::Unretained(this));
|
| + }
|
| +
|
| + std::string source_data() { return source_data_; }
|
| +
|
| + size_t source_data_len() { return source_data_.length(); }
|
| +
|
| + std::string encoded_buffer() { return encoded_buffer_; }
|
| +
|
| + size_t encoded_len() { return encoded_buffer_.length(); }
|
| +
|
| + MockStreamSource* brotli_source() { return brotli_source_; }
|
| + scoped_refptr<net::IOBufferWithSize> out_buffer_;
|
| +
|
| + private:
|
| + MockStreamSource* brotli_source_;
|
| + scoped_ptr<BrotliStreamSource> brotli_stream_;
|
| + scoped_ptr<base::RunLoop> loop_;
|
| +
|
| + Error last_error_;
|
| + size_t last_bytes_read_;
|
| +
|
| + std::string source_data_;
|
| + std::string encoded_buffer_;
|
| +};
|
| +
|
| +// Basic scenario: decoding brotli data with big enough buffer.
|
| +TEST_F(BrotliStreamSourceTest, DecodeBrotliOneBlockSync) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), encoded_len(),
|
| + net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| +
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(source_data_len(), last_bytes_read());
|
| + EXPECT_EQ(0, memcmp(out_data(), source_data().c_str(), source_data_len()));
|
| +}
|
| +
|
| +// Basic scenario: decoding brotli data with big enough buffer.
|
| +TEST_F(BrotliStreamSourceTest, DecodeBrotliTwoBlockSync) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), 10, net::OK, true);
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str() + 10,
|
| + encoded_len() - 10, net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(source_data_len(), last_bytes_read());
|
| + EXPECT_EQ(0, memcmp(out_data(), source_data().c_str(), source_data_len()));
|
| +}
|
| +// Basic scenario: decoding brotli data with big enough buffer.
|
| +TEST_F(BrotliStreamSourceTest, DecodeBrotliOneBlockAsync) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), encoded_len(),
|
| + net::OK, false);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| +
|
| + EXPECT_EQ(net::ERR_IO_PENDING, last_error());
|
| + brotli_source()->CompleteNextRead();
|
| + WaitUntilCallback();
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(source_data_len(), last_bytes_read());
|
| + EXPECT_EQ(0, memcmp(out_data(), source_data().c_str(), source_data_len()));
|
| +}
|
| +
|
| +// Tests we can call filter repeatedly to get all the data decoded.
|
| +// To do that, we create a filter with a small buffer that can not hold all
|
| +// the input data.
|
| +TEST_F(BrotliStreamSourceTest, DecodeWithSmallBufferSync) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), encoded_len(),
|
| + net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kSmallBufferSize);
|
| +
|
| + char buffer[source_data_len()];
|
| + size_t bytes_read = 0;
|
| + do {
|
| + ReadStream();
|
| + EXPECT_GE(kSmallBufferSize, last_bytes_read());
|
| + memcpy(buffer + bytes_read, out_data(), last_bytes_read());
|
| + bytes_read += last_bytes_read();
|
| + } while (last_error() == net::OK && last_bytes_read() > 0);
|
| + EXPECT_EQ(source_data_len(), bytes_read);
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(0, memcmp(buffer, source_data().c_str(), source_data_len()));
|
| +}
|
| +
|
| +// Tests we can call filter repeatedly to get all the data decoded.
|
| +// To do that, we create a filter with a small buffer that can not hold all
|
| +// the input data.
|
| +TEST_F(BrotliStreamSourceTest, DecodeWithSmallBufferAsyncSync) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), encoded_len(),
|
| + net::OK, false);
|
| + out_buffer_ = new net::IOBufferWithSize(kSmallBufferSize);
|
| +
|
| + char buffer[source_data_len()];
|
| + size_t bytes_read = 0;
|
| + do {
|
| + ReadStream();
|
| + if (last_error() == net::ERR_IO_PENDING) {
|
| + brotli_source()->CompleteNextRead();
|
| + WaitUntilCallback();
|
| + EXPECT_LT(0u, last_bytes_read());
|
| + }
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_GE(kSmallBufferSize, last_bytes_read());
|
| + memcpy(buffer + bytes_read, out_data(), last_bytes_read());
|
| + bytes_read += last_bytes_read();
|
| + } while (last_error() == net::OK && last_bytes_read() > 0);
|
| + EXPECT_EQ(source_data_len(), bytes_read);
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(0, memcmp(buffer, source_data().c_str(), source_data_len()));
|
| +}
|
| +
|
| +// Tests we can still decode with just 1 byte buffer in the filter.
|
| +// The purpose of this test: sometimes the filter will consume input without
|
| +// generating output. Verify filter can handle it correctly.
|
| +TEST_F(BrotliStreamSourceTest, DecodeWithOneByteBuffer) {
|
| + brotli_source()->AddReadResult(encoded_buffer().c_str(), encoded_len(),
|
| + net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(1);
|
| + char buffer[source_data_len()];
|
| + size_t bytes_read = 0;
|
| + do {
|
| + ReadStream();
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_GE(1u, last_bytes_read());
|
| + memcpy(buffer + bytes_read, out_data(), last_bytes_read());
|
| + bytes_read += last_bytes_read();
|
| + } while (last_error() == net::OK && last_bytes_read() > 0);
|
| + EXPECT_EQ(source_data_len(), bytes_read);
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(0, memcmp(buffer, source_data().c_str(), source_data_len()));
|
| +}
|
| +
|
| +// Decoding deflate stream with corrupted data.
|
| +TEST_F(BrotliStreamSourceTest, DecodeCorruptedData) {
|
| + char corrupt_data[kDefaultBufferSize];
|
| + int corrupt_data_len = encoded_len();
|
| + memcpy(corrupt_data, encoded_buffer().c_str(), encoded_len());
|
| + int pos = corrupt_data_len / 2;
|
| + corrupt_data[pos] = !corrupt_data[pos];
|
| +
|
| + brotli_source()->AddReadResult(corrupt_data, corrupt_data_len, net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| + // Expect failures
|
| + EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED, last_error());
|
| +}
|
| +
|
| +// Decoding deflate stream with missing data.
|
| +TEST_F(BrotliStreamSourceTest, DecodeMissingData) {
|
| + char corrupt_data[kDefaultBufferSize];
|
| + int corrupt_data_len = encoded_len();
|
| + memcpy(corrupt_data, encoded_buffer().c_str(), encoded_len());
|
| +
|
| + int pos = corrupt_data_len / 2;
|
| + int len = corrupt_data_len - pos - 1;
|
| + memmove(&corrupt_data[pos], &corrupt_data[pos + 1], len);
|
| + --corrupt_data_len;
|
| +
|
| + // Decode the corrupted data with filter
|
| + brotli_source()->AddReadResult(corrupt_data, corrupt_data_len, net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| + // Expect failures
|
| + EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED, last_error());
|
| +}
|
| +
|
| +// Decoding brotli stream with empty output data.
|
| +TEST_F(BrotliStreamSourceTest, DecodeEmptyData) {
|
| + char data[1] = {6}; // WBITS = 16, ISLAST = 1, ISLASTEMPTY = 1
|
| + int data_len = 1;
|
| +
|
| + // Decode the corrupted data with filter
|
| + brotli_source()->AddReadResult(data, data_len, net::OK, true);
|
| + out_buffer_ = new net::IOBufferWithSize(kDefaultBufferSize);
|
| + ReadStream();
|
| + EXPECT_EQ(net::OK, last_error());
|
| + EXPECT_EQ(0u, last_bytes_read());
|
| +}
|
| +
|
| +} // namespace
|
|
|