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..ecf8dd9cc165f28aef5bf4c1e8ca8383385966e6 |
--- /dev/null |
+++ b/net/filter/sdch_policy_delegate_unittest.cc |
@@ -0,0 +1,344 @@ |
+// 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(bool possible_pass_through) { |
+ return base::WrapUnique(new SdchPolicyDelegate( |
+ possible_pass_through, 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 |
+ |
+// Tests FixUpSdchContentEncodings() does the right thing when Sdch is present |
+// in the encodings. |
+TEST_F(SdchPolicyDelegateTest, FixUpSdchContentEncodingsWhenSdchPresent) { |
+ 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]); |
+} |
+ |
+// Tests FixUpSdchContentEncodings() does the right thing when Sdch is missing |
+// from the encodings. |
+TEST_F(SdchPolicyDelegateTest, FixUpSdchContentEncodingsSdchMissing) { |
+ 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) { |
+ // TODO(xunjieli): Right now possible pass-throughs are handled by |
+ // meta-refresh. This is done to match old behavior. |
+ set_dictionaries_advertised( |
+ SdchManager::CreateEmptyDictionarySetForTesting()); |
+ set_mime_type("text/html"); |
+ std::unique_ptr<SdchPolicyDelegate> delegate(GetDelegate(true)); |
+ std::string replace_output; |
+ EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
+ delegate->OnDictionaryIdError(&replace_output)); |
+ EXPECT_EQ(kRefreshHtml, replace_output); |
+ |
+ replace_output.clear(); |
+ EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
+ delegate->OnGetDictionaryError(&replace_output)); |
+ EXPECT_EQ(kRefreshHtml, 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(true); |
+ replace_output.clear(); |
+ 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(false); |
+ EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
+ delegate->OnDictionaryIdError(&replace_output)); |
+ |
+ // non-200 case. |
+ replace_output.clear(); |
+ set_response_code(500); |
+ set_mime_type("text/html; charset=UTF-8"); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
+ delegate->OnDictionaryIdError(&replace_output)); |
+ EXPECT_EQ(kRefreshHtml, replace_output); |
+ |
+ // Cached content case. |
+ set_response_code(200); |
+ set_is_cached_content(true); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
+ delegate->OnDictionaryIdError(&replace_output)); |
+ |
+ // Dictionary not advertised case. |
+ set_is_cached_content(false); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
+ delegate->OnDictionaryIdError(&replace_output)); |
+ |
+ // Dictionary advertised case. |
+ replace_output.clear(); |
+ set_is_cached_content(false); |
+ set_dictionaries_advertised( |
+ SdchManager::CreateEmptyDictionarySetForTesting()); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
+ delegate->OnDictionaryIdError(&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(false); |
+ EXPECT_EQ(SdchPolicyDelegate::PASS_THROUGH, |
+ delegate->OnGetDictionaryError(&replace_output)); |
+ |
+ // Meta-refresh case. |
+ set_response_code(200); |
+ set_mime_type("text/html; charset=UTF-8"); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::REPLACE_OUTPUT, |
+ delegate->OnGetDictionaryError(&replace_output)); |
+ EXPECT_EQ(kRefreshHtml, replace_output); |
+ |
+ // Meta-refresh not applied case. Hard failure. |
+ replace_output.clear(); |
+ set_mime_type("other/mime"); |
+ delegate = GetDelegate(false); |
+ EXPECT_EQ(SdchPolicyDelegate::NONE, |
+ delegate->OnGetDictionaryError(&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(false); |
+ 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(false); |
+ 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(false); |
+ |
+ const std::string* dictionary_text; |
+ delegate->OnGetDictionary(server_id, &dictionary_text); |
+ delegate->OnStreamDestroyed(SdchSourceStream::STATE_DECODE, false, false); |
+ EXPECT_EQ(1, observer.dictionary_used_calls()); |
+ EXPECT_EQ(server_id, observer.last_server_hash()); |
+} |
+ |
+} // namespace net |