// 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 #include #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "base/test/simple_test_clock.h" #include "net/base/sdch_manager.h" #include "net/base/sdch_observer.h" #include "net/log/net_log.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace net { //------------------------------------------------------------------------------ // 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"; //------------------------------------------------------------------------------ class MockSdchObserver : public SdchObserver { public: MockSdchObserver() : dictionary_added_notifications_(0), dictionary_removed_notifications_(0), dictionary_used_notifications_(0), get_dictionary_notifications_(0), clear_dictionaries_notifications_(0) {} int dictionary_added_notifications() const { return dictionary_added_notifications_; } int dictionary_removed_notifications() const { return dictionary_removed_notifications_; } std::string last_server_hash() const { return last_server_hash_; } int dictionary_used_notifications() const { return dictionary_used_notifications_; } const GURL& last_dictionary_request_url() const { return last_dictionary_request_url_; } const GURL& last_dictionary_url() const { return last_dictionary_url_; } int get_dictionary_notifications() const { return get_dictionary_notifications_; } int clear_dictionary_notifications() const { return clear_dictionaries_notifications_; } // SdchObserver implementation void OnDictionaryAdded(const GURL& dictionary_url, const std::string& server_hash) override { last_server_hash_ = server_hash; last_dictionary_url_ = dictionary_url; ++dictionary_added_notifications_; } void OnDictionaryRemoved(const std::string& server_hash) override { last_server_hash_ = server_hash; ++dictionary_removed_notifications_; } void OnDictionaryUsed(const std::string& server_hash) override { last_server_hash_ = server_hash; ++dictionary_used_notifications_; } void OnGetDictionary(const GURL& request_url, const GURL& dictionary_url) override { ++get_dictionary_notifications_; last_dictionary_request_url_ = request_url; last_dictionary_url_ = dictionary_url; } void OnClearDictionaries() override { ++clear_dictionaries_notifications_; } private: int dictionary_added_notifications_; int dictionary_removed_notifications_; int dictionary_used_notifications_; int get_dictionary_notifications_; int clear_dictionaries_notifications_; std::string last_server_hash_; GURL last_dictionary_request_url_; GURL last_dictionary_url_; DISALLOW_COPY_AND_ASSIGN(MockSdchObserver); }; class SdchManagerTest : public testing::Test { protected: SdchManagerTest() : sdch_manager_(new SdchManager) {} ~SdchManagerTest() override {} SdchManager* sdch_manager() { return sdch_manager_.get(); } // Attempt to add a dictionary to the manager and probe for success or // failure. bool AddSdchDictionary(const std::string& dictionary_text, const GURL& gurl) { return sdch_manager_->AddSdchDictionary(dictionary_text, gurl, nullptr) == SDCH_OK; } private: scoped_ptr sdch_manager_; }; static 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; } TEST_F(SdchManagerTest, DomainSupported) { GURL google_url("http://www.google.com"); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(google_url)); } TEST_F(SdchManagerTest, DomainBlacklisting) { GURL test_url("http://www.test.com"); GURL google_url("http://www.google.com"); sdch_manager()->BlacklistDomain(test_url, SDCH_OK); EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(test_url)); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(google_url)); sdch_manager()->BlacklistDomain(google_url, SDCH_OK); EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(google_url)); } TEST_F(SdchManagerTest, DomainBlacklistingCaseSensitivity) { GURL test_url("http://www.TesT.com"); GURL test2_url("http://www.tEst.com"); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(test_url)); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(test2_url)); sdch_manager()->BlacklistDomain(test_url, SDCH_OK); EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(test2_url)); } TEST_F(SdchManagerTest, BlacklistingReset) { GURL gurl("http://mytest.DoMain.com"); std::string domain(gurl.host()); sdch_manager()->ClearBlacklistings(); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0); EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), 0); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(gurl)); } TEST_F(SdchManagerTest, BlacklistingSingleBlacklist) { GURL gurl("http://mytest.DoMain.com"); std::string domain(gurl.host()); sdch_manager()->ClearBlacklistings(); sdch_manager()->BlacklistDomain(gurl, SDCH_OK); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 1); EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), 1); // Check that any domain lookup reduces the blacklist counter. EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(gurl)); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(gurl)); } TEST_F(SdchManagerTest, BlacklistingExponential) { GURL gurl("http://mytest.DoMain.com"); std::string domain(gurl.host()); sdch_manager()->ClearBlacklistings(); int exponential = 1; for (int i = 1; i < 100; ++i) { sdch_manager()->BlacklistDomain(gurl, SDCH_OK); EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), exponential); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), exponential); EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(gurl)); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), exponential - 1); // Simulate a large number of domain checks (which eventually remove the // blacklisting). sdch_manager()->ClearDomainBlacklisting(domain); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 0); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(gurl)); // Predict what exponential backoff will be. exponential = 1 + 2 * exponential; if (exponential < 0) exponential = INT_MAX; // We don't wrap. } } TEST_F(SdchManagerTest, CanSetExactMatchDictionary) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Perfect match should work. EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); } TEST_F(SdchManagerTest, CanAdvertiseDictionaryOverHTTP) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); // HTTP target URL can advertise dictionary. EXPECT_TRUE(sdch_manager()->GetDictionarySet( GURL("http://" + dictionary_domain + "/test"))); } TEST_F(SdchManagerTest, CanNotAdvertiseDictionaryOverHTTPS) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); // HTTPS target URL should NOT advertise dictionary. EXPECT_FALSE(sdch_manager()->GetDictionarySet( GURL("https://" + dictionary_domain + "/test"))); } TEST_F(SdchManagerTest, CanUseHTTPSDictionaryOverHTTPSIfEnabled) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("https://" + dictionary_domain))); GURL target_url("https://" + dictionary_domain + "/test"); // HTTPS target URL should advertise dictionary if secure scheme support is // enabled. EXPECT_TRUE(sdch_manager()->GetDictionarySet(target_url)); // Dictionary should be available. std::string client_hash; std::string server_hash; sdch_manager()->GenerateHash(dictionary_text, &client_hash, &server_hash); SdchProblemCode problem_code; scoped_ptr dict_set( sdch_manager()->GetDictionarySetByHash( target_url, server_hash, &problem_code)); EXPECT_EQ(SDCH_OK, problem_code); EXPECT_TRUE(dict_set.get()); EXPECT_TRUE(dict_set->GetDictionaryText(server_hash)); } TEST_F(SdchManagerTest, CanNotUseHTTPDictionaryOverHTTPS) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); GURL target_url("https://" + dictionary_domain + "/test"); // HTTPS target URL should not advertise dictionary acquired over HTTP even if // secure scheme support is enabled. EXPECT_FALSE(sdch_manager()->GetDictionarySet(target_url)); std::string client_hash; std::string server_hash; sdch_manager()->GenerateHash(dictionary_text, &client_hash, &server_hash); SdchProblemCode problem_code; scoped_ptr dict_set( sdch_manager()->GetDictionarySetByHash( target_url, server_hash, &problem_code)); EXPECT_FALSE(dict_set.get()); EXPECT_EQ(SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME, problem_code); } TEST_F(SdchManagerTest, CanNotUseHTTPSDictionaryOverHTTP) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("https://" + dictionary_domain))); GURL target_url("http://" + dictionary_domain + "/test"); // HTTP target URL should not advertise dictionary acquired over HTTPS even if // secure scheme support is enabled. EXPECT_FALSE(sdch_manager()->GetDictionarySet(target_url)); std::string client_hash; std::string server_hash; sdch_manager()->GenerateHash(dictionary_text, &client_hash, &server_hash); SdchProblemCode problem_code; scoped_ptr dict_set( sdch_manager()->GetDictionarySetByHash( target_url, server_hash, &problem_code)); EXPECT_FALSE(dict_set.get()); EXPECT_EQ(SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME, problem_code); } TEST_F(SdchManagerTest, FailToSetDomainMismatchDictionary) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Fail the "domain match" requirement. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://y.z.google.com"))); } TEST_F(SdchManagerTest, FailToSetDotHostPrefixDomainDictionary) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Fail the HD with D being the domain and H having a dot requirement. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://w.x.y.z.google.com"))); } TEST_F(SdchManagerTest, FailToSetDotHostPrefixDomainDictionaryTrailingDot) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Fail the HD with D being the domain and H having a dot requirement. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://w.x.y.z.google.com."))); } TEST_F(SdchManagerTest, FailToSetRepeatPrefixWithDotDictionary) { // Make sure that a prefix that matches the domain postfix won't confuse // the validation checks. std::string dictionary_domain("www.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Fail the HD with D being the domain and H having a dot requirement. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://www.google.com.www.google.com"))); } TEST_F(SdchManagerTest, CanSetLeadingDotDomainDictionary) { // Make sure that a prefix that matches the domain postfix won't confuse // the validation checks. std::string dictionary_domain(".google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Verify that a leading dot in the domain is acceptable, as long as the host // name does not contain any dots preceding the matched domain name. EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://www.google.com"))); } TEST_F(SdchManagerTest, CanSetLeadingDotDomainDictionaryFromURLWithTrailingDot) { // Make sure that a prefix that matches the domain postfix won't confuse // the validation checks. std::string dictionary_domain(".google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Verify that a leading dot in the domain is acceptable, as long as the host // name does not contain any dots preceding the matched domain name. EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://www.google.com."))); } TEST_F(SdchManagerTest, CannotSetLeadingDotDomainDictionary) { // Make sure that a prefix that matches the domain postfix won't confuse // the validation checks. std::string dictionary_domain(".google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Verify that a leading dot in the domain does not affect the name containing // dots failure. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://www.subdomain.google.com"))); } TEST_F(SdchManagerTest, CannotSetLeadingDotDomainDictionaryTrailingDot) { // Make sure that a prefix that matches the domain postfix won't confuse // the validation checks. std::string dictionary_domain(".google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Verify that a trailing period in the URL doesn't affect the check. EXPECT_FALSE(AddSdchDictionary(dictionary_text, GURL("http://www.subdomain.google.com."))); } // Make sure the order of the tests is not helping us or confusing things. // See test CanSetExactMatchDictionary above for first try. TEST_F(SdchManagerTest, CanStillSetExactMatchDictionary) { std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); // Perfect match should *STILL* work. EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); } // The following are only applicable while we have a latency test in the code, // and can be removed when that functionality is stripped. TEST_F(SdchManagerTest, LatencyTestControls) { GURL url("http://www.google.com"); GURL url2("http://www.google2.com"); // First make sure we default to false. EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url)); EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2)); // That we can set each to true. sdch_manager()->SetAllowLatencyExperiment(url, true); EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url)); EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2)); sdch_manager()->SetAllowLatencyExperiment(url2, true); EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url)); EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url2)); // And can reset them to false. sdch_manager()->SetAllowLatencyExperiment(url, false); EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url)); EXPECT_TRUE(sdch_manager()->AllowLatencyExperiment(url2)); sdch_manager()->SetAllowLatencyExperiment(url2, false); EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url)); EXPECT_FALSE(sdch_manager()->AllowLatencyExperiment(url2)); } TEST_F(SdchManagerTest, CanUseMultipleManagers) { SdchManager second_manager; std::string dictionary_domain_1("x.y.z.google.com"); std::string dictionary_domain_2("x.y.z.chromium.org"); std::string dictionary_text_1(NewSdchDictionary(dictionary_domain_1)); std::string dictionary_text_2(NewSdchDictionary(dictionary_domain_2)); std::string tmp_hash; std::string server_hash_1; std::string server_hash_2; SdchManager::GenerateHash(dictionary_text_1, &tmp_hash, &server_hash_1); SdchManager::GenerateHash(dictionary_text_2, &tmp_hash, &server_hash_2); // Confirm that if you add directories to one manager, you // can't get them from the other. EXPECT_TRUE(AddSdchDictionary(dictionary_text_1, GURL("http://" + dictionary_domain_1))); scoped_ptr dict_set; SdchProblemCode problem_code; dict_set = sdch_manager()->GetDictionarySetByHash( GURL("http://" + dictionary_domain_1 + "/random_url"), server_hash_1, &problem_code); EXPECT_TRUE(dict_set); EXPECT_TRUE(dict_set->GetDictionaryText(server_hash_1)); EXPECT_EQ(SDCH_OK, problem_code); second_manager.AddSdchDictionary( dictionary_text_2, GURL("http://" + dictionary_domain_2), nullptr); dict_set = second_manager.GetDictionarySetByHash( GURL("http://" + dictionary_domain_2 + "/random_url"), server_hash_2, &problem_code); EXPECT_TRUE(dict_set); EXPECT_TRUE(dict_set->GetDictionaryText(server_hash_2)); EXPECT_EQ(SDCH_OK, problem_code); dict_set = sdch_manager()->GetDictionarySetByHash( GURL("http://" + dictionary_domain_2 + "/random_url"), server_hash_2, &problem_code); EXPECT_FALSE(dict_set); EXPECT_EQ(SDCH_DICTIONARY_HASH_NOT_FOUND, problem_code); dict_set = second_manager.GetDictionarySetByHash( GURL("http://" + dictionary_domain_1 + "/random_url"), server_hash_1, &problem_code); EXPECT_FALSE(dict_set); EXPECT_EQ(SDCH_DICTIONARY_HASH_NOT_FOUND, problem_code); } TEST_F(SdchManagerTest, ClearDictionaryData) { std::string dictionary_domain("x.y.z.google.com"); GURL blacklist_url("http://bad.chromium.org"); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); std::string tmp_hash; std::string server_hash; SdchManager::GenerateHash(dictionary_text, &tmp_hash, &server_hash); EXPECT_TRUE(AddSdchDictionary(dictionary_text, GURL("http://" + dictionary_domain))); scoped_ptr dict_set; SdchProblemCode problem_code; dict_set = sdch_manager()->GetDictionarySetByHash( GURL("http://" + dictionary_domain + "/random_url"), server_hash, &problem_code); EXPECT_TRUE(dict_set); EXPECT_TRUE(dict_set->GetDictionaryText(server_hash)); EXPECT_EQ(SDCH_OK, problem_code); sdch_manager()->BlacklistDomain(GURL(blacklist_url), SDCH_OK); EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET, sdch_manager()->IsInSupportedDomain(blacklist_url)); sdch_manager()->ClearData(); dict_set = sdch_manager()->GetDictionarySetByHash( GURL("http://" + dictionary_domain + "/random_url"), server_hash, &problem_code); EXPECT_FALSE(dict_set); EXPECT_EQ(SDCH_DICTIONARY_HASH_NOT_FOUND, problem_code); EXPECT_EQ(SDCH_OK, sdch_manager()->IsInSupportedDomain(blacklist_url)); } TEST_F(SdchManagerTest, GetDictionaryNotification) { GURL test_request_gurl(GURL("http://www.example.com/data")); GURL test_dictionary_gurl(GURL("http://www.example.com/dict")); MockSdchObserver observer; sdch_manager()->AddObserver(&observer); EXPECT_EQ(0, observer.get_dictionary_notifications()); sdch_manager()->OnGetDictionary(test_request_gurl, test_dictionary_gurl); EXPECT_EQ(1, observer.get_dictionary_notifications()); EXPECT_EQ(test_request_gurl, observer.last_dictionary_request_url()); EXPECT_EQ(test_dictionary_gurl, observer.last_dictionary_url()); sdch_manager()->RemoveObserver(&observer); sdch_manager()->OnGetDictionary(test_request_gurl, test_dictionary_gurl); EXPECT_EQ(1, observer.get_dictionary_notifications()); EXPECT_EQ(test_request_gurl, observer.last_dictionary_request_url()); EXPECT_EQ(test_dictionary_gurl, observer.last_dictionary_url()); } TEST_F(SdchManagerTest, ExpirationCheckedProperly) { // Create an SDCH dictionary with an expiration time in the past. std::string dictionary_domain("x.y.z.google.com"); std::string dictionary_text(base::StringPrintf("Domain: %s\nMax-age: -1\n\n", dictionary_domain.c_str())); dictionary_text.append( kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1); std::string client_hash; std::string server_hash; SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash); GURL target_gurl("http://" + dictionary_domain); AddSdchDictionary(dictionary_text, target_gurl); // It should be visible if looked up by hash whether expired or not. SdchProblemCode problem_code; scoped_ptr hash_set( sdch_manager()->GetDictionarySetByHash(target_gurl, server_hash, &problem_code)); ASSERT_TRUE(hash_set); ASSERT_EQ(SDCH_OK, problem_code); // Make sure it's not visible for advertisement, but is visible // if looked up by hash. EXPECT_FALSE(sdch_manager()->GetDictionarySet(target_gurl)); EXPECT_TRUE(sdch_manager()->GetDictionarySetByHash( target_gurl, server_hash, &problem_code)); EXPECT_EQ(SDCH_OK, problem_code); } // Confirm dispatch of notification. TEST_F(SdchManagerTest, SdchDictionaryUsed) { MockSdchObserver observer; sdch_manager()->AddObserver(&observer); EXPECT_EQ(0, observer.dictionary_used_notifications()); sdch_manager()->OnDictionaryUsed("xyzzy"); EXPECT_EQ(1, observer.dictionary_used_notifications()); EXPECT_EQ("xyzzy", observer.last_server_hash()); std::string dictionary_domain("x.y.z.google.com"); GURL target_gurl("http://" + dictionary_domain); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); std::string client_hash; std::string server_hash; SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash); EXPECT_TRUE(AddSdchDictionary(dictionary_text, target_gurl)); EXPECT_EQ(1, observer.dictionary_used_notifications()); EXPECT_TRUE(sdch_manager()->GetDictionarySet(target_gurl)); EXPECT_EQ(1, observer.dictionary_used_notifications()); sdch_manager()->RemoveObserver(&observer); EXPECT_EQ(1, observer.dictionary_used_notifications()); sdch_manager()->OnDictionaryUsed("plugh"); EXPECT_EQ(1, observer.dictionary_used_notifications()); } TEST_F(SdchManagerTest, AddRemoveNotifications) { MockSdchObserver observer; sdch_manager()->AddObserver(&observer); std::string dictionary_domain("x.y.z.google.com"); GURL target_gurl("http://" + dictionary_domain); std::string dictionary_text(NewSdchDictionary(dictionary_domain)); std::string client_hash; std::string server_hash; SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash); EXPECT_TRUE(AddSdchDictionary(dictionary_text, target_gurl)); EXPECT_EQ(1, observer.dictionary_added_notifications()); EXPECT_EQ(target_gurl, observer.last_dictionary_url()); EXPECT_EQ(server_hash, observer.last_server_hash()); EXPECT_EQ(SDCH_OK, sdch_manager()->RemoveSdchDictionary(server_hash)); EXPECT_EQ(1, observer.dictionary_removed_notifications()); EXPECT_EQ(server_hash, observer.last_server_hash()); sdch_manager()->RemoveObserver(&observer); } } // namespace net