#include "net/base/io_buffer.h"
#include "net/filter/filter.h"
#include "net/filter/mock_filter_context.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

class PassThroughFilter : public Filter {
 public:
  PassThroughFilter() : Filter(FILTER_TYPE_UNSUPPORTED) {}

  FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override {
    return CopyOut(dest_buffer, dest_len);
  }

  DISALLOW_COPY_AND_ASSIGN(PassThroughFilter);
};

}  

TEST(FilterTest, ContentTypeId) {

  EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE,
            Filter::ConvertEncodingToType("deflate"));
  EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE,
            Filter::ConvertEncodingToType("deflAte"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("gzip"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("GzIp"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("x-gzip"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("X-GzIp"));
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH,
            Filter::ConvertEncodingToType("sdch"));
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH,
            Filter::ConvertEncodingToType("sDcH"));
  EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED,
            Filter::ConvertEncodingToType("weird"));
  EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED,
            Filter::ConvertEncodingToType("strange"));
}

TEST(FilterTest, ApacheGzip) {
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(NULL);

  const std::string kGzipMime1("application/x-gzip");
  const std::string kGzipMime2("application/gzip");
  const std::string kGzipMime3("application/x-gunzip");
  std::vector<Filter::FilterType> encoding_types;

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime1);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime2);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime3);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  filter_context.SetMimeType(kGzipMime1);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType("other/mime");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, GzipContentDispositionFilename) {
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(NULL);

  const std::string kGzipMime("application/x-tar");
  const std::string kContentDisposition("attachment; filename=\"foo.tgz\"");
  const std::string kURL("http:
  std::vector<Filter::FilterType> encoding_types;

  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime);
  filter_context.SetURL(GURL(kURL));
  filter_context.SetContentDisposition(kContentDisposition);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(0U, encoding_types.size());
}

TEST(FilterTest, SdchEncoding) {

  const std::string kTextHtmlMime("text/html");
  MockFilterContext filter_context;

  filter_context.SetSdchResponse(
      SdchManager::CreateEmptyDictionarySetForTesting().Pass());

  std::vector<Filter::FilterType> encoding_types;

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kTextHtmlMime);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]);

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType("other/type");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]);

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);
}

TEST(FilterTest, MissingSdchEncoding) {

  const std::string kTextHtmlMime("text/html");
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(
      SdchManager::CreateEmptyDictionarySetForTesting().Pass());

  std::vector<Filter::FilterType> encoding_types;

  encoding_types.clear();
  filter_context.SetMimeType(kTextHtmlMime);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);

  encoding_types.clear();
  filter_context.SetMimeType("text/html; charset=UTF-8");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);

  encoding_types.clear();
  filter_context.SetMimeType("other/mime");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);
}

