Chromium Code Reviews| 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..c5b78f55493588e5460a1b7a87d941b226a98567 |
| --- /dev/null |
| +++ b/net/filter/sdch_source_stream_unittest.cc |
| @@ -0,0 +1,517 @@ |
| +// 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* 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::TestWithParam<MockSourceStream::Mode>, |
| + public SdchSourceStream::Delegate { |
| + public: |
| + SdchSourceStreamTest() |
| + : dictionary_id_error_handled_(false), |
| + get_dictionary_error_handled_(false), |
| + decoding_error_handled_(false), |
| + error_recover_(NONE) {} |
| + |
| + 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)); |
| + } |
| + |
| + // If MockSourceStream::Mode is ASYNC, completes 1 read 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) { |
| + if (GetParam() == MockSourceStream::ASYNC) { |
| + EXPECT_EQ(ERR_IO_PENDING, previous_result); |
| + mock_stream->CompleteNextRead(); |
| + return callback->WaitForResult(); |
| + } |
| + return previous_result; |
| + } |
| + |
| + int CompleteReadIfAsync(int previous_result, |
| + TestCompletionCallback* callback, |
| + MockSourceStream* mock_stream, |
| + int 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; |
| + } |
| + |
| + 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_id_error_handled() { return dictionary_id_error_handled_; } |
| + bool get_dictionary_error_handled() { return get_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 SetErrorRecover(SdchSourceStream::Delegate::ErrorRecover error_recover, |
| + const std::string& replace_output) { |
| + error_recover_ = error_recover; |
| + replace_output_ = replace_output; |
| + } |
| + |
| + 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. |
| + SdchSourceStream::Delegate::ErrorRecover OnDictionaryIdError( |
| + SdchSourceStream* source, |
| + std::string* replace_output) override { |
| + dictionary_id_error_handled_ = true; |
| + if (error_recover_ == REPLACE_OUTPUT) |
| + *replace_output = replace_output_; |
| + return error_recover_; |
| + } |
| + |
| + SdchSourceStream::Delegate::ErrorRecover OnGetDictionaryError( |
| + SdchSourceStream* source, |
| + std::string* replace_output) override { |
| + get_dictionary_error_handled_ = true; |
| + if (error_recover_ == REPLACE_OUTPUT) |
| + *replace_output = replace_output_; |
| + return error_recover_; |
| + } |
| + |
| + SdchSourceStream::Delegate::ErrorRecover OnDecodingError( |
| + SdchSourceStream* source, |
| + std::string* replace_output) override { |
| + decoding_error_handled_ = true; |
| + if (error_recover_ == REPLACE_OUTPUT) |
| + *replace_output = replace_output_; |
| + return error_recover_; |
| + } |
| + |
| + 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_id_error_handled_; |
| + bool get_dictionary_error_handled_; |
| + bool decoding_error_handled_; |
| + SdchSourceStream::Delegate::ErrorRecover error_recover_; |
| + std::string replace_output_; |
| +}; |
| + |
| +INSTANTIATE_TEST_CASE_P(SdchSourceStreamTests, |
| + SdchSourceStreamTest, |
| + ::testing::Values(MockSourceStream::SYNC, |
| + MockSourceStream::ASYNC)); |
| + |
| +TEST_P(SdchSourceStreamTest, EmptyStream) { |
| + mock_source()->AddReadResult("", 0, OK, GetParam()); |
| + TestCompletionCallback callback; |
| + int result = ReadStream(callback); |
| + result = CompleteReadIfAsync(result, &callback, mock_source()); |
| + EXPECT_EQ(OK, result); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +// Ensure that GetDictionary() is not called at all if the SDCH dictionary ID is |
| +// malformed. |
| +TEST_P(SdchSourceStreamTest, BogusDictionaryId) { |
| + char id[] = {0x1f, '0', '0', '0', '0', '0', '0', '0', 0x0}; |
| + mock_source()->AddReadResult(id, sizeof(id), OK, GetParam()); |
| + SetTestDictionary(id, "..."); |
| + SetErrorRecover(SdchSourceStream::Delegate::PASS_THROUGH, std::string()); |
| + TestCompletionCallback callback; |
| + int result = ReadStream(callback); |
| + result = CompleteReadIfAsync(result, &callback, mock_source()); |
| + |
| + EXPECT_TRUE(dictionary_id_error_handled()); |
| + EXPECT_EQ("", last_get_dictionary_id()); |
| + EXPECT_EQ(9, result); |
| + EXPECT_EQ(0, memcmp(id, out_data(), result)); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +// When encounter a dictionary error, delegate returns ErrorRecover NONE. |
| +TEST_P(SdchSourceStreamTest, BogusDictionaryIdNoRecover) { |
| + char id[] = {0x1f, '0', '0', '0', '0', '0', '0', '0', 0x0}; |
| + mock_source()->AddReadResult(id, sizeof(id), OK, GetParam()); |
| + SetTestDictionary(id, "..."); |
| + SetErrorRecover(SdchSourceStream::Delegate::NONE, std::string()); |
| + TestCompletionCallback callback; |
| + int result = ReadStream(callback); |
| + result = CompleteReadIfAsync(result, &callback, mock_source()); |
| + |
| + EXPECT_TRUE(dictionary_id_error_handled()); |
| + EXPECT_EQ("", last_get_dictionary_id()); |
| + EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, result); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +// Ensure that the stream's dictionary error handler is called if GetDictionary |
| +// returns no dictionary. |
| +TEST_P(SdchSourceStreamTest, NoDictionaryError) { |
| + char id[] = "00000000"; |
| + SetErrorRecover(SdchSourceStream::Delegate::PASS_THROUGH, std::string()); |
| + mock_source()->AddReadResult(id, sizeof(id), OK, GetParam()); |
| + TestCompletionCallback callback; |
| + int result = ReadStream(callback); |
| + result = CompleteReadIfAsync(result, &callback, mock_source()); |
| + EXPECT_EQ(9, result); |
| + EXPECT_TRUE(get_dictionary_error_handled()); |
| + EXPECT_EQ(id, last_get_dictionary_id()); |
| + EXPECT_EQ(0, memcmp(id, out_data(), result)); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +TEST_P(SdchSourceStreamTest, DictionaryLoaded) { |
| + std::string response; |
| + std::string server_id; |
| + AppendDictionaryIdTo(&response, &server_id); |
| + mock_source()->AddReadResult(response.data(), response.size(), OK, |
| + GetParam()); |
| + mock_source()->AddReadResult(response.data(), 0, OK, MockSourceStream::SYNC); |
| + TestCompletionCallback callback; |
| + int rv = ReadStream(callback); |
| + rv = CompleteReadIfAsync(rv, &callback, mock_source()); |
| + // Decoded response should be empty. |
| + EXPECT_EQ(0, rv); |
| + EXPECT_FALSE(get_dictionary_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +TEST_P(SdchSourceStreamTest, DecompressOneBlock) { |
| + std::string response; |
| + std::string server_id; |
| + AppendDictionaryIdTo(&response, &server_id); |
| + response.append(kSdchCompressedTestData, sizeof(kSdchCompressedTestData) - 1); |
| + mock_source()->AddReadResult(response.data(), response.size(), OK, |
| + GetParam()); |
| + TestCompletionCallback callback; |
| + int rv = ReadStream(callback); |
| + rv = CompleteReadIfAsync(rv, &callback, mock_source()); |
| + |
| + 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()->Description()); |
| +} |
| + |
| +TEST_P(SdchSourceStreamTest, DecompressWithSmallOutputBuffer) { |
| + out_buffer_ = new IOBufferWithSize(kSmallBufferSize); |
| + std::string response; |
| + std::string server_id; |
| + AppendDictionaryIdTo(&response, &server_id); |
| + response.append(kSdchCompressedTestData, sizeof(kSdchCompressedTestData) - 1); |
| + mock_source()->AddReadResult(response.data(), response.size(), OK, |
| + GetParam()); |
| + // Add a 0 byte read to signal EOF. |
| + mock_source()->AddReadResult(kSdchCompressedTestData, 0, OK, |
| + MockSourceStream::SYNC); |
| + |
| + std::string actual_output; |
| + while (true) { |
| + TestCompletionCallback callback; |
| + int rv = ReadStream(callback); |
| + if (rv == ERR_IO_PENDING) |
| + rv = CompleteReadIfAsync(rv, &callback, mock_source()); |
| + if (rv == OK) |
| + break; |
| + ASSERT_GT(rv, OK); |
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(rv)); |
| + actual_output.append(out_data(), rv); |
| + } |
| + EXPECT_FALSE(decoding_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ(sizeof(kTestData) - 1, actual_output.size()); |
| + EXPECT_EQ(kTestData, actual_output); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +TEST_P(SdchSourceStreamTest, DecompressWithSmallInputBuffer) { |
| + std::string response; |
| + std::string server_id; |
| + AppendDictionaryIdTo(&response, &server_id); |
| + response.append(kSdchCompressedTestData, sizeof(kSdchCompressedTestData) - 1); |
| + // Add a sequence of small reads. |
| + for (size_t i = 0; i < response.size(); i++) { |
| + mock_source()->AddReadResult(response.data() + i, 1, OK, |
| + MockSourceStream::SYNC); |
| + } |
| + // Add a 0 byte read to signal EOF. |
| + mock_source()->AddReadResult(kSdchCompressedTestData, 0, OK, |
| + MockSourceStream::SYNC); |
| + std::string actual_output; |
| + while (true) { |
| + TestCompletionCallback callback; |
| + int rv = ReadStream(callback); |
| + if (rv == ERR_IO_PENDING) |
| + rv = CompleteReadIfAsync(rv, &callback, mock_source()); |
| + if (rv == OK) |
| + break; |
| + actual_output.append(out_data(), rv); |
| + } |
| + EXPECT_FALSE(decoding_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ(sizeof(kTestData) - 1, actual_output.size()); |
| + EXPECT_EQ(kTestData, actual_output); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +TEST_P(SdchSourceStreamTest, DecompressTwoBlocks) { |
| + std::string response; |
| + std::string server_id; |
| + AppendDictionaryIdTo(&response, &server_id); |
| + response.append(kSdchCompressedTestData, sizeof(kSdchCompressedTestData) - 1); |
| + mock_source()->AddReadResult(response.data(), 32, OK, GetParam()); |
| + mock_source()->AddReadResult(response.data() + 32, response.size() - 32, OK, |
| + GetParam()); |
| + mock_source()->AddReadResult(kSdchCompressedTestData, 0, OK, |
| + MockSourceStream::SYNC); |
| + std::string actual_output; |
| + while (true) { |
| + TestCompletionCallback callback; |
| + int rv = ReadStream(callback); |
| + if (rv == ERR_IO_PENDING) |
| + rv = CompleteReadIfAsync(rv, &callback, mock_source(), 2); |
| + if (rv == OK) |
| + break; |
| + ASSERT_GT(rv, OK); |
| + actual_output.append(out_data(), rv); |
| + } |
| + EXPECT_FALSE(decoding_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ(sizeof(kTestData) - 1, actual_output.size()); |
| + EXPECT_EQ(kTestData, actual_output); |
| + EXPECT_EQ("SDCH", sdch_source()->Description()); |
| +} |
| + |
| +// 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_P(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); |
| + MockSourceStream* source = new MockSourceStream; |
| + source->AddReadResult(gzip_compressed_sdch.data(), gzip_length, OK, |
| + GetParam()); |
| + |
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create( |
| + base::WrapUnique(source), SourceStream::TYPE_GZIP); |
| + std::unique_ptr<SdchSourceStream> sdch_source( |
| + new SdchSourceStream(std::move(gzip_source), this)); |
| + TestCompletionCallback callback; |
| + int result = |
| + sdch_source->Read(out_buffer(), out_buffer_size(), callback.callback()); |
| + result = CompleteReadIfAsync(result, &callback, source); |
| + EXPECT_FALSE(decoding_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1), result); |
| + EXPECT_EQ(0, memcmp(kTestData, out_data(), result)); |
| + EXPECT_EQ("GZIP,SDCH", sdch_source->Description()); |
| + } |
| + |
| + { |
| + // Try a small output 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, |
| + GetParam()); |
| + // Add a 0 byte read to signal EOF. |
| + source->AddReadResult(gzip_compressed_sdch.data(), 0, OK, |
| + MockSourceStream::SYNC); |
| + |
| + std::unique_ptr<GzipSourceStream> gzip_source = GzipSourceStream::Create( |
| + base::WrapUnique(source), SourceStream::TYPE_GZIP); |
| + std::unique_ptr<SdchSourceStream> sdch_source( |
| + new SdchSourceStream(std::move(gzip_source), this)); |
| + std::string actual_output; |
| + while (true) { |
| + TestCompletionCallback callback; |
| + int rv = sdch_source->Read(out_buffer(), out_buffer_size(), |
| + callback.callback()); |
| + if (rv == ERR_IO_PENDING) |
| + rv = CompleteReadIfAsync(rv, &callback, source); |
| + if (rv == OK) |
| + break; |
| + ASSERT_GT(rv, OK); |
| + EXPECT_GE(kSmallBufferSize, static_cast<size_t>(rv)); |
| + actual_output.append(out_data(), rv); |
| + } |
| + EXPECT_FALSE(decoding_error_handled()); |
| + EXPECT_EQ(server_id, last_get_dictionary_id()); |
| + EXPECT_EQ(sizeof(kTestData) - 1, actual_output.size()); |
| + EXPECT_EQ(kTestData, actual_output); |
| + EXPECT_EQ("GZIP,SDCH", sdch_source->Description()); |
| + } |
| +} |
| + |
|
Randy Smith (Not in Mondays)
2016/10/04 20:06:30
Should we have a DecompressWithSmallOutputBuffer t
xunjieli
2016/10/05 13:44:57
On line 344?
TEST_P(SdchSourceStreamTest, Decompre
|
| +} // namespace net |