| Index: net/filter/sdch_source_stream_unittest.cc
|
| diff --git a/net/filter/sdch_source_stream_unittest.cc b/net/filter/sdch_source_stream_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e8592cd9492b1d62e53a90592d1e347aa227c588
|
| --- /dev/null
|
| +++ b/net/filter/sdch_source_stream_unittest.cc
|
| @@ -0,0 +1,515 @@
|
| +// 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/memory/ptr_util.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "net/base/test_completion_callback.h"
|
| +#include "net/filter/gzip_source_stream.h"
|
| +#include "net/filter/mock_source_stream.h"
|
| +#include "net/filter/sdch_source_stream.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "third_party/zlib/zlib.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +const size_t kBufferSize = 4096;
|
| +const char* kDictionaryError = "dictionary error";
|
| +const char* kDecodingError = "decoding error";
|
| +const size_t kSmallBufferSize = 1;
|
| +
|
| +// Provide sample data and compression results with a sample VCDIFF dictionary.
|
| +// Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
|
| +static const char kTestVcdiffDictionary[] =
|
| + "DictionaryFor"
|
| + "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
|
| +
|
| +// Pre-compression test data. Note that we pad with a lot of highly gzip
|
| +// compressible content to help to exercise the chaining pipeline. That is why
|
| +// there are a PILE of zeros at the start and end.
|
| +static const char kTestData[] =
|
| + "0000000000000000000000000000000000000000000000"
|
| + "0000000000000000000000000000TestData "
|
| + "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
|
| + "00000000000000000000000000000000000000000000000000000000000000000000000000"
|
| + "000000000000000000000000000000000000000\n";
|
| +static const char kSdchCompressedTestData[] =
|
| + "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
|
| + "00000000000000000000000000000000000000000000000000000000000000000000000000"
|
| + "TestData 00000000000000000000000000000000000000000000000000000000000000000"
|
| + "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
|
| +
|
| +// Helper function to perform gzip compression of data.
|
| +static std::string gzip_compress(const std::string& input,
|
| + size_t input_size,
|
| + size_t* output_size) {
|
| + z_stream zlib_stream;
|
| + memset(&zlib_stream, 0, sizeof(zlib_stream));
|
| + int code;
|
| +
|
| + // Initialize zlib
|
| + code =
|
| + deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
|
| + 8, // DEF_MEM_LEVEL
|
| + Z_DEFAULT_STRATEGY);
|
| +
|
| + CHECK_EQ(Z_OK, code);
|
| +
|
| + // Fill in zlib control block
|
| + zlib_stream.next_in = bit_cast<Bytef*>(input.data());
|
| + zlib_stream.avail_in = input_size;
|
| +
|
| + // Assume we can compress into similar buffer (add 100 bytes to be sure).
|
| + size_t gzip_compressed_length = zlib_stream.avail_in + 100;
|
| + std::unique_ptr<char[]> gzip_compressed(new char[gzip_compressed_length]);
|
| + zlib_stream.next_out = bit_cast<Bytef*>(gzip_compressed.get());
|
| + zlib_stream.avail_out = gzip_compressed_length;
|
| +
|
| + // The GZIP header (see RFC 1952):
|
| + // +---+---+---+---+---+---+---+---+---+---+
|
| + // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
|
| + // +---+---+---+---+---+---+---+---+---+---+
|
| + // ID1 \037
|
| + // ID2 \213
|
| + // CM \010 (compression method == DEFLATE)
|
| + // FLG \000 (special flags that we do not support)
|
| + // MTIME Unix format modification time (0 means not available)
|
| + // XFL 2-4? DEFLATE flags
|
| + // OS ???? Operating system indicator (255 means unknown)
|
| + //
|
| + // Header value we generate:
|
| + const char kGZipHeader[] = {'\037', '\213', '\010', '\000', '\000',
|
| + '\000', '\000', '\000', '\002', '\377'};
|
| + CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader));
|
| + memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader));
|
| + zlib_stream.next_out += sizeof(kGZipHeader);
|
| + zlib_stream.avail_out -= sizeof(kGZipHeader);
|
| +
|
| + // Do deflate
|
| + code = deflate(&zlib_stream, Z_FINISH);
|
| + gzip_compressed_length -= zlib_stream.avail_out;
|
| + std::string compressed(gzip_compressed.get(), gzip_compressed_length);
|
| + deflateEnd(&zlib_stream);
|
| + *output_size = gzip_compressed_length;
|
| + return compressed;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class SdchSourceStreamTest : public ::testing::Test,
|
| + public SdchSourceStream::Delegate {
|
| + public:
|
| + SdchSourceStreamTest()
|
| + : dictionary_error_handled_(false), decoding_error_handled_(false) {}
|
| +
|
| + void SetUp() override {
|
| + ::testing::Test::SetUp();
|
| +
|
| + out_buffer_ = new IOBufferWithSize(kBufferSize);
|
| +
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + source_ = source.get();
|
| + sdch_source_.reset(new SdchSourceStream(std::move(source), this));
|
| + }
|
| +
|
| + IOBuffer* out_buffer() { return out_buffer_.get(); }
|
| + char* out_data() { return out_buffer_->data(); }
|
| + size_t out_buffer_size() { return out_buffer_->size(); }
|
| +
|
| + MockSourceStream* mock_source() { return source_; }
|
| + SdchSourceStream* sdch_source() { return sdch_source_.get(); }
|
| +
|
| + bool dictionary_error_handled() { return dictionary_error_handled_; }
|
| + bool decoding_error_handled() { return decoding_error_handled_; }
|
| + std::string last_get_dictionary_id() { return last_get_dictionary_id_; }
|
| +
|
| + int ReadStream(const TestCompletionCallback& callback) {
|
| + return sdch_source()->Read(out_buffer(), out_buffer_size(),
|
| + callback.callback());
|
| + }
|
| +
|
| + void SetTestDictionary(const std::string& dictionary_id,
|
| + const std::string& dictionary_text) {
|
| + test_dictionary_id_ = dictionary_id;
|
| + test_dictionary_text_ = dictionary_text;
|
| + }
|
| +
|
| + void AppendDictionaryIdTo(std::string* resp, std::string* server_id) {
|
| + std::string client_id;
|
| + SdchManager::GenerateHash(kTestVcdiffDictionary, &client_id, server_id);
|
| + SetTestDictionary(*server_id, kTestVcdiffDictionary);
|
| + std::string response(server_id->data(), server_id->size());
|
| + response.append("\0");
|
| + resp->append(response.data(), server_id->size() + 1);
|
| + }
|
| +
|
| + // SdchSourceStream::Delegate implementation.
|
| + bool OnDictionaryError(SdchSourceStream* source) override {
|
| + dictionary_error_handled_ = true;
|
| + sdch_source()->ReplaceOutput(kDictionaryError, strlen(kDictionaryError));
|
| + return true;
|
| + }
|
| +
|
| + bool OnDecodingError(SdchSourceStream* source) override {
|
| + decoding_error_handled_ = true;
|
| + sdch_source()->ReplaceOutput(kDecodingError, strlen(kDictionaryError));
|
| + return true;
|
| + }
|
| +
|
| + bool OnGetDictionary(const std::string& server_id,
|
| + const std::string** text) override {
|
| + last_get_dictionary_id_ = server_id;
|
| + if (server_id == test_dictionary_id_) {
|
| + *text = &test_dictionary_text_;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + protected:
|
| + scoped_refptr<IOBufferWithSize> out_buffer_;
|
| +
|
| + private:
|
| + // Owned by sdch_source_.
|
| + MockSourceStream* source_;
|
| + std::unique_ptr<SdchSourceStream> sdch_source_;
|
| +
|
| + std::string test_dictionary_id_;
|
| + std::string test_dictionary_text_;
|
| +
|
| + std::string last_get_dictionary_id_;
|
| + bool dictionary_error_handled_;
|
| + bool decoding_error_handled_;
|
| +};
|
| +
|
| +TEST_F(SdchSourceStreamTest, EmptyStream) {
|
| + mock_source()->AddReadResult("", 0, OK, true);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| + EXPECT_EQ(OK, bytes_read);
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +// Ensure that GetDictionary() is not called at all if the SDCH dictionary ID is
|
| +// malformed.
|
| +TEST_F(SdchSourceStreamTest, BogusDictionaryId) {
|
| + char id[] = {0x1f, '0', '0', '0', '0', '0', '0', '0', 0x0};
|
| + mock_source()->AddReadResult(id, sizeof(id), OK, true);
|
| + SetTestDictionary(id, "...");
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| +
|
| + EXPECT_TRUE(dictionary_error_handled());
|
| + EXPECT_EQ("", last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(strlen(kDictionaryError)), bytes_read);
|
| + EXPECT_EQ(0, memcmp(kDictionaryError, out_data(), bytes_read));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +// Ensure that the stream's dictionary error handler is called if GetDictionary
|
| +// returns no dictionary.
|
| +TEST_F(SdchSourceStreamTest, NoDictionaryError) {
|
| + char id[] = "00000000";
|
| + mock_source()->AddReadResult(id, sizeof(id), OK, true);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| + EXPECT_EQ(static_cast<int>(strlen(kDictionaryError)), bytes_read);
|
| + EXPECT_TRUE(dictionary_error_handled());
|
| + EXPECT_EQ(id, last_get_dictionary_id());
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DictionaryLoaded) {
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, true);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| + // Decoded response should be empty.
|
| + EXPECT_EQ(0, bytes_read);
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DecompressOneBlockSync) {
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, true);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1, OK, true);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), bytes_read));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DecompressOneBlockAsync) {
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, false);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1, OK, false);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| + EXPECT_EQ(ERR_IO_PENDING, bytes_read);
|
| + mock_source()->CompleteNextRead();
|
| + mock_source()->CompleteNextRead();
|
| + int rv = callback.WaitForResult();
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), rv);
|
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), rv));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DecompressWithSmallBufferSync) {
|
| + out_buffer_ = new IOBufferWithSize(kSmallBufferSize);
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, true);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1, OK, true);
|
| + char buffer[sizeof(kTestData)];
|
| + size_t total_bytes_read = 0;
|
| + int bytes_read = 0;
|
| + TestCompletionCallback callback;
|
| + do {
|
| + bytes_read = ReadStream(callback);
|
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(bytes_read));
|
| + memcpy(buffer + total_bytes_read, out_data(), bytes_read);
|
| + total_bytes_read += bytes_read;
|
| + } while (bytes_read > 0);
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(sizeof(kTestData) - 1, total_bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, buffer, total_bytes_read));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DecompressWithSmallBufferAsync) {
|
| + out_buffer_ = new IOBufferWithSize(kSmallBufferSize);
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, false);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1, OK, false);
|
| + char buffer[sizeof(kTestData)];
|
| + size_t total_bytes_read = 0;
|
| + int bytes_read = 0;
|
| + TestCompletionCallback callback;
|
| + do {
|
| + bytes_read = ReadStream(callback);
|
| + if (bytes_read == ERR_IO_PENDING) {
|
| + mock_source()->CompleteNextRead();
|
| + mock_source()->CompleteNextRead();
|
| + bytes_read = callback.WaitForResult();
|
| + }
|
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(bytes_read));
|
| + memcpy(buffer + total_bytes_read, out_data(), bytes_read);
|
| + total_bytes_read += bytes_read;
|
| + } while (bytes_read > 0);
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(sizeof(kTestData) - 1, total_bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, buffer, total_bytes_read));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, DecompressTwoBlocksSync) {
|
| + std::string response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&response, &server_id);
|
| + mock_source()->AddReadResult(response.data(), response.size(), OK, true);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData, 32, OK, true);
|
| + mock_source()->AddReadResult(kSdchCompressedTestData + 32,
|
| + sizeof(kSdchCompressedTestData) - 1 - 32, OK,
|
| + true);
|
| + TestCompletionCallback callback;
|
| + int bytes_read = ReadStream(callback);
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), bytes_read));
|
| + EXPECT_EQ("SDCH", sdch_source()->OrderedTypeStringList());
|
| +}
|
| +
|
| +// Test that filters can be cascaded (chained) so that the output of one filter
|
| +// is processed by the next one. This is most critical for SDCH, which is
|
| +// routinely followed by gzip (during encoding). The filter we'll test for will
|
| +// do the gzip decoding first, and then decode the SDCH content.
|
| +TEST_F(SdchSourceStreamTest, FilterChaining) {
|
| + {
|
| + std::string sdch_response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&sdch_response, &server_id);
|
| + sdch_response.append(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1);
|
| +
|
| + size_t expected_length =
|
| + server_id.length() + sizeof(kSdchCompressedTestData);
|
| + size_t gzip_length;
|
| + std::string gzip_compressed_sdch =
|
| + gzip_compress(sdch_response, expected_length, &gzip_length);
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + source->AddReadResult(gzip_compressed_sdch.data(), gzip_length, OK, true);
|
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create(
|
| + std::move(source), GzipSourceStream::GZIP_SOURCE_STREAM_GZIP);
|
| + std::unique_ptr<SdchSourceStream> sdch_source(
|
| + new SdchSourceStream(std::move(gzip_source), this));
|
| + TestCompletionCallback callback;
|
| + int bytes_read =
|
| + sdch_source->Read(out_buffer(), out_buffer_size(), callback.callback());
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), bytes_read));
|
| + EXPECT_EQ("GZIP,SDCH", sdch_source->OrderedTypeStringList());
|
| + }
|
| +
|
| + {
|
| + // Try a small buffer
|
| + out_buffer_ = new IOBufferWithSize(kSmallBufferSize);
|
| + std::string sdch_response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&sdch_response, &server_id);
|
| + sdch_response.append(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1);
|
| +
|
| + size_t expected_length =
|
| + server_id.length() + sizeof(kSdchCompressedTestData);
|
| + size_t gzip_length;
|
| + std::string gzip_compressed_sdch =
|
| + gzip_compress(sdch_response, expected_length, &gzip_length);
|
| + std::unique_ptr<MockSourceStream> source(new MockSourceStream);
|
| + source->AddReadResult(gzip_compressed_sdch.data(), gzip_length, OK, true);
|
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create(
|
| + std::move(source), GzipSourceStream::GZIP_SOURCE_STREAM_GZIP);
|
| + std::unique_ptr<SdchSourceStream> sdch_source(
|
| + new SdchSourceStream(std::move(gzip_source), this));
|
| + char buffer[sizeof(kTestData)];
|
| + size_t total_bytes_read = 0;
|
| + int bytes_read = 0;
|
| + TestCompletionCallback callback;
|
| + do {
|
| + bytes_read = sdch_source->Read(out_buffer(), out_buffer_size(),
|
| + callback.callback());
|
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(bytes_read));
|
| + memcpy(buffer + total_bytes_read, out_data(), bytes_read);
|
| + total_bytes_read += bytes_read;
|
| + } while (bytes_read > 0);
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(sizeof(kTestData) - 1, total_bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, buffer, total_bytes_read));
|
| + EXPECT_EQ("GZIP,SDCH", sdch_source->OrderedTypeStringList());
|
| + }
|
| +}
|
| +
|
| +TEST_F(SdchSourceStreamTest, FilterChainingAsync) {
|
| + {
|
| + std::string sdch_response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&sdch_response, &server_id);
|
| + sdch_response.append(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1);
|
| +
|
| + size_t expected_length =
|
| + server_id.length() + sizeof(kSdchCompressedTestData);
|
| + size_t gzip_length;
|
| + std::string gzip_compressed_sdch =
|
| + gzip_compress(sdch_response, expected_length, &gzip_length);
|
| + MockSourceStream* source = new MockSourceStream;
|
| + source->AddReadResult(gzip_compressed_sdch.data(), gzip_length, OK, false);
|
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create(
|
| + base::WrapUnique(source), GzipSourceStream::GZIP_SOURCE_STREAM_GZIP);
|
| + std::unique_ptr<SdchSourceStream> sdch_source(
|
| + new SdchSourceStream(std::move(gzip_source), this));
|
| + TestCompletionCallback callback;
|
| + int bytes_read =
|
| + sdch_source->Read(out_buffer(), out_buffer_size(), callback.callback());
|
| + EXPECT_EQ(ERR_IO_PENDING, bytes_read);
|
| + source->CompleteNextRead();
|
| + bytes_read = callback.WaitForResult();
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), bytes_read));
|
| + EXPECT_EQ("GZIP,SDCH", sdch_source->OrderedTypeStringList());
|
| + }
|
| +
|
| + {
|
| + // Try a small buffer
|
| + out_buffer_ = new IOBufferWithSize(kSmallBufferSize);
|
| + std::string sdch_response;
|
| + std::string server_id;
|
| + AppendDictionaryIdTo(&sdch_response, &server_id);
|
| + sdch_response.append(kSdchCompressedTestData,
|
| + sizeof(kSdchCompressedTestData) - 1);
|
| +
|
| + size_t expected_length =
|
| + server_id.length() + sizeof(kSdchCompressedTestData);
|
| + size_t gzip_length;
|
| + std::string gzip_compressed_sdch =
|
| + gzip_compress(sdch_response, expected_length, &gzip_length);
|
| + MockSourceStream* source = new MockSourceStream;
|
| + source->AddReadResult(gzip_compressed_sdch.data(), gzip_length, OK, false);
|
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create(
|
| + base::WrapUnique(source), GzipSourceStream::GZIP_SOURCE_STREAM_GZIP);
|
| + std::unique_ptr<SdchSourceStream> sdch_source(
|
| + new SdchSourceStream(std::move(gzip_source), this));
|
| + char buffer[sizeof(kTestData)];
|
| + size_t total_bytes_read = 0;
|
| + int bytes_read = 0;
|
| + TestCompletionCallback callback;
|
| + do {
|
| + bytes_read = sdch_source->Read(out_buffer(), out_buffer_size(),
|
| + callback.callback());
|
| + if (bytes_read == ERR_IO_PENDING) {
|
| + source->CompleteNextRead();
|
| + bytes_read = callback.WaitForResult();
|
| + }
|
| +
|
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(bytes_read));
|
| + memcpy(buffer + total_bytes_read, out_data(), bytes_read);
|
| + total_bytes_read += bytes_read;
|
| + } while (bytes_read > 0);
|
| +
|
| + EXPECT_FALSE(dictionary_error_handled());
|
| + EXPECT_FALSE(decoding_error_handled());
|
| + EXPECT_EQ(server_id, last_get_dictionary_id());
|
| + EXPECT_EQ(sizeof(kTestData) - 1, total_bytes_read);
|
| + EXPECT_EQ(0, memcmp(kTestData, buffer, total_bytes_read));
|
| + EXPECT_EQ("GZIP,SDCH", sdch_source->OrderedTypeStringList());
|
| + }
|
| +}
|
| +
|
| +} // namespace net
|
|
|