TEST(FilterTest, Svgz) {
  MockFilterContext filter_context;

  const std::string kSvgzMime("image/svg+xml");
  const std::string kSvgzUrl("http:
  const std::string kSvgUrl("http:
  std::vector<Filter::FilterType> encoding_types;

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, UnsupportedMimeGzip) {

  MockFilterContext filter_context;
  std::vector<Filter::FilterType> encoding_types;
  const std::string kTarMime("application/x-tar");
  const std::string kCpioMime("application/x-cpio");
  const std::string kTarUrl("http:
  const std::string kTargzUrl("http:
  const std::string kTgzUrl("http:
  const std::string kBadTgzUrl("http:
  const std::string kUrl("http:

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTargzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kCpioMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kCpioMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTarUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kBadTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kBadTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, SupportedMimeGzip) {

  MockFilterContext filter_context;
  std::vector<Filter::FilterType> encoding_types;
  const std::string kGzUrl("http:
  const std::string kUrl("http:
  const std::string kHtmlMime("text/html");
  const std::string kJavascriptMime("text/javascript");

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kJavascriptMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());
}

TEST(FilterTest, ThreeFilterChain) {
  scoped_ptr<PassThroughFilter> filter1(new PassThroughFilter);
  scoped_ptr<PassThroughFilter> filter2(new PassThroughFilter);
  scoped_ptr<PassThroughFilter> filter3(new PassThroughFilter);

  filter1->InitBuffer(32 * 1024);
  filter2->InitBuffer(32 * 1024);
  filter3->InitBuffer(32 * 1024);

  filter2->next_filter_ = filter3.Pass();
  filter1->next_filter_ = filter2.Pass();

  const size_t input_array_size = 64 * 1024;
  char input_array[input_array_size];
  size_t read_array_index = 0;
  for (size_t i = 0; i < input_array_size; i++) {
    input_array[i] = i % 113;
  }

  const size_t output_array_size = 4 * 1024;
  char output_array[output_array_size];

  size_t compare_array_index = 0;

  do {

    int amount_read = output_array_size;
    Filter::FilterStatus status = filter1->ReadData(output_array, &amount_read);
    EXPECT_NE(Filter::FILTER_ERROR, status);
    EXPECT_EQ(0, memcmp(output_array, input_array + compare_array_index,
                        amount_read));
    compare_array_index += amount_read;

    if (Filter::FILTER_DONE == status || Filter::FILTER_ERROR == status ||
        (Filter::FILTER_OK == status && amount_read == 0) ||
        (Filter::FILTER_NEED_MORE_DATA == status &&
         read_array_index == input_array_size))
      break;

    if (Filter::FILTER_OK == status)
      continue;

    ASSERT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
    ASSERT_NE(0, filter1->stream_buffer_size());
    size_t amount_to_copy = std::min(
        static_cast<size_t>(filter1->stream_buffer_size()),
        input_array_size - read_array_index);
    memcpy(filter1->stream_buffer()->data(),
           input_array + read_array_index,
           amount_to_copy);
    filter1->FlushStreamBuffer(amount_to_copy);
    read_array_index += amount_to_copy;
  } while (true);

  EXPECT_EQ(read_array_index, input_array_size);
  EXPECT_EQ(compare_array_index, input_array_size);
}

}
// Copyright 2014 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 "net/base/io_buffer.h"
#include "net/filter/filter.h"
#include "net/filter/mock_filter_context.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

class PassThroughFilter : public Filter {
 public:
  PassThroughFilter() : Filter(FILTER_TYPE_UNSUPPORTED) {}

  FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override {
    return CopyOut(dest_buffer, dest_len);
  }

  DISALLOW_COPY_AND_ASSIGN(PassThroughFilter);
};

}  // namespace

TEST(FilterTest, ContentTypeId) {
  // Check for basic translation of Content-Encoding, including case variations.
  EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE,
            Filter::ConvertEncodingToType("deflate"));
  EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE,
            Filter::ConvertEncodingToType("deflAte"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("gzip"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("GzIp"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("x-gzip"));
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP,
            Filter::ConvertEncodingToType("X-GzIp"));
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH,
            Filter::ConvertEncodingToType("sdch"));
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH,
            Filter::ConvertEncodingToType("sDcH"));
  EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED,
            Filter::ConvertEncodingToType("weird"));
  EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED,
            Filter::ConvertEncodingToType("strange"));
}

// Check various fixups that modify content encoding lists.
TEST(FilterTest, ApacheGzip) {
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(NULL);

  // Check that redundant gzip mime type removes only solo gzip encoding.
  const std::string kGzipMime1("application/x-gzip");
  const std::string kGzipMime2("application/gzip");
  const std::string kGzipMime3("application/x-gunzip");
  std::vector<Filter::FilterType> encoding_types;

  // First show it removes the gzip, given any gzip style mime type.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime1);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime2);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime3);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  // Check to be sure it doesn't remove everything when it has such a type.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  filter_context.SetMimeType(kGzipMime1);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types.front());

  // Check to be sure that gzip can survive with other mime types.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType("other/mime");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, GzipContentDispositionFilename) {
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(NULL);

  const std::string kGzipMime("application/x-tar");
  const std::string kContentDisposition("attachment; filename=\"foo.tgz\"");
  const std::string kURL("http://foo.com/getfoo.php");
  std::vector<Filter::FilterType> encoding_types;

  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kGzipMime);
  filter_context.SetURL(GURL(kURL));
  filter_context.SetContentDisposition(kContentDisposition);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(0U, encoding_types.size());
}

TEST(FilterTest, SdchEncoding) {
  // Handle content encodings including SDCH.
  const std::string kTextHtmlMime("text/html");
  MockFilterContext filter_context;
  // Empty handle indicates to filter that SDCH is active.
  filter_context.SetSdchResponse(
      SdchManager::CreateEmptyDictionarySetForTesting().Pass());

  std::vector<Filter::FilterType> encoding_types;

  // Check for most common encoding, and verify it survives unchanged.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType(kTextHtmlMime);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]);

  // Unchanged even with other mime types.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetMimeType("other/type");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]);

  // Solo SDCH is extended to include optional gunzip.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_SDCH);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);
}

TEST(FilterTest, MissingSdchEncoding) {
  // Handle interesting case where entire SDCH encoding assertion "got lost."
  const std::string kTextHtmlMime("text/html");
  MockFilterContext filter_context;
  filter_context.SetSdchResponse(
      SdchManager::CreateEmptyDictionarySetForTesting().Pass());

  std::vector<Filter::FilterType> encoding_types;

  // Loss of encoding, but it was an SDCH response with html type.
  encoding_types.clear();
  filter_context.SetMimeType(kTextHtmlMime);
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);

  // Loss of encoding, but it was an SDCH response with a prefix that says it
  // was an html type. Note that it *should* be the case that a precise match
  // with "text/html" we be collected by GetMimeType() and passed in, but we
  // coded the fixup defensively (scanning for a prefix of "text/html", so this
  // is an example which could survive such confusion in the caller).
  encoding_types.clear();
  filter_context.SetMimeType("text/html; charset=UTF-8");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);

  // No encoding, but it was an SDCH response with non-html type.
  encoding_types.clear();
  filter_context.SetMimeType("other/mime");
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(2U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]);
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]);
}

TEST(FilterTest, Svgz) {
  MockFilterContext filter_context;

  // Check that svgz files are only decompressed when not downloading.
  const std::string kSvgzMime("image/svg+xml");
  const std::string kSvgzUrl("http://ignore.com/foo.svgz");
  const std::string kSvgUrl("http://ignore.com/foo.svg");
  std::vector<Filter::FilterType> encoding_types;

  // Test svgz extension
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  // Test svg extension
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kSvgzMime);
  filter_context.SetURL(GURL(kSvgUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, UnsupportedMimeGzip) {
  // From issue 8170 - handling files with Content-Encoding: x-gzip
  MockFilterContext filter_context;
  std::vector<Filter::FilterType> encoding_types;
  const std::string kTarMime("application/x-tar");
  const std::string kCpioMime("application/x-cpio");
  const std::string kTarUrl("http://ignore.com/foo.tar");
  const std::string kTargzUrl("http://ignore.com/foo.tar.gz");
  const std::string kTgzUrl("http://ignore.com/foo.tgz");
  const std::string kBadTgzUrl("http://ignore.com/foo.targz");
  const std::string kUrl("http://ignore.com/foo");

  // Firefox 3 does not decompress when we have unsupported mime types for
  // certain filenames.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTargzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kCpioMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  // Same behavior for downloads.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kCpioMime);
  filter_context.SetURL(GURL(kTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());

  // Unsupported mime type with wrong file name, decompressed.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kTarUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kBadTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  // Same behavior for downloads.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kTarMime);
  filter_context.SetURL(GURL(kBadTgzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());
}

TEST(FilterTest, SupportedMimeGzip) {
  // From issue 16430 - Files with supported mime types should be decompressed,
  // even though these files end in .gz/.tgz.
  MockFilterContext filter_context;
  std::vector<Filter::FilterType> encoding_types;
  const std::string kGzUrl("http://ignore.com/foo.gz");
  const std::string kUrl("http://ignore.com/foo");
  const std::string kHtmlMime("text/html");
  const std::string kJavascriptMime("text/javascript");

  // For files that does not end in .gz/.tgz, we always decompress.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  // And also decompress files that end in .gz/.tgz.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(false);
  filter_context.SetMimeType(kJavascriptMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  ASSERT_EQ(1U, encoding_types.size());
  EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front());

  // Except on downloads, where they just get saved.
  encoding_types.clear();
  encoding_types.push_back(Filter::FILTER_TYPE_GZIP);
  filter_context.SetDownload(true);
  filter_context.SetMimeType(kHtmlMime);
  filter_context.SetURL(GURL(kGzUrl));
  Filter::FixupEncodingTypes(filter_context, &encoding_types);
  EXPECT_TRUE(encoding_types.empty());
}

// Make sure a series of three pass-through filters copies the data cleanly.
// Regression test for http://crbug.com/418975.
TEST(FilterTest, ThreeFilterChain) {
  scoped_ptr<PassThroughFilter> filter1(new PassThroughFilter);
  scoped_ptr<PassThroughFilter> filter2(new PassThroughFilter);
  scoped_ptr<PassThroughFilter> filter3(new PassThroughFilter);

  filter1->InitBuffer(32 * 1024);
  filter2->InitBuffer(32 * 1024);
  filter3->InitBuffer(32 * 1024);

  filter2->next_filter_ = filter3.Pass();
  filter1->next_filter_ = filter2.Pass();

  // Initialize the input array with a varying byte sequence.
  const size_t input_array_size = 64 * 1024;
  char input_array[input_array_size];
  size_t read_array_index = 0;
  for (size_t i = 0; i < input_array_size; i++) {
    input_array[i] = i % 113;
  }

  const size_t output_array_size = 4 * 1024;
  char output_array[output_array_size];

  size_t compare_array_index = 0;

  do {
    // Read data from the filter chain.
    int amount_read = output_array_size;
    Filter::FilterStatus status = filter1->ReadData(output_array, &amount_read);
    EXPECT_NE(Filter::FILTER_ERROR, status);
    EXPECT_EQ(0, memcmp(output_array, input_array + compare_array_index,
                        amount_read));
    compare_array_index += amount_read;

    // Detect the various indications that data transfer along the chain is
    // complete.
    if (Filter::FILTER_DONE == status || Filter::FILTER_ERROR == status ||
        (Filter::FILTER_OK == status && amount_read == 0) ||
        (Filter::FILTER_NEED_MORE_DATA == status &&
         read_array_index == input_array_size))
      break;

    if (Filter::FILTER_OK == status)
      continue;

    // Write needed data into the filter chain.
    ASSERT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
    ASSERT_NE(0, filter1->stream_buffer_size());
    size_t amount_to_copy = std::min(
        static_cast<size_t>(filter1->stream_buffer_size()),
        input_array_size - read_array_index);
    memcpy(filter1->stream_buffer()->data(),
           input_array + read_array_index,
           amount_to_copy);
    filter1->FlushStreamBuffer(amount_to_copy);
    read_array_index += amount_to_copy;
  } while (true);

  EXPECT_EQ(read_array_index, input_array_size);
  EXPECT_EQ(compare_array_index, input_array_size);
}

}  // Namespace net
