Lots of random cleanups, mostly for native_theme_win.cc:
[chromium-blink-merge.git] / net / filter / sdch_filter_unittest.cc
blob28da615c2e90bedd4b5c01dadf6ade807f8576f6
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <limits.h>
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "net/base/io_buffer.h"
14 #include "net/filter/mock_filter_context.h"
15 #include "net/filter/sdch_filter.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_http_job.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/zlib/zlib.h"
21 namespace net {
23 //------------------------------------------------------------------------------
24 // Provide sample data and compression results with a sample VCDIFF dictionary.
25 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
26 static const char kTestVcdiffDictionary[] = "DictionaryFor"
27 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
28 // Pre-compression test data. Note that we pad with a lot of highly gzip
29 // compressible content to help to exercise the chaining pipeline. That is why
30 // there are a PILE of zeros at the start and end.
31 // This will ensure that gzip compressed data can be fed to the chain in one
32 // gulp, but (with careful selection of intermediate buffers) that it takes
33 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
34 // CHECK() calls in FilterChaining test for specifics.
35 static const char kTestData[] = "0000000000000000000000000000000000000000000000"
36 "0000000000000000000000000000TestData "
37 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
38 "00000000000000000000000000000000000000000000000000000000000000000000000000"
39 "000000000000000000000000000000000000000\n";
41 // Note SDCH compressed data will include a reference to the SDCH dictionary.
42 static const char kSdchCompressedTestData[] =
43 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
44 "00000000000000000000000000000000000000000000000000000000000000000000000000"
45 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
46 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
48 //------------------------------------------------------------------------------
50 class SdchFilterTest : public testing::Test {
51 protected:
52 SdchFilterTest()
53 : test_vcdiff_dictionary_(kTestVcdiffDictionary,
54 sizeof(kTestVcdiffDictionary) - 1),
55 vcdiff_compressed_data_(kSdchCompressedTestData,
56 sizeof(kSdchCompressedTestData) - 1),
57 expanded_(kTestData, sizeof(kTestData) - 1),
58 sdch_manager_(new SdchManager),
59 filter_context_(new MockFilterContext) {
60 URLRequestContext* url_request_context =
61 filter_context_->GetModifiableURLRequestContext();
63 url_request_context->set_sdch_manager(sdch_manager_.get());
66 MockFilterContext* filter_context() { return filter_context_.get(); }
68 std::string NewSdchCompressedData(const std::string dictionary);
70 const std::string test_vcdiff_dictionary_;
71 const std::string vcdiff_compressed_data_;
72 const std::string expanded_; // Desired final, decompressed data.
74 scoped_ptr<SdchManager> sdch_manager_;
75 scoped_ptr<MockFilterContext> filter_context_;
78 std::string SdchFilterTest::NewSdchCompressedData(
79 const std::string dictionary) {
80 std::string client_hash;
81 std::string server_hash;
82 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash);
84 // Build compressed data that refers to our dictionary.
85 std::string compressed(server_hash);
86 compressed.append("\0", 1);
87 compressed.append(vcdiff_compressed_data_);
88 return compressed;
91 //------------------------------------------------------------------------------
94 TEST_F(SdchFilterTest, Hashing) {
95 std::string client_hash, server_hash;
96 std::string dictionary("test contents");
97 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash);
99 EXPECT_EQ(client_hash, "lMQBjS3P");
100 EXPECT_EQ(server_hash, "MyciMVll");
104 //------------------------------------------------------------------------------
105 // Provide a generic helper function for trying to filter data.
106 // This function repeatedly calls the filter to process data, until the entire
107 // source is consumed. The return value from the filter is appended to output.
108 // This allows us to vary input and output block sizes in order to test for edge
109 // effects (boundary effects?) during the filtering process.
110 // This function provides data to the filter in blocks of no-more-than the
111 // specified input_block_length. It allows the filter to fill no more than
112 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
113 // concatenates all these little output blocks into the singular output string.
114 static bool FilterTestData(const std::string& source,
115 size_t input_block_length,
116 const size_t output_buffer_length,
117 Filter* filter, std::string* output) {
118 CHECK_GT(input_block_length, 0u);
119 Filter::FilterStatus status(Filter::FILTER_NEED_MORE_DATA);
120 size_t source_index = 0;
121 scoped_ptr<char[]> output_buffer(new char[output_buffer_length]);
122 size_t input_amount = std::min(input_block_length,
123 static_cast<size_t>(filter->stream_buffer_size()));
125 do {
126 int copy_amount = std::min(input_amount, source.size() - source_index);
127 if (copy_amount > 0 && status == Filter::FILTER_NEED_MORE_DATA) {
128 memcpy(filter->stream_buffer()->data(), source.data() + source_index,
129 copy_amount);
130 filter->FlushStreamBuffer(copy_amount);
131 source_index += copy_amount;
133 int buffer_length = output_buffer_length;
134 status = filter->ReadData(output_buffer.get(), &buffer_length);
135 output->append(output_buffer.get(), buffer_length);
136 if (status == Filter::FILTER_ERROR)
137 return false;
138 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
139 if (Filter::FILTER_OK == status && 0 == buffer_length)
140 return true;
141 if (copy_amount == 0 && buffer_length == 0)
142 return true;
143 } while (1);
145 //------------------------------------------------------------------------------
146 static std::string NewSdchDictionary(const std::string& domain) {
147 std::string dictionary;
148 if (!domain.empty()) {
149 dictionary.append("Domain: ");
150 dictionary.append(domain);
151 dictionary.append("\n");
153 dictionary.append("\n");
154 dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1);
155 return dictionary;
158 //------------------------------------------------------------------------------
160 TEST_F(SdchFilterTest, EmptyInputOk) {
161 std::vector<Filter::FilterType> filter_types;
162 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
163 char output_buffer[20];
164 std::string url_string("http://ignore.com");
165 filter_context()->SetURL(GURL(url_string));
166 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
169 // With no input data, try to read output.
170 int output_bytes_or_buffer_size = sizeof(output_buffer);
171 Filter::FilterStatus status = filter->ReadData(output_buffer,
172 &output_bytes_or_buffer_size);
174 EXPECT_EQ(0, output_bytes_or_buffer_size);
175 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
178 TEST_F(SdchFilterTest, PassThroughWhenTentative) {
179 std::vector<Filter::FilterType> filter_types;
180 // Selective a tentative filter (which can fall back to pass through).
181 filter_types.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
182 char output_buffer[20];
183 // Response code needs to be 200 to allow a pass through.
184 filter_context()->SetResponseCode(200);
185 std::string url_string("http://ignore.com");
186 filter_context()->SetURL(GURL(url_string));
187 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
189 // Supply enough data to force a pass-through mode..
190 std::string non_gzip_content("not GZIPed data");
192 char* input_buffer = filter->stream_buffer()->data();
193 int input_buffer_size = filter->stream_buffer_size();
195 EXPECT_LT(static_cast<int>(non_gzip_content.size()),
196 input_buffer_size);
197 memcpy(input_buffer, non_gzip_content.data(),
198 non_gzip_content.size());
199 filter->FlushStreamBuffer(non_gzip_content.size());
201 // Try to read output.
202 int output_bytes_or_buffer_size = sizeof(output_buffer);
203 Filter::FilterStatus status = filter->ReadData(output_buffer,
204 &output_bytes_or_buffer_size);
206 EXPECT_EQ(non_gzip_content.size(),
207 static_cast<size_t>(output_bytes_or_buffer_size));
208 ASSERT_GT(sizeof(output_buffer),
209 static_cast<size_t>(output_bytes_or_buffer_size));
210 output_buffer[output_bytes_or_buffer_size] = '\0';
211 EXPECT_TRUE(non_gzip_content == output_buffer);
212 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
215 TEST_F(SdchFilterTest, RefreshBadReturnCode) {
216 std::vector<Filter::FilterType> filter_types;
217 // Selective a tentative filter (which can fall back to pass through).
218 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
219 char output_buffer[20];
220 // Response code needs to be 200 to allow a pass through.
221 filter_context()->SetResponseCode(403);
222 // Meta refresh will only appear for html content
223 filter_context()->SetMimeType("text/html");
224 std::string url_string("http://ignore.com");
225 filter_context()->SetURL(GURL(url_string));
226 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
228 // Supply enough data to force a pass-through mode, which means we have
229 // provided more than 9 characters that can't be a dictionary hash.
230 std::string non_sdch_content("This is not SDCH");
232 char* input_buffer = filter->stream_buffer()->data();
233 int input_buffer_size = filter->stream_buffer_size();
235 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
236 input_buffer_size);
237 memcpy(input_buffer, non_sdch_content.data(),
238 non_sdch_content.size());
239 filter->FlushStreamBuffer(non_sdch_content.size());
241 // Try to read output.
242 int output_bytes_or_buffer_size = sizeof(output_buffer);
243 Filter::FilterStatus status = filter->ReadData(output_buffer,
244 &output_bytes_or_buffer_size);
246 // We should have read a long and complicated meta-refresh request.
247 EXPECT_TRUE(sizeof(output_buffer) == output_bytes_or_buffer_size);
248 // Check at least the prefix of the return.
249 EXPECT_EQ(0, strncmp(output_buffer,
250 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
251 sizeof(output_buffer)));
252 EXPECT_EQ(Filter::FILTER_OK, status);
255 TEST_F(SdchFilterTest, ErrorOnBadReturnCode) {
256 std::vector<Filter::FilterType> filter_types;
257 // Selective a tentative filter (which can fall back to pass through).
258 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
259 char output_buffer[20];
260 // Response code needs to be 200 to allow a pass through.
261 filter_context()->SetResponseCode(403);
262 // Meta refresh will only appear for html content, so set to something else
263 // to induce an error (we can't meta refresh).
264 filter_context()->SetMimeType("anything");
265 std::string url_string("http://ignore.com");
266 filter_context()->SetURL(GURL(url_string));
267 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
269 // Supply enough data to force a pass-through mode, which means we have
270 // provided more than 9 characters that can't be a dictionary hash.
271 std::string non_sdch_content("This is not SDCH");
273 char* input_buffer = filter->stream_buffer()->data();
274 int input_buffer_size = filter->stream_buffer_size();
276 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
277 input_buffer_size);
278 memcpy(input_buffer, non_sdch_content.data(),
279 non_sdch_content.size());
280 filter->FlushStreamBuffer(non_sdch_content.size());
282 // Try to read output.
283 int output_bytes_or_buffer_size = sizeof(output_buffer);
284 Filter::FilterStatus status = filter->ReadData(output_buffer,
285 &output_bytes_or_buffer_size);
287 EXPECT_EQ(0, output_bytes_or_buffer_size);
288 EXPECT_EQ(Filter::FILTER_ERROR, status);
291 TEST_F(SdchFilterTest, ErrorOnBadReturnCodeWithHtml) {
292 std::vector<Filter::FilterType> filter_types;
293 // Selective a tentative filter (which can fall back to pass through).
294 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
295 char output_buffer[20];
296 // Response code needs to be 200 to allow a pass through.
297 filter_context()->SetResponseCode(403);
298 // Meta refresh will only appear for html content
299 filter_context()->SetMimeType("text/html");
300 std::string url_string("http://ignore.com");
301 filter_context()->SetURL(GURL(url_string));
302 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
304 // Supply enough data to force a pass-through mode, which means we have
305 // provided more than 9 characters that can't be a dictionary hash.
306 std::string non_sdch_content("This is not SDCH");
308 char* input_buffer = filter->stream_buffer()->data();
309 int input_buffer_size = filter->stream_buffer_size();
311 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
312 input_buffer_size);
313 memcpy(input_buffer, non_sdch_content.data(),
314 non_sdch_content.size());
315 filter->FlushStreamBuffer(non_sdch_content.size());
317 // Try to read output.
318 int output_bytes_or_buffer_size = sizeof(output_buffer);
319 Filter::FilterStatus status = filter->ReadData(output_buffer,
320 &output_bytes_or_buffer_size);
322 // We should have read a long and complicated meta-refresh request.
323 EXPECT_EQ(sizeof(output_buffer),
324 static_cast<size_t>(output_bytes_or_buffer_size));
325 // Check at least the prefix of the return.
326 EXPECT_EQ(0, strncmp(output_buffer,
327 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
328 sizeof(output_buffer)));
329 EXPECT_EQ(Filter::FILTER_OK, status);
332 TEST_F(SdchFilterTest, BasicBadDictionary) {
333 std::vector<Filter::FilterType> filter_types;
334 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
335 char output_buffer[20];
336 std::string url_string("http://ignore.com");
337 filter_context()->SetURL(GURL(url_string));
338 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
340 // Supply bogus data (which doesn't yet specify a full dictionary hash).
341 // Dictionary hash is 8 characters followed by a null.
342 std::string dictionary_hash_prefix("123");
344 char* input_buffer = filter->stream_buffer()->data();
345 int input_buffer_size = filter->stream_buffer_size();
347 EXPECT_LT(static_cast<int>(dictionary_hash_prefix.size()),
348 input_buffer_size);
349 memcpy(input_buffer, dictionary_hash_prefix.data(),
350 dictionary_hash_prefix.size());
351 filter->FlushStreamBuffer(dictionary_hash_prefix.size());
353 // With less than a dictionary specifier, try to read output.
354 int output_bytes_or_buffer_size = sizeof(output_buffer);
355 Filter::FilterStatus status = filter->ReadData(output_buffer,
356 &output_bytes_or_buffer_size);
358 EXPECT_EQ(0, output_bytes_or_buffer_size);
359 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
361 // Provide enough data to complete *a* hash, but it is bogus, and not in our
362 // list of dictionaries, so the filter should error out immediately.
363 std::string dictionary_hash_postfix("4abcd\0", 6);
365 CHECK_LT(dictionary_hash_postfix.size(),
366 static_cast<size_t>(input_buffer_size));
367 memcpy(input_buffer, dictionary_hash_postfix.data(),
368 dictionary_hash_postfix.size());
369 filter->FlushStreamBuffer(dictionary_hash_postfix.size());
371 // With a non-existant dictionary specifier, try to read output.
372 output_bytes_or_buffer_size = sizeof(output_buffer);
373 status = filter->ReadData(output_buffer, &output_bytes_or_buffer_size);
375 EXPECT_EQ(0, output_bytes_or_buffer_size);
376 EXPECT_EQ(Filter::FILTER_ERROR, status);
378 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
379 sdch_manager_->ClearBlacklistings();
380 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
383 TEST_F(SdchFilterTest, DictionaryAddOnce) {
384 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
385 const std::string kSampleDomain = "sdchtest.com";
386 std::string dictionary(NewSdchDictionary(kSampleDomain));
388 std::string url_string = "http://" + kSampleDomain;
389 GURL url(url_string);
390 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
392 // Check we can't add it twice.
393 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary, url));
395 const std::string kSampleDomain2 = "sdchtest2.com";
397 // Don't test adding a second dictionary if our limits are tight.
398 if (SdchManager::kMaxDictionaryCount > 1) {
399 // Construct a second SDCH dictionary from a VCDIFF dictionary.
400 std::string dictionary2(NewSdchDictionary(kSampleDomain2));
402 std::string url_string2 = "http://" + kSampleDomain2;
403 GURL url2(url_string2);
404 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary2, url2));
408 TEST_F(SdchFilterTest, BasicDictionary) {
409 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
410 const std::string kSampleDomain = "sdchtest.com";
411 std::string dictionary(NewSdchDictionary(kSampleDomain));
413 std::string url_string = "http://" + kSampleDomain;
415 GURL url(url_string);
416 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
418 std::string compressed(NewSdchCompressedData(dictionary));
420 std::vector<Filter::FilterType> filter_types;
421 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
423 filter_context()->SetURL(url);
425 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
427 size_t feed_block_size = 100;
428 size_t output_block_size = 100;
429 std::string output;
430 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
431 filter.get(), &output));
432 EXPECT_EQ(output, expanded_);
434 // Decode with really small buffers (size 1) to check for edge effects.
435 filter.reset(Filter::Factory(filter_types, *filter_context()));
437 feed_block_size = 1;
438 output_block_size = 1;
439 output.clear();
440 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
441 filter.get(), &output));
442 EXPECT_EQ(output, expanded_);
445 TEST_F(SdchFilterTest, NoDecodeHttps) {
446 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
447 const std::string kSampleDomain = "sdchtest.com";
448 std::string dictionary(NewSdchDictionary(kSampleDomain));
450 std::string url_string = "http://" + kSampleDomain;
452 GURL url(url_string);
453 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
455 std::string compressed(NewSdchCompressedData(dictionary));
457 std::vector<Filter::FilterType> filter_types;
458 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
460 filter_context()->SetURL(GURL("https://" + kSampleDomain));
461 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
463 const size_t feed_block_size(100);
464 const size_t output_block_size(100);
465 std::string output;
467 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
468 filter.get(), &output));
471 // Current failsafe TODO/hack refuses to decode any content that doesn't use
472 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
473 // The following tests this blockage. Note that blacklisting results, so we
474 // we need separate tests for each of these.
475 TEST_F(SdchFilterTest, NoDecodeFtp) {
476 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
477 const std::string kSampleDomain = "sdchtest.com";
478 std::string dictionary(NewSdchDictionary(kSampleDomain));
480 std::string url_string = "http://" + kSampleDomain;
482 GURL url(url_string);
483 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
485 std::string compressed(NewSdchCompressedData(dictionary));
487 std::vector<Filter::FilterType> filter_types;
488 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
490 filter_context()->SetURL(GURL("ftp://" + kSampleDomain));
491 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
493 const size_t feed_block_size(100);
494 const size_t output_block_size(100);
495 std::string output;
497 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
498 filter.get(), &output));
501 TEST_F(SdchFilterTest, NoDecodeFileColon) {
502 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
503 const std::string kSampleDomain = "sdchtest.com";
504 std::string dictionary(NewSdchDictionary(kSampleDomain));
506 std::string url_string = "http://" + kSampleDomain;
508 GURL url(url_string);
509 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
511 std::string compressed(NewSdchCompressedData(dictionary));
513 std::vector<Filter::FilterType> filter_types;
514 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
516 filter_context()->SetURL(GURL("file://" + kSampleDomain));
517 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
519 const size_t feed_block_size(100);
520 const size_t output_block_size(100);
521 std::string output;
523 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
524 filter.get(), &output));
527 TEST_F(SdchFilterTest, NoDecodeAboutColon) {
528 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
529 const std::string kSampleDomain = "sdchtest.com";
530 std::string dictionary(NewSdchDictionary(kSampleDomain));
532 std::string url_string = "http://" + kSampleDomain;
534 GURL url(url_string);
535 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
537 std::string compressed(NewSdchCompressedData(dictionary));
539 std::vector<Filter::FilterType> filter_types;
540 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
542 filter_context()->SetURL(GURL("about://" + kSampleDomain));
543 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
545 const size_t feed_block_size(100);
546 const size_t output_block_size(100);
547 std::string output;
549 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
550 filter.get(), &output));
553 TEST_F(SdchFilterTest, NoDecodeJavaScript) {
554 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
555 const std::string kSampleDomain = "sdchtest.com";
556 std::string dictionary(NewSdchDictionary(kSampleDomain));
558 std::string url_string = "http://" + kSampleDomain;
560 GURL url(url_string);
561 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
563 std::string compressed(NewSdchCompressedData(dictionary));
565 std::vector<Filter::FilterType> filter_types;
566 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
568 filter_context()->SetURL(GURL("javascript://" + kSampleDomain));
569 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
571 const size_t feed_block_size(100);
572 const size_t output_block_size(100);
573 std::string output;
575 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
576 filter.get(), &output));
579 TEST_F(SdchFilterTest, CanStillDecodeHttp) {
580 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
581 const std::string kSampleDomain = "sdchtest.com";
582 std::string dictionary(NewSdchDictionary(kSampleDomain));
584 std::string url_string = "http://" + kSampleDomain;
586 GURL url(url_string);
587 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
589 std::string compressed(NewSdchCompressedData(dictionary));
591 std::vector<Filter::FilterType> filter_types;
592 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
594 filter_context()->SetURL(GURL("http://" + kSampleDomain));
595 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
597 const size_t feed_block_size(100);
598 const size_t output_block_size(100);
599 std::string output;
601 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
602 filter.get(), &output));
605 TEST_F(SdchFilterTest, CrossDomainDictionaryUse) {
606 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
607 const std::string kSampleDomain = "sdchtest.com";
608 std::string dictionary(NewSdchDictionary(kSampleDomain));
610 std::string url_string = "http://" + kSampleDomain;
612 GURL url(url_string);
613 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
615 std::string compressed(NewSdchCompressedData(dictionary));
617 std::vector<Filter::FilterType> filter_types;
618 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
620 // Decode with content arriving from the "wrong" domain.
621 // This tests SdchManager::CanSet().
622 GURL wrong_domain_url("http://www.wrongdomain.com");
623 filter_context()->SetURL(wrong_domain_url);
624 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
626 size_t feed_block_size = 100;
627 size_t output_block_size = 100;
628 std::string output;
629 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
630 filter.get(), &output));
631 EXPECT_EQ(output.size(), 0u); // No output written.
633 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
634 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(wrong_domain_url));
635 sdch_manager_->ClearBlacklistings();
636 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(wrong_domain_url));
639 TEST_F(SdchFilterTest, DictionaryPathValidation) {
640 // Can't test path distinction between dictionaries if we aren't allowed
641 // more than one dictionary.
642 if (SdchManager::kMaxDictionaryCount <= 1)
643 return;
645 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
646 const std::string kSampleDomain = "sdchtest.com";
647 std::string dictionary(NewSdchDictionary(kSampleDomain));
649 std::string url_string = "http://" + kSampleDomain;
651 GURL url(url_string);
652 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
654 // Create a dictionary with a path restriction, by prefixing dictionary.
655 const std::string path("/special_path/bin");
656 std::string dictionary_with_path("Path: " + path + "\n");
657 dictionary_with_path.append(dictionary);
658 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_with_path, url));
660 std::string compressed_for_path(NewSdchCompressedData(dictionary_with_path));
662 std::vector<Filter::FilterType> filter_types;
663 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
665 // Test decode the path data, arriving from a valid path.
666 filter_context()->SetURL(GURL(url_string + path));
667 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
669 size_t feed_block_size = 100;
670 size_t output_block_size = 100;
671 std::string output;
673 EXPECT_TRUE(FilterTestData(compressed_for_path, feed_block_size,
674 output_block_size, filter.get(), &output));
675 EXPECT_EQ(output, expanded_);
677 // Test decode the path data, arriving from a invalid path.
678 filter_context()->SetURL(GURL(url_string));
679 filter.reset(Filter::Factory(filter_types, *filter_context()));
681 feed_block_size = 100;
682 output_block_size = 100;
683 output.clear();
684 EXPECT_FALSE(FilterTestData(compressed_for_path, feed_block_size,
685 output_block_size, filter.get(), &output));
686 EXPECT_EQ(output.size(), 0u); // No output written.
688 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
689 sdch_manager_->ClearBlacklistings();
690 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
693 TEST_F(SdchFilterTest, DictionaryPortValidation) {
694 // Can't test port distinction between dictionaries if we aren't allowed
695 // more than one dictionary.
696 if (SdchManager::kMaxDictionaryCount <= 1)
697 return;
699 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
700 const std::string kSampleDomain = "sdchtest.com";
701 std::string dictionary(NewSdchDictionary(kSampleDomain));
703 std::string url_string = "http://" + kSampleDomain;
705 GURL url(url_string);
706 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
709 // Create a dictionary with a port restriction, by prefixing old dictionary.
710 const std::string port("502");
711 std::string dictionary_with_port("Port: " + port + "\n");
712 dictionary_with_port.append("Port: 80\n"); // Add default port.
713 dictionary_with_port.append(dictionary);
714 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_with_port,
715 GURL(url_string + ":" + port)));
717 std::string compressed_for_port(NewSdchCompressedData(dictionary_with_port));
719 std::vector<Filter::FilterType> filter_types;
720 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
722 // Test decode the port data, arriving from a valid port.
723 filter_context()->SetURL(GURL(url_string + ":" + port));
724 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
726 size_t feed_block_size = 100;
727 size_t output_block_size = 100;
728 std::string output;
729 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size,
730 output_block_size, filter.get(), &output));
731 EXPECT_EQ(output, expanded_);
733 // Test decode the port data, arriving from a valid (default) port.
734 filter_context()->SetURL(GURL(url_string)); // Default port.
735 filter.reset(Filter::Factory(filter_types, *filter_context()));
737 feed_block_size = 100;
738 output_block_size = 100;
739 output.clear();
740 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size,
741 output_block_size, filter.get(), &output));
742 EXPECT_EQ(output, expanded_);
744 // Test decode the port data, arriving from a invalid port.
745 filter_context()->SetURL(GURL(url_string + ":" + port + "1"));
746 filter.reset(Filter::Factory(filter_types, *filter_context()));
748 feed_block_size = 100;
749 output_block_size = 100;
750 output.clear();
751 EXPECT_FALSE(FilterTestData(compressed_for_port, feed_block_size,
752 output_block_size, filter.get(), &output));
753 EXPECT_EQ(output.size(), 0u); // No output written.
755 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
756 sdch_manager_->ClearBlacklistings();
757 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string)));
760 //------------------------------------------------------------------------------
761 // Helper function to perform gzip compression of data.
763 static std::string gzip_compress(const std::string &input) {
764 z_stream zlib_stream;
765 memset(&zlib_stream, 0, sizeof(zlib_stream));
766 int code;
768 // Initialize zlib
769 code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
770 -MAX_WBITS,
771 8, // DEF_MEM_LEVEL
772 Z_DEFAULT_STRATEGY);
774 CHECK_EQ(Z_OK, code);
776 // Fill in zlib control block
777 zlib_stream.next_in = bit_cast<Bytef*>(input.data());
778 zlib_stream.avail_in = input.size();
780 // Assume we can compress into similar buffer (add 100 bytes to be sure).
781 size_t gzip_compressed_length = zlib_stream.avail_in + 100;
782 scoped_ptr<char[]> gzip_compressed(new char[gzip_compressed_length]);
783 zlib_stream.next_out = bit_cast<Bytef*>(gzip_compressed.get());
784 zlib_stream.avail_out = gzip_compressed_length;
786 // The GZIP header (see RFC 1952):
787 // +---+---+---+---+---+---+---+---+---+---+
788 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
789 // +---+---+---+---+---+---+---+---+---+---+
790 // ID1 \037
791 // ID2 \213
792 // CM \010 (compression method == DEFLATE)
793 // FLG \000 (special flags that we do not support)
794 // MTIME Unix format modification time (0 means not available)
795 // XFL 2-4? DEFLATE flags
796 // OS ???? Operating system indicator (255 means unknown)
798 // Header value we generate:
799 const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000',
800 '\000', '\000', '\000', '\002', '\377' };
801 CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader));
802 memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader));
803 zlib_stream.next_out += sizeof(kGZipHeader);
804 zlib_stream.avail_out -= sizeof(kGZipHeader);
806 // Do deflate
807 code = deflate(&zlib_stream, Z_FINISH);
808 gzip_compressed_length -= zlib_stream.avail_out;
809 std::string compressed(gzip_compressed.get(), gzip_compressed_length);
810 deflateEnd(&zlib_stream);
811 return compressed;
814 //------------------------------------------------------------------------------
816 class SdchFilterChainingTest {
817 public:
818 static Filter* Factory(const std::vector<Filter::FilterType>& types,
819 const FilterContext& context, int size) {
820 return Filter::FactoryForTests(types, context, size);
824 // Test that filters can be cascaded (chained) so that the output of one filter
825 // is processed by the next one. This is most critical for SDCH, which is
826 // routinely followed by gzip (during encoding). The filter we'll test for will
827 // do the gzip decoding first, and then decode the SDCH content.
828 TEST_F(SdchFilterTest, FilterChaining) {
829 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
830 const std::string kSampleDomain = "sdchtest.com";
831 std::string dictionary(NewSdchDictionary(kSampleDomain));
833 std::string url_string = "http://" + kSampleDomain;
835 GURL url(url_string);
836 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
838 std::string sdch_compressed(NewSdchCompressedData(dictionary));
840 // Use Gzip to compress the sdch sdch_compressed data.
841 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
843 // Construct a chained filter.
844 std::vector<Filter::FilterType> filter_types;
845 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
846 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
848 // First try with a large buffer (larger than test input, or compressed data).
849 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
850 CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size());
851 CHECK_GT(kLargeInputBufferSize, sdch_compressed.size());
852 CHECK_GT(kLargeInputBufferSize, expanded_.size());
853 filter_context()->SetURL(url);
854 scoped_ptr<Filter> filter(
855 SdchFilterChainingTest::Factory(filter_types, *filter_context(),
856 kLargeInputBufferSize));
857 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
858 filter->stream_buffer_size());
860 // Verify that chained filter is waiting for data.
861 char tiny_output_buffer[10];
862 int tiny_output_size = sizeof(tiny_output_buffer);
863 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
864 filter->ReadData(tiny_output_buffer, &tiny_output_size));
866 // Make chain process all data.
867 size_t feed_block_size = kLargeInputBufferSize;
868 size_t output_block_size = kLargeInputBufferSize;
869 std::string output;
870 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
871 output_block_size, filter.get(), &output));
872 EXPECT_EQ(output, expanded_);
874 // Next try with a mid-sized internal buffer size.
875 const size_t kMidSizedInputBufferSize(100);
876 // Buffer should be big enough to swallow whole gzip content.
877 CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size());
878 // Buffer should be small enough that entire SDCH content can't fit.
879 // We'll go even further, and force the chain to flush the buffer between the
880 // two filters more than once (that is why we multiply by 2).
881 CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size());
882 filter_context()->SetURL(url);
883 filter.reset(
884 SdchFilterChainingTest::Factory(filter_types, *filter_context(),
885 kMidSizedInputBufferSize));
886 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize),
887 filter->stream_buffer_size());
889 feed_block_size = kMidSizedInputBufferSize;
890 output_block_size = kMidSizedInputBufferSize;
891 output.clear();
892 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
893 output_block_size, filter.get(), &output));
894 EXPECT_EQ(output, expanded_);
896 // Next try with a tiny input and output buffer to cover edge effects.
897 filter.reset(SdchFilterChainingTest::Factory(filter_types, *filter_context(),
898 kLargeInputBufferSize));
899 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
900 filter->stream_buffer_size());
902 feed_block_size = 1;
903 output_block_size = 1;
904 output.clear();
905 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
906 output_block_size, filter.get(), &output));
907 EXPECT_EQ(output, expanded_);
910 TEST_F(SdchFilterTest, DefaultGzipIfSdch) {
911 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
912 const std::string kSampleDomain = "sdchtest.com";
913 std::string dictionary(NewSdchDictionary(kSampleDomain));
915 std::string url_string = "http://" + kSampleDomain;
917 GURL url(url_string);
918 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
920 std::string sdch_compressed(NewSdchCompressedData(dictionary));
922 // Use Gzip to compress the sdch sdch_compressed data.
923 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
925 // Only claim to have sdch content, but really use the gzipped sdch content.
926 // System should automatically add the missing (optional) gzip.
927 std::vector<Filter::FilterType> filter_types;
928 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
930 filter_context()->SetMimeType("anything/mime");
931 filter_context()->SetSdchResponse(true);
932 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
933 ASSERT_EQ(filter_types.size(), 2u);
934 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH);
935 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
937 // First try with a large buffer (larger than test input, or compressed data).
938 filter_context()->SetURL(url);
939 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
942 // Verify that chained filter is waiting for data.
943 char tiny_output_buffer[10];
944 int tiny_output_size = sizeof(tiny_output_buffer);
945 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
946 filter->ReadData(tiny_output_buffer, &tiny_output_size));
948 size_t feed_block_size = 100;
949 size_t output_block_size = 100;
950 std::string output;
951 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
952 output_block_size, filter.get(), &output));
953 EXPECT_EQ(output, expanded_);
955 // Next try with a tiny buffer to cover edge effects.
956 filter.reset(Filter::Factory(filter_types, *filter_context()));
958 feed_block_size = 1;
959 output_block_size = 1;
960 output.clear();
961 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
962 output_block_size, filter.get(), &output));
963 EXPECT_EQ(output, expanded_);
966 TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) {
967 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
968 const std::string kSampleDomain = "sdchtest.com";
969 std::string dictionary(NewSdchDictionary(kSampleDomain));
971 std::string url_string = "http://" + kSampleDomain;
973 GURL url(url_string);
974 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
976 std::string sdch_compressed(NewSdchCompressedData(dictionary));
978 // Use Gzip to compress the sdch sdch_compressed data.
979 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
981 // Some proxies strip the content encoding statement down to a mere gzip, but
982 // pass through the original content (with full sdch,gzip encoding).
983 // Only claim to have gzip content, but really use the gzipped sdch content.
984 // System should automatically add the missing (optional) sdch.
985 std::vector<Filter::FilterType> filter_types;
986 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
988 filter_context()->SetMimeType("anything/mime");
989 filter_context()->SetSdchResponse(true);
990 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
991 ASSERT_EQ(filter_types.size(), 3u);
992 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
993 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
994 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
996 // First try with a large buffer (larger than test input, or compressed data).
997 filter_context()->SetURL(url);
998 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
1001 // Verify that chained filter is waiting for data.
1002 char tiny_output_buffer[10];
1003 int tiny_output_size = sizeof(tiny_output_buffer);
1004 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1005 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1007 size_t feed_block_size = 100;
1008 size_t output_block_size = 100;
1009 std::string output;
1010 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1011 output_block_size, filter.get(), &output));
1012 EXPECT_EQ(output, expanded_);
1014 // Next try with a tiny buffer to cover edge effects.
1015 filter.reset(Filter::Factory(filter_types, *filter_context()));
1017 feed_block_size = 1;
1018 output_block_size = 1;
1019 output.clear();
1020 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1021 output_block_size, filter.get(), &output));
1022 EXPECT_EQ(output, expanded_);
1025 TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) {
1026 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1027 const std::string kSampleDomain = "sdchtest.com";
1028 std::string dictionary(NewSdchDictionary(kSampleDomain));
1030 std::string url_string = "http://" + kSampleDomain;
1032 GURL url(url_string);
1033 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
1035 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1037 // Use Gzip to compress the sdch sdch_compressed data.
1038 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
1040 // Only claim to have non-encoded content, but really use the gzipped sdch
1041 // content.
1042 // System should automatically add the missing (optional) sdch,gzip.
1043 std::vector<Filter::FilterType> filter_types;
1045 filter_context()->SetMimeType("anything/mime");
1046 filter_context()->SetSdchResponse(true);
1047 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1048 ASSERT_EQ(filter_types.size(), 2u);
1049 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
1050 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1052 // First try with a large buffer (larger than test input, or compressed data).
1053 filter_context()->SetURL(url);
1054 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
1057 // Verify that chained filter is waiting for data.
1058 char tiny_output_buffer[10];
1059 int tiny_output_size = sizeof(tiny_output_buffer);
1060 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1061 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1063 size_t feed_block_size = 100;
1064 size_t output_block_size = 100;
1065 std::string output;
1066 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1067 output_block_size, filter.get(), &output));
1068 EXPECT_EQ(output, expanded_);
1070 // Next try with a tiny buffer to cover edge effects.
1071 filter.reset(Filter::Factory(filter_types, *filter_context()));
1073 feed_block_size = 1;
1074 output_block_size = 1;
1075 output.clear();
1076 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1077 output_block_size, filter.get(), &output));
1078 EXPECT_EQ(output, expanded_);
1081 TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) {
1082 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1083 const std::string kSampleDomain = "sdchtest.com";
1084 std::string dictionary(NewSdchDictionary(kSampleDomain));
1086 std::string url_string = "http://" + kSampleDomain;
1088 GURL url(url_string);
1089 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url));
1091 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1093 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1094 // encoding of merely gzip (apparently, only listing the extra level of
1095 // wrapper compression they added, but discarding the actual content encoding.
1096 // Use Gzip to double compress the sdch sdch_compressed data.
1097 std::string double_gzip_compressed_sdch = gzip_compress(gzip_compress(
1098 sdch_compressed));
1100 // Only claim to have gzip content, but really use the double gzipped sdch
1101 // content.
1102 // System should automatically add the missing (optional) sdch, gzip decoders.
1103 std::vector<Filter::FilterType> filter_types;
1104 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
1106 filter_context()->SetMimeType("anything/mime");
1107 filter_context()->SetSdchResponse(true);
1108 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1109 ASSERT_EQ(filter_types.size(), 3u);
1110 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
1111 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1112 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
1114 // First try with a large buffer (larger than test input, or compressed data).
1115 filter_context()->SetURL(url);
1116 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context()));
1118 // Verify that chained filter is waiting for data.
1119 char tiny_output_buffer[10];
1120 int tiny_output_size = sizeof(tiny_output_buffer);
1121 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1122 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1124 size_t feed_block_size = 100;
1125 size_t output_block_size = 100;
1126 std::string output;
1127 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size,
1128 output_block_size, filter.get(), &output));
1129 EXPECT_EQ(output, expanded_);
1131 // Next try with a tiny buffer to cover edge effects.
1132 filter.reset(Filter::Factory(filter_types, *filter_context()));
1134 feed_block_size = 1;
1135 output_block_size = 1;
1136 output.clear();
1137 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size,
1138 output_block_size, filter.get(), &output));
1139 EXPECT_EQ(output, expanded_);
1142 } // namespace net