Chromium Code Reviews| Index: net/filter/sdch_policy_delegate_unittest.cc |
| diff --git a/net/filter/sdch_policy_delegate_unittest.cc b/net/filter/sdch_policy_delegate_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8c49e09cbce774359d9d00422428e57a3635d7a4 |
| --- /dev/null |
| +++ b/net/filter/sdch_policy_delegate_unittest.cc |
| @@ -0,0 +1,331 @@ |
| +// 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 "net/filter/sdch_policy_delegate.h" |
| + |
| +#include <string> |
| + |
| +#include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "net/base/sdch_manager.h" |
| +#include "net/base/sdch_observer.h" |
| +#include "net/url_request/url_request_http_job.h" |
| +#include "net/url_request/url_request_test_util.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace net { |
| + |
| +namespace { |
| + |
| +// 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"; |
| + |
| +const char kRefreshHtml[] = |
| + "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>"; |
| + |
| +const char kTextHtmlMime[] = "text/html"; |
| + |
| +class SimpleSdchObserver : public SdchObserver { |
| + public: |
| + explicit SimpleSdchObserver(SdchManager* manager) |
| + : dictionary_used_(0), manager_(manager) { |
| + manager_->AddObserver(this); |
| + } |
| + ~SimpleSdchObserver() override { manager_->RemoveObserver(this); } |
| + |
| + // SdchObserver |
| + void OnDictionaryUsed(const std::string& server_hash) override { |
| + dictionary_used_++; |
| + last_server_hash_ = server_hash; |
| + } |
| + |
| + int dictionary_used_calls() const { return dictionary_used_; } |
| + std::string last_server_hash() const { return last_server_hash_; } |
| + |
| + void OnDictionaryAdded(const GURL& /* dictionary_url */, |
| + const std::string& /* server_hash */) override {} |
| + void OnDictionaryRemoved(const std::string& /* server_hash */) override {} |
| + void OnGetDictionary(const GURL& /* request_url */, |
| + const GURL& /* dictionary_url */) override {} |
| + void OnClearDictionaries() override {} |
| + |
| + private: |
| + int dictionary_used_; |
| + std::string last_server_hash_; |
| + SdchManager* manager_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SimpleSdchObserver); |
| +}; |
| + |
| +std::string NewSdchDictionary(const std::string& domain) { |
| + std::string dictionary; |
| + if (!domain.empty()) { |
| + dictionary.append("Domain: "); |
| + dictionary.append(domain); |
| + dictionary.append("\n"); |
| + } |
| + dictionary.append("\n"); |
| + dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1); |
| + return dictionary; |
| +} |
| + |
| +// Inherit from URLRequestHttpJob to expose the hidden constructor. |
| +class TestURLRequestHttpJob : public URLRequestHttpJob { |
| + public: |
| + explicit TestURLRequestHttpJob(URLRequest* request) |
| + : URLRequestHttpJob(request, |
| + request->context()->network_delegate(), |
| + request->context()->http_user_agent_settings()) {} |
| + ~TestURLRequestHttpJob() override {} |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(TestURLRequestHttpJob); |
| +}; |
| + |
| +class SdchPolicyDelegateTest : public testing::Test { |
| + protected: |
| + SdchPolicyDelegateTest() |
| + : sdch_manager_(new SdchManager), |
| + is_cached_content_(false), |
| + response_code_(200) { |
| + req_ = context_.CreateRequest(GURL("http://www.example.com"), |
| + DEFAULT_PRIORITY, &request_delegate_); |
| + job_.reset(new TestURLRequestHttpJob(req_.get())); |
| + } |
| + |
| + std::unique_ptr<SdchPolicyDelegate> GetDelegate() { |
| + return base::WrapUnique(new SdchPolicyDelegate( |
| + job_.get(), mime_type_, url_, is_cached_content_, sdch_manager_.get(), |
| + std::move(dictionary_set_), response_code_, net_log_)); |
| + } |
| + |
| + void set_mime_type(const std::string& mime_type) { mime_type_ = mime_type; } |
| + void set_url(const GURL& url) { url_ = url; } |
| + void set_is_cached_content(bool is_cached_content) { |
| + is_cached_content_ = is_cached_content; |
| + } |
| + void set_response_code(int response_code) { response_code_ = response_code; } |
| + void set_dictionaries_advertised( |
| + std::unique_ptr<SdchManager::DictionarySet> dictionary_set) { |
| + dictionary_set_ = std::move(dictionary_set); |
| + } |
| + |
| + // Getters: |
| + std::unique_ptr<SdchPolicyDelegate> delegate() { |
| + return std::move(delegate_); |
| + } |
| + |
| + SdchManager* sdch_manager() const { return sdch_manager_.get(); } |
| + |
| + private: |
| + TestURLRequestContext context_; |
| + TestDelegate request_delegate_; |
| + std::unique_ptr<URLRequest> req_; |
| + std::unique_ptr<TestURLRequestHttpJob> job_; |
| + std::unique_ptr<SdchManager> sdch_manager_; |
| + std::unique_ptr<SdchPolicyDelegate> delegate_; |
| + std::string mime_type_; |
| + GURL url_; |
| + bool is_cached_content_; |
| + int response_code_; |
| + NetLogWithSource net_log_; |
| + std::unique_ptr<SdchManager::DictionarySet> dictionary_set_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SdchPolicyDelegateTest); |
| +}; |
| + |
| +} // namespace |
| + |
| +TEST_F(SdchPolicyDelegateTest, FixUpSdchContentEncodings) { |
|
Randy Smith (Not in Mondays)
2016/10/13 22:05:26
nit, suggestion: Given that the next test is also
xunjieli
2016/10/14 18:23:34
Done.
|
| + std::vector<SourceStream::SourceType> encoding_types; |
| + |
| + // Check for most common encoding, and verify it survives unchanged. |
| + encoding_types.clear(); |
| + encoding_types.push_back(SourceStream::TYPE_SDCH); |
| + encoding_types.push_back(SourceStream::TYPE_GZIP); |
| + std::unique_ptr<SdchManager::DictionarySet> dictionary_set( |
| + SdchManager::CreateEmptyDictionarySetForTesting()); |
| + NetLogWithSource net_log; |
| + SdchPolicyDelegate::FixUpSdchContentEncodings( |
| + net_log, kTextHtmlMime, dictionary_set.get(), &encoding_types); |
| + ASSERT_EQ(2U, encoding_types.size()); |
| + EXPECT_EQ(SourceStream::TYPE_SDCH, encoding_types[0]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP, encoding_types[1]); |
| + |
| + // Unchanged even with other mime types. |
| + encoding_types.clear(); |
| + encoding_types.push_back(SourceStream::TYPE_SDCH); |
| + encoding_types.push_back(SourceStream::TYPE_GZIP); |
| + SdchPolicyDelegate::FixUpSdchContentEncodings( |
| + net_log, "other/type", dictionary_set.get(), &encoding_types); |
| + ASSERT_EQ(2U, encoding_types.size()); |
| + EXPECT_EQ(SourceStream::TYPE_SDCH, encoding_types[0]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP, encoding_types[1]); |
| + |
| + // Solo SDCH is extended to include optional gunzip. |
| + encoding_types.clear(); |
| + encoding_types.push_back(SourceStream::TYPE_SDCH); |
| + SdchPolicyDelegate::FixUpSdchContentEncodings( |
| + net_log, "other/type", dictionary_set.get(), &encoding_types); |
| + ASSERT_EQ(2U, encoding_types.size()); |
| + EXPECT_EQ(SourceStream::TYPE_SDCH, encoding_types[0]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP_FALLBACK, encoding_types[1]); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, MissingSdchEncoding) { |
| + std::vector<SourceStream::SourceType> encoding_types; |
| + |
| + // When content encodings are empty, but dictionary is advertised. Make sure |
| + // that Sdch and Gzip filters are added. |
| + std::unique_ptr<SdchManager::DictionarySet> dictionary_set( |
| + SdchManager::CreateEmptyDictionarySetForTesting()); |
| + NetLogWithSource net_log; |
| + SdchPolicyDelegate::FixUpSdchContentEncodings( |
| + net_log, "other/type", dictionary_set.get(), &encoding_types); |
| + ASSERT_EQ(2U, encoding_types.size()); |
| + EXPECT_EQ(SourceStream::TYPE_SDCH_POSSIBLE, encoding_types[0]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP_FALLBACK, encoding_types[1]); |
| + |
| + // Only Gzip present, but dictionary is advertised. |
| + encoding_types.clear(); |
| + encoding_types.push_back(SourceStream::TYPE_GZIP); |
| + SdchPolicyDelegate::FixUpSdchContentEncodings( |
| + net_log, "other/type", dictionary_set.get(), &encoding_types); |
| + ASSERT_EQ(3U, encoding_types.size()); |
| + EXPECT_EQ(SourceStream::TYPE_SDCH_POSSIBLE, encoding_types[0]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP_FALLBACK, encoding_types[1]); |
| + EXPECT_EQ(SourceStream::TYPE_GZIP, encoding_types[2]); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, PossiblePassThrough) { |
| + std::unique_ptr<SdchPolicyDelegate> delegate(GetDelegate()); |
| + std::string replace_output; |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnDictionaryIdError(true, &replace_output)); |
| + |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnGetDictionaryError(true, &replace_output)); |
| + // |possible_pass_through_| shouldn't have any effect on non-dictionary |
| + // errors, because at this point a dictionary is successfully fetched. |
| + // Make sure meta refresh is issued. |
| + set_mime_type("text/html; charset=UTF-8"); |
| + set_dictionaries_advertised( |
| + SdchManager::CreateEmptyDictionarySetForTesting()); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
| + delegate->OnDecodingError(&replace_output)); |
| + EXPECT_EQ(kRefreshHtml, replace_output); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, OnDictionaryIdError) { |
| + std::unique_ptr<SdchPolicyDelegate> delegate; |
| + // 404 case. |
| + set_response_code(404); |
| + std::string replace_output; |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnDictionaryIdError(false, &replace_output)); |
| + |
| + // non-200 case. |
| + replace_output.clear(); |
| + set_response_code(500); |
| + set_mime_type("text/html; charset=UTF-8"); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
| + delegate->OnDictionaryIdError(false, &replace_output)); |
| + EXPECT_EQ(kRefreshHtml, replace_output); |
| + |
| + // Cached content case. |
| + set_response_code(200); |
| + set_is_cached_content(true); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnDictionaryIdError(false, &replace_output)); |
| + |
| + // Dictionary not advertised case. |
| + set_is_cached_content(false); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnDictionaryIdError(false, &replace_output)); |
| + |
| + // Dictionary advertised case. |
| + replace_output.clear(); |
| + set_is_cached_content(false); |
| + set_dictionaries_advertised( |
| + SdchManager::CreateEmptyDictionarySetForTesting()); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
| + delegate->OnDictionaryIdError(false, &replace_output)); |
| + EXPECT_EQ(kRefreshHtml, replace_output); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, OnGetDictionaryError) { |
| + std::unique_ptr<SdchPolicyDelegate> delegate; |
| + |
| + // 404 case. |
| + set_response_code(404); |
| + std::string replace_output; |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
| + delegate->OnGetDictionaryError(false, &replace_output)); |
| + |
| + // Meta-refresh case. |
| + set_response_code(200); |
| + set_mime_type("text/html; charset=UTF-8"); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
| + delegate->OnGetDictionaryError(false, &replace_output)); |
| + EXPECT_EQ(kRefreshHtml, replace_output); |
| + |
| + // Meta-refresh not applied case. Hard failure. |
| + replace_output.clear(); |
| + set_mime_type("other/mime"); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::NONE, |
| + delegate->OnGetDictionaryError(false, &replace_output)); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, OnDecodingError) { |
| + std::unique_ptr<SdchPolicyDelegate> delegate; |
| + |
| + std::string replace_output; |
| + // Meta-refresh case. |
| + set_mime_type("text/html; charset=UTF-8"); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
| + delegate->OnDecodingError(&replace_output)); |
| + EXPECT_EQ(kRefreshHtml, replace_output); |
| + |
| + // Meta-refresh not applied case. Hard failure. |
| + replace_output.clear(); |
| + set_mime_type("other/mime"); |
| + delegate = GetDelegate(); |
| + EXPECT_EQ(SdchPolicyDelegate::NONE, |
| + delegate->OnDecodingError(&replace_output)); |
| +} |
| + |
| +TEST_F(SdchPolicyDelegateTest, DictionaryUsedSignal) { |
| + // Construct a valid SDCH dictionary from a VCDIFF dictionary. |
| + const std::string kSampleDomain = "sdchtest.com"; |
| + std::string dictionary(NewSdchDictionary(kSampleDomain)); |
| + std::string url_string = "http://" + kSampleDomain; |
| + GURL url(url_string); |
| + std::string server_id; |
| + sdch_manager()->AddSdchDictionary(dictionary, url, &server_id); |
| + SimpleSdchObserver observer(sdch_manager()); |
| + set_dictionaries_advertised(sdch_manager()->GetDictionarySet(url)); |
| + std::unique_ptr<SdchPolicyDelegate> delegate = GetDelegate(); |
| + |
| + const std::string* dictionary_text; |
| + delegate->OnGetDictionary(server_id, &dictionary_text); |
| + delegate->OnStreamDestroyed(SdchSourceStream::STATE_DECODE, std::string(), |
| + nullptr); |
| + EXPECT_EQ(1, observer.dictionary_used_calls()); |
| + EXPECT_EQ(server_id, observer.last_server_hash()); |
| +} |
| + |
| +} // namespace net |