Chromium Code Reviews| Index: net/sdch/sdch_owner_unittest.cc |
| diff --git a/net/sdch/sdch_owner_unittest.cc b/net/sdch/sdch_owner_unittest.cc |
| index e89533a361c4a45477c6affcf16a87ea2820031b..0a24ff61b4c8a0a7e30a441cb1c6ec7a1e8c566c 100644 |
| --- a/net/sdch/sdch_owner_unittest.cc |
| +++ b/net/sdch/sdch_owner_unittest.cc |
| @@ -3,9 +3,11 @@ |
| // found in the LICENSE file. |
| #include "base/memory/memory_pressure_listener.h" |
| +#include "base/prefs/testing_pref_store.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/simple_test_clock.h" |
| +#include "base/values.h" |
| #include "net/base/net_log.h" |
| #include "net/base/sdch_manager.h" |
| #include "net/sdch/sdch_owner.h" |
| @@ -14,8 +16,45 @@ |
| #include "net/url_request/url_request_error_job.h" |
| #include "net/url_request/url_request_job.h" |
| #include "net/url_request/url_request_job_factory.h" |
| +#include "net/url_request/url_request_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| +namespace { |
| + |
| +bool GetDictionaryForURL(TestingPrefStore* store, const GURL& url, |
| + std::string* hash, base::DictionaryValue** dict) { |
| + base::Value* sdch_val = nullptr; |
| + base::DictionaryValue* sdch_dict = nullptr; |
| + if (!store->GetMutableValue("SDCH", &sdch_val)) |
| + return false; |
| + if (!sdch_val->GetAsDictionary(&sdch_dict)) |
| + return false; |
| + |
| + base::DictionaryValue* dicts_dict = nullptr; |
| + if (!sdch_dict->GetDictionary("dictionaries", &dicts_dict)) |
| + return false; |
| + |
| + base::DictionaryValue::Iterator it(*dicts_dict); |
| + while (!it.IsAtEnd()) { |
| + const base::DictionaryValue* d = nullptr; |
| + if (!it.value().GetAsDictionary(&d)) |
| + continue; |
| + std::string dict_url; |
| + if (d->GetString("url", &dict_url) && dict_url == url.spec()) { |
| + if (hash) |
| + *hash = it.key(); |
| + if (dict) |
| + dicts_dict->GetDictionary(it.key(), dict); |
| + return true; |
| + } |
| + it.Advance(); |
| + } |
| + |
| + return false; |
| +} |
| + |
| +} // namespace |
| + |
| namespace net { |
| static const char generic_url[] = "http://www.example.com"; |
| @@ -120,6 +159,83 @@ class MockURLRequestJobFactory : public URLRequestJobFactory { |
| } |
| }; |
| +class MockSdchDictionaryFetcher : public SdchDictionaryFetcher { |
| + public: |
| + MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_) {} |
| + ~MockSdchDictionaryFetcher() {} |
| + |
| + struct PendingRequest { |
| + PendingRequest(const GURL& url, |
| + const OnDictionaryFetchedCallback& callback) |
| + : url_(url), callback_(callback) {} |
| + GURL url_; |
| + OnDictionaryFetchedCallback callback_; |
| + }; |
| + |
| + virtual void Schedule(const GURL& dictionary_url, |
| + const OnDictionaryFetchedCallback& callback) { |
| + if (!HasPendingRequest(dictionary_url)) |
| + requests_.push_back(PendingRequest(dictionary_url, callback)); |
| + } |
| + |
| + virtual void ScheduleReload(const GURL& dictionary_url, |
| + const OnDictionaryFetchedCallback& callback) { |
| + if (!HasPendingRequest(dictionary_url)) |
| + requests_.push_back(PendingRequest(dictionary_url, callback)); |
| + } |
| + |
| + virtual void Cancel() { |
| + requests_.clear(); |
| + } |
| + |
| + bool HasPendingRequest(const GURL& dictionary_url) { |
| + for (std::vector<PendingRequest>::iterator it = requests_.begin(); |
| + it != requests_.end(); ++it) { |
| + if (it->url_ == dictionary_url) |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + bool CompletePendingRequest(const GURL& dictionary_url, |
| + const std::string& dictionary_text, |
| + const BoundNetLog& net_log) { |
| + for (std::vector<PendingRequest>::iterator it = requests_.begin(); |
| + it != requests_.end(); ++it) { |
| + if (it->url_ == dictionary_url) { |
| + it->callback_.Run(dictionary_text, dictionary_url, net_log); |
| + requests_.erase(it); |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + private: |
| + TestURLRequestContext test_context_; |
| + std::vector<PendingRequest> requests_; |
| +}; |
| + |
| +// File testing infrastructure summary: |
| +// * NewSdchDictionary(): Creates a dictionary of a specific size. |
| +// * URLRequestErrorCountingJob: A URLRequestJob that returns an error |
| +// and counts the number of outstanding (started but not finished) |
| +// jobs, and calls a global callback when that number transitions to zero. |
| +// * MockURLRequestJobFactory: Factory to create the above jobs. Tracks |
| +// the number of jobs created. |
| +// * SdchOwnerTest: Interfaces |
| +// * Access manager, owner, and net log |
| +// * Return the number of jobs created in a time interval |
| +// * Return dictionary present in the manager |
| +// * Notify SdchOwner of an incoming dictionary (& wait until jobs clear) |
| +// * Attempt to add a dictionary and test for success. |
| +// Test patterns: |
| +// * Let the owner know about a Get-Dictionary header and test for |
| +// appropriate jobs being created. |
| +// * Let the owner know that a dictionary was successfully fetched |
| +// and test for appropriate outcome. |
| +// * Either of the above, having previously added dictionaries to create |
| +// a particular initial state. |
| class SdchOwnerTest : public testing::Test { |
| public: |
| static const size_t kMaxSizeForTesting = 1000 * 50; |
| @@ -128,6 +244,7 @@ class SdchOwnerTest : public testing::Test { |
| SdchOwnerTest() |
| : last_jobs_created_(error_jobs_created), |
| dictionary_creation_index_(0), |
| + pref_store_(new TestingPrefStore), |
| sdch_owner_(&sdch_manager_, &url_request_context_) { |
| // Any jobs created on this context will immediately error, |
| // which leaves the test in control of signals to SdchOwner. |
| @@ -141,6 +258,7 @@ class SdchOwnerTest : public testing::Test { |
| SdchManager& sdch_manager() { return sdch_manager_; } |
| SdchOwner& sdch_owner() { return sdch_owner_; } |
| BoundNetLog& bound_net_log() { return net_log_; } |
| + TestingPrefStore& pref_store() { return *(pref_store_.get()); } |
| int JobsRecentlyCreated() { |
| int result = error_jobs_created - last_jobs_created_; |
| @@ -157,10 +275,10 @@ class SdchOwnerTest : public testing::Test { |
| return !!set.get(); |
| } |
| - void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) { |
| - sdch_owner().OnGetDictionary(&sdch_manager_, request_url, dictionary_url); |
| + void WaitForNoJobs() { |
| if (outstanding_url_request_error_counting_jobs == 0) |
| return; |
| + |
| base::RunLoop run_loop; |
| base::Closure quit_closure(run_loop.QuitClosure()); |
| empty_url_request_jobs_callback = &quit_closure; |
| @@ -168,11 +286,18 @@ class SdchOwnerTest : public testing::Test { |
| empty_url_request_jobs_callback = NULL; |
| } |
| + void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) { |
| + sdch_owner().OnGetDictionary(&sdch_manager_, request_url, dictionary_url); |
| + WaitForNoJobs(); |
| + } |
| + |
| // Create a unique (by hash) dictionary of the given size, |
| // associate it with a unique URL, add it to the manager through |
| // SdchOwner::OnDictionaryFetched(), and return whether that |
| // addition was successful or not. |
| - bool CreateAndAddDictionary(size_t size, std::string* server_hash_p) { |
| + bool CreateAndAddDictionary(size_t size, |
| + std::string* server_hash_p, |
| + base::Time last_used_time) { |
| GURL dictionary_url( |
| base::StringPrintf("%s/d%d", generic_url, dictionary_creation_index_)); |
| std::string dictionary_text(NewSdchDictionary(size - 4)); |
| @@ -184,7 +309,8 @@ class SdchOwnerTest : public testing::Test { |
| if (DictionaryPresentInManager(server_hash)) |
| return false; |
| - sdch_owner().OnDictionaryFetched(dictionary_text, dictionary_url, net_log_); |
| + sdch_owner().OnDictionaryFetched(last_used_time, 0, dictionary_text, |
| + dictionary_url, net_log_); |
| if (server_hash_p) |
| *server_hash_p = server_hash; |
| return DictionaryPresentInManager(server_hash); |
| @@ -201,6 +327,7 @@ class SdchOwnerTest : public testing::Test { |
| MockURLRequestJobFactory job_factory_; |
| URLRequestContext url_request_context_; |
| SdchManager sdch_manager_; |
| + scoped_refptr<TestingPrefStore> pref_store_; |
| SdchOwner sdch_owner_; |
| DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest); |
| @@ -220,7 +347,8 @@ TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) { |
| // Fetch generated when half full. |
| GURL dict_url2(std::string(generic_url) + "/d2"); |
| std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2)); |
| - sdch_owner().OnDictionaryFetched(dictionary1, dict_url1, bound_net_log()); |
| + sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1, dict_url1, |
| + bound_net_log()); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| SignalGetDictionaryAndClearJobs(request_url, dict_url2); |
| EXPECT_EQ(1, JobsRecentlyCreated()); |
| @@ -229,7 +357,8 @@ TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) { |
| GURL dict_url3(std::string(generic_url) + "/d3"); |
| std::string dictionary2(NewSdchDictionary( |
| (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2))); |
| - sdch_owner().OnDictionaryFetched(dictionary2, dict_url2, bound_net_log()); |
| + sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2, dict_url2, |
| + bound_net_log()); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| SignalGetDictionaryAndClearJobs(request_url, dict_url3); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| @@ -241,16 +370,23 @@ TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) { |
| std::string client_hash; |
| std::string server_hash; |
| + // In the past, but still fresh for an unused dictionary. |
| + base::Time dictionary_last_used_time(base::Time::Now() - |
| + base::TimeDelta::FromMinutes(30)); |
| + |
| // Add successful when empty. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr, |
| + dictionary_last_used_time)); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| // Add successful when half full. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr, |
| + dictionary_last_used_time)); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| // Add unsuccessful when full. |
| - EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr)); |
| + EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr, |
| + dictionary_last_used_time)); |
| EXPECT_EQ(0, JobsRecentlyCreated()); |
| } |
| @@ -261,23 +397,19 @@ TEST_F(SdchOwnerTest, ConfirmAutoEviction) { |
| std::string server_hash_d3; |
| // Add two dictionaries, one recent, one more than a day in the past. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1)); |
| - |
| - scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| + base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23)); |
| + base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25)); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2, stale)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| - // The addition of a new dictionary should succeed, evicting the old one. |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now()); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3, fresh)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| @@ -292,26 +424,23 @@ TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) { |
| // Add dictionaries, one recent, two more than a day in the past that |
| // between them add up to the space needed. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1)); |
| + base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23)); |
| + base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh)); |
| - scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2)); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, stale)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, stale)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| - // The addition of a new dictionary should succeed, evicting the old one. |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now()); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - |
| std::string server_hash_d4; |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3)); |
| @@ -326,17 +455,18 @@ TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) { |
| // Add dictionaries, one recent, one two days in the past, and one |
| // four days in the past. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1)); |
| + base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23)); |
| + base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47)); |
| + base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71)); |
| + |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh)); |
| - scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, |
| + stale_newer)); |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, |
| + stale_older)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| @@ -344,12 +474,10 @@ TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) { |
| // The addition of a new dictionary should succeed, evicting only the |
| // oldest one. |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now()); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| std::string server_hash_d4; |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3)); |
| @@ -364,33 +492,31 @@ TEST_F(SdchOwnerTest, UseChangesEviction) { |
| // Add dictionaries, one recent, one two days in the past, and one |
| // four days in the past. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1)); |
| + base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23)); |
| + base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47)); |
| + base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71)); |
| - scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh)); |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, |
| + stale_newer)); |
| + |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, |
| + stale_older)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now()); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - |
| // Use the oldest dictionary. |
| sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3); |
| // The addition of a new dictionary should succeed, evicting only the |
| // newer stale one. |
| std::string server_hash_d4; |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| @@ -405,33 +531,31 @@ TEST_F(SdchOwnerTest, UsePreventsAddition) { |
| // Add dictionaries, one recent, one two days in the past, and one |
| // four days in the past. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1)); |
| + base::Time fresh(base::Time::Now() - base::TimeDelta::FromMinutes(30)); |
| + base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47)); |
| + base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71)); |
| + |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh)); |
| - scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, |
| + stale_newer)); |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4)); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, |
| + stale_older)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| - clock.reset(new base::SimpleTestClock); |
| - clock->SetNow(base::Time::Now()); |
| - sdch_owner().SetClockForTesting(clock.Pass()); |
| - |
| // Use the older dictionaries. |
| sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2); |
| sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3); |
| // The addition of a new dictionary should fail, not evicting anything. |
| std::string server_hash_d4; |
| - EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4)); |
| + EXPECT_FALSE( |
| + CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2)); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3)); |
| @@ -444,20 +568,20 @@ TEST_F(SdchOwnerTest, ClearReturnsSpace) { |
| std::string server_hash_d2; |
| // Take up all the space. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1)); |
| - |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1, |
| + base::Time::Now())); |
| // Addition should fail. |
| - EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2)); |
| - |
| + EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2, |
| + base::Time::Now())); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| - |
| sdch_manager().ClearData(); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| // Addition should now succeed. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, nullptr)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now())); |
| } |
| // Confirm memory pressure gets all the space back. |
| @@ -466,10 +590,12 @@ TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) { |
| std::string server_hash_d2; |
| // Take up all the space. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1)); |
| + EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1, |
| + base::Time::Now())); |
| // Addition should fail. |
| - EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2)); |
| + EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2, |
| + base::Time::Now())); |
| EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1)); |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| @@ -485,7 +611,195 @@ TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) { |
| EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2)); |
| // Addition should now succeed. |
| - EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, nullptr)); |
| + EXPECT_TRUE( |
| + CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now())); |
| +} |
| + |
| +class SdchOwnerPersistenceTest : public ::testing::Test /* SdchOwnerTest */ { |
|
Randy Smith (Not in Mondays)
2015/02/16 01:10:05
nit: I find this comment syntax confusing. Should
Elly Fong-Jones
2015/02/17 20:35:29
The comment was saying that it notionally was, but
|
| + public: |
| + SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) { |
| + pref_store_->SetInitializationCompleted(); |
| + } |
| + virtual ~SdchOwnerPersistenceTest() {} |
| + |
| + void ClearOwner() { |
| + owner_.reset(NULL); |
| + } |
| + |
| + void ResetOwner(bool delay) { |
| + // This has to be done first, since SdchOwner may be observing SdchManager, |
| + // and SdchManager can't be destroyed with a live observer. |
| + owner_.reset(NULL); |
| + manager_.reset(new SdchManager()); |
| + fetcher_ = new MockSdchDictionaryFetcher(); |
| + owner_.reset(new SdchOwner(manager_.get(), |
| + &url_request_context_)); |
| + owner_->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting); |
| + owner_->SetMinSpaceForDictionaryFetch( |
| + SdchOwnerTest::kMinFetchSpaceForTesting); |
| + owner_->SetFetcherForTesting(make_scoped_ptr(fetcher_)); |
| + if (!delay) |
| + owner_->EnablePersistentStorage(pref_store_.get()); |
| + } |
| + |
| + void InsertDictionaryForURL(const GURL& url, const std::string& nonce) { |
| + owner_->OnDictionaryFetched(base::Time::Now(), 1, |
| + CreateDictionary(url, nonce), |
| + url, net_log_); |
| + } |
| + |
| + bool CompleteLoadFromURL(const GURL& url, const std::string& nonce) { |
| + return fetcher_->CompletePendingRequest(url, CreateDictionary(url, nonce), |
| + net_log_); |
| + } |
| + |
| + std::string CreateDictionary(const GURL& url, const std::string& nonce) { |
| + std::string dict; |
| + dict.append("Domain: "); |
| + dict.append(url.host()); |
| + dict.append("\n\n"); |
| + dict.append(url.spec()); |
| + dict.append(nonce); |
| + return dict; |
| + } |
| + |
| + protected: |
| + BoundNetLog net_log_; |
| + scoped_refptr<TestingPrefStore> pref_store_; |
| + scoped_ptr<SdchManager> manager_; |
| + MockSdchDictionaryFetcher* fetcher_; |
| + scoped_ptr<SdchOwner> owner_; |
| + TestURLRequestContext url_request_context_; |
| +}; |
| + |
| +// Test an empty persistence store. |
| +TEST_F(SdchOwnerPersistenceTest, Empty) { |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| +} |
| + |
| +// Test a persistence store with an empty dictionary. |
| +TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDict) { |
| + pref_store_->SetValue("SDCH", new base::DictionaryValue()); |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| +} |
| + |
| +// Test a persistence store with a bad version number. |
| +TEST_F(SdchOwnerPersistenceTest, Persistent_BadVersion) { |
| + base::DictionaryValue* sdch_dict = new base::DictionaryValue(); |
| + sdch_dict->SetInteger("version", 2); |
| + pref_store_->SetValue("SDCH", sdch_dict); |
| + |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| +} |
| + |
| +// Test a persistence store with an empty dictionaries map. |
| +TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDictList) { |
| + base::DictionaryValue* sdch_dict = new base::DictionaryValue(); |
| + scoped_ptr<base::DictionaryValue> dicts(new base::DictionaryValue()); |
| + sdch_dict->SetInteger("version", 1); |
| + sdch_dict->Set("dictionaries", dicts.Pass()); |
| + pref_store_->SetValue("SDCH", sdch_dict); |
| + |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| +} |
| + |
| +TEST_F(SdchOwnerPersistenceTest, OneDict) { |
| + const GURL url("http://www.example.com/dict"); |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| + InsertDictionaryForURL(url, "0"); |
| + EXPECT_EQ(1, owner_->GetDictionaryCount()); |
| + |
| + ResetOwner(false); |
| + EXPECT_EQ(0, owner_->GetDictionaryCount()); |
| + EXPECT_TRUE(CompleteLoadFromURL(url, "0")); |
| + EXPECT_EQ(1, owner_->GetDictionaryCount()); |
| +} |
| + |
| +TEST_F(SdchOwnerPersistenceTest, TwoDicts) { |
| + const GURL url0("http://www.example.com/dict0"); |
| + const GURL url1("http://www.example.com/dict1"); |
| + ResetOwner(false); |
| + InsertDictionaryForURL(url0, "0"); |
| + InsertDictionaryForURL(url1, "1"); |
| + |
| + ResetOwner(false); |
| + EXPECT_TRUE(CompleteLoadFromURL(url0, "0")); |
| + EXPECT_TRUE(CompleteLoadFromURL(url1, "1")); |
| + EXPECT_EQ(2, owner_->GetDictionaryCount()); |
| + EXPECT_TRUE(owner_->HasDictionaryFrom(url0)); |
| + EXPECT_TRUE(owner_->HasDictionaryFrom(url1)); |
| +} |
| + |
| +TEST_F(SdchOwnerPersistenceTest, OneGoodDictOneBadDict) { |
| + const GURL url0("http://www.example.com/dict0"); |
| + const GURL url1("http://www.example.com/dict1"); |
| + ResetOwner(false); |
| + InsertDictionaryForURL(url0, "0"); |
| + InsertDictionaryForURL(url1, "1"); |
| + |
| + // Mutate the pref store a bit now. Clear the owner first, to ensure that the |
| + // SdchOwner doesn't observe these changes and object. The manual dictionary |
| + // manipulation is a bit icky. |
| + ClearOwner(); |
| + base::DictionaryValue* dict = nullptr; |
| + ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url1, nullptr, &dict)); |
| + dict->Remove("use_count", nullptr); |
| + |
| + ResetOwner(false); |
| + EXPECT_TRUE(CompleteLoadFromURL(url0, "0")); |
| + EXPECT_FALSE(CompleteLoadFromURL(url1, "1")); |
| + EXPECT_EQ(1, owner_->GetDictionaryCount()); |
| + EXPECT_TRUE(owner_->HasDictionaryFrom(url0)); |
| + EXPECT_FALSE(owner_->HasDictionaryFrom(url1)); |
| +} |
| + |
| +TEST_F(SdchOwnerPersistenceTest, UsingDictionaryUpdatesUseCount) { |
| + const GURL url("http://www.example.com/dict"); |
| + ResetOwner(false); |
| + InsertDictionaryForURL(url, "0"); |
| + |
| + std::string hash; |
| + int old_count; |
| + { |
| + ClearOwner(); |
| + base::DictionaryValue* dict = nullptr; |
| + ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, &hash, &dict)); |
| + ASSERT_TRUE(dict->GetInteger("use_count", &old_count)); |
| + } |
| + |
| + ResetOwner(false); |
| + ASSERT_TRUE(CompleteLoadFromURL(url, "0")); |
| + owner_->OnDictionaryUsed(manager_.get(), hash); |
| + |
| + int new_count; |
| + { |
| + ClearOwner(); |
| + base::DictionaryValue* dict = nullptr; |
| + ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, nullptr, &dict)); |
| + ASSERT_TRUE(dict->GetInteger("use_count", &new_count)); |
| + } |
| + |
| + EXPECT_EQ(old_count + 1, new_count); |
| +} |
| + |
| +TEST_F(SdchOwnerPersistenceTest, LoadingDictionaryMerges) { |
| + const GURL url0("http://www.example.com/dict0"); |
| + const GURL url1("http://www.example.com/dict1"); |
| + |
| + ResetOwner(false); |
| + InsertDictionaryForURL(url1, "1"); |
| + |
| + ResetOwner(true); |
| + InsertDictionaryForURL(url0, "0"); |
| + EXPECT_EQ(1, owner_->GetDictionaryCount()); |
| + owner_->EnablePersistentStorage(pref_store_.get()); |
| + ASSERT_TRUE(CompleteLoadFromURL(url1, "1")); |
| + EXPECT_EQ(2, owner_->GetDictionaryCount()); |
| } |
| } // namespace net |