Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / sdch / sdch_owner_unittest.cc
blob149041fd359be67dc2f6e0104788d4df8fc48067
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/location.h"
6 #include "base/memory/memory_pressure_listener.h"
7 #include "base/prefs/testing_pref_store.h"
8 #include "base/run_loop.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/test/histogram_tester.h"
12 #include "base/test/simple_test_clock.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "net/base/sdch_manager.h"
16 #include "net/log/net_log.h"
17 #include "net/sdch/sdch_owner.h"
18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_error_job.h"
21 #include "net/url_request/url_request_job.h"
22 #include "net/url_request/url_request_job_factory.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 namespace {
28 bool GetDictionaryForURL(TestingPrefStore* store,
29 const GURL& url,
30 std::string* hash,
31 base::DictionaryValue** dict) {
32 base::Value* sdch_val = nullptr;
33 base::DictionaryValue* sdch_dict = nullptr;
34 if (!store->GetMutableValue("SDCH", &sdch_val))
35 return false;
36 if (!sdch_val->GetAsDictionary(&sdch_dict))
37 return false;
39 base::DictionaryValue* dicts_dict = nullptr;
40 if (!sdch_dict->GetDictionary("dictionaries", &dicts_dict))
41 return false;
43 base::DictionaryValue::Iterator it(*dicts_dict);
44 while (!it.IsAtEnd()) {
45 const base::DictionaryValue* d = nullptr;
46 if (!it.value().GetAsDictionary(&d))
47 continue;
48 std::string dict_url;
49 if (d->GetString("url", &dict_url) && dict_url == url.spec()) {
50 if (hash)
51 *hash = it.key();
52 if (dict)
53 dicts_dict->GetDictionary(it.key(), dict);
54 return true;
56 it.Advance();
59 return false;
62 } // namespace
64 namespace net {
66 static const char generic_url[] = "http://www.example.com";
67 static const char generic_domain[] = "www.example.com";
69 static std::string NewSdchDictionary(size_t dictionary_size) {
70 std::string dictionary;
71 dictionary.append("Domain: ");
72 dictionary.append(generic_domain);
73 dictionary.append("\n");
74 dictionary.append("\n");
76 size_t original_dictionary_size = dictionary.size();
77 dictionary.resize(dictionary_size);
78 for (size_t i = original_dictionary_size; i < dictionary_size; ++i)
79 dictionary[i] = static_cast<char>((i % 127) + 1);
81 return dictionary;
84 int outstanding_url_request_error_counting_jobs = 0;
85 base::Closure* empty_url_request_jobs_callback = 0;
87 // Variation of URLRequestErrorJob to count number of outstanding
88 // instances and notify when that goes to zero.
89 class URLRequestErrorCountingJob : public URLRequestJob {
90 public:
91 URLRequestErrorCountingJob(URLRequest* request,
92 NetworkDelegate* network_delegate,
93 int error)
94 : URLRequestJob(request, network_delegate),
95 error_(error),
96 weak_factory_(this) {
97 ++outstanding_url_request_error_counting_jobs;
100 void Start() override {
101 base::ThreadTaskRunnerHandle::Get()->PostTask(
102 FROM_HERE, base::Bind(&URLRequestErrorCountingJob::StartAsync,
103 weak_factory_.GetWeakPtr()));
106 private:
107 ~URLRequestErrorCountingJob() override {
108 --outstanding_url_request_error_counting_jobs;
109 if (0 == outstanding_url_request_error_counting_jobs &&
110 empty_url_request_jobs_callback) {
111 empty_url_request_jobs_callback->Run();
115 void StartAsync() {
116 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, error_));
119 int error_;
121 base::WeakPtrFactory<URLRequestErrorCountingJob> weak_factory_;
123 DISALLOW_COPY_AND_ASSIGN(URLRequestErrorCountingJob);
126 static int error_jobs_created = 0;
128 class MockURLRequestJobFactory : public URLRequestJobFactory {
129 public:
130 MockURLRequestJobFactory() {}
132 ~MockURLRequestJobFactory() override {}
134 URLRequestJob* MaybeCreateJobWithProtocolHandler(
135 const std::string& scheme,
136 URLRequest* request,
137 NetworkDelegate* network_delegate) const override {
138 ++error_jobs_created;
139 return new URLRequestErrorCountingJob(request, network_delegate,
140 ERR_INTERNET_DISCONNECTED);
143 URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
144 NetworkDelegate* network_delegate,
145 const GURL& location) const override {
146 return nullptr;
149 URLRequestJob* MaybeInterceptResponse(
150 URLRequest* request,
151 NetworkDelegate* network_delegate) const override {
152 return nullptr;
155 bool IsHandledProtocol(const std::string& scheme) const override {
156 return scheme == "http";
159 bool IsHandledURL(const GURL& url) const override {
160 return url.SchemeIs("http");
163 bool IsSafeRedirectTarget(const GURL& location) const override {
164 return false;
168 class MockSdchDictionaryFetcher : public SdchDictionaryFetcher {
169 public:
170 MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_) {}
171 ~MockSdchDictionaryFetcher() {}
173 struct PendingRequest {
174 PendingRequest(const GURL& url,
175 const OnDictionaryFetchedCallback& callback)
176 : url_(url), callback_(callback) {}
177 GURL url_;
178 OnDictionaryFetchedCallback callback_;
181 virtual bool Schedule(const GURL& dictionary_url,
182 const OnDictionaryFetchedCallback& callback) {
183 if (!HasPendingRequest(dictionary_url)) {
184 requests_.push_back(PendingRequest(dictionary_url, callback));
185 return true;
187 return false;
190 virtual bool ScheduleReload(const GURL& dictionary_url,
191 const OnDictionaryFetchedCallback& callback) {
192 if (!HasPendingRequest(dictionary_url)) {
193 requests_.push_back(PendingRequest(dictionary_url, callback));
194 return true;
196 return false;
199 virtual void Cancel() {
200 requests_.clear();
203 bool HasPendingRequest(const GURL& dictionary_url) {
204 for (std::vector<PendingRequest>::iterator it = requests_.begin();
205 it != requests_.end(); ++it) {
206 if (it->url_ == dictionary_url)
207 return true;
209 return false;
212 bool CompletePendingRequest(const GURL& dictionary_url,
213 const std::string& dictionary_text,
214 const BoundNetLog& net_log,
215 bool was_from_cache) {
216 for (std::vector<PendingRequest>::iterator it = requests_.begin();
217 it != requests_.end(); ++it) {
218 if (it->url_ == dictionary_url) {
219 it->callback_.Run(dictionary_text, dictionary_url, net_log,
220 was_from_cache);
221 requests_.erase(it);
222 return true;
225 return false;
228 private:
229 TestURLRequestContext test_context_;
230 std::vector<PendingRequest> requests_;
231 DISALLOW_COPY_AND_ASSIGN(MockSdchDictionaryFetcher);
234 // File testing infrastructure summary:
235 // * NewSdchDictionary(): Creates a dictionary of a specific size.
236 // * URLRequestErrorCountingJob: A URLRequestJob that returns an error
237 // and counts the number of outstanding (started but not finished)
238 // jobs, and calls a global callback when that number transitions to zero.
239 // * MockURLRequestJobFactory: Factory to create the above jobs. Tracks
240 // the number of jobs created.
241 // * SdchOwnerTest: Interfaces
242 // * Access manager, owner, and net log
243 // * Return the number of jobs created in a time interval
244 // * Return dictionary present in the manager
245 // * Notify SdchOwner of an incoming dictionary (& wait until jobs clear)
246 // * Attempt to add a dictionary and test for success.
247 // Test patterns:
248 // * Let the owner know about a Get-Dictionary header and test for
249 // appropriate jobs being created.
250 // * Let the owner know that a dictionary was successfully fetched
251 // and test for appropriate outcome.
252 // * Either of the above, having previously added dictionaries to create
253 // a particular initial state.
254 class SdchOwnerTest : public testing::Test {
255 public:
256 static const size_t kMaxSizeForTesting = 1000 * 50;
257 static const size_t kMinFetchSpaceForTesting = 500;
259 SdchOwnerTest()
260 : last_jobs_created_(error_jobs_created),
261 dictionary_creation_index_(0),
262 pref_store_(new TestingPrefStore),
263 sdch_owner_(new SdchOwner(&sdch_manager_, &url_request_context_)) {
264 // Any jobs created on this context will immediately error,
265 // which leaves the test in control of signals to SdchOwner.
266 url_request_context_.set_job_factory(&job_factory_);
268 // Reduce sizes to reduce time for string operations.
269 sdch_owner_->SetMaxTotalDictionarySize(kMaxSizeForTesting);
270 sdch_owner_->SetMinSpaceForDictionaryFetch(kMinFetchSpaceForTesting);
273 SdchManager& sdch_manager() { return sdch_manager_; }
274 SdchOwner& sdch_owner() { return *(sdch_owner_.get()); }
275 BoundNetLog& bound_net_log() { return net_log_; }
276 TestingPrefStore& pref_store() { return *(pref_store_.get()); }
278 int JobsRecentlyCreated() {
279 int result = error_jobs_created - last_jobs_created_;
280 last_jobs_created_ = error_jobs_created;
281 return result;
284 bool DictionaryPresentInManager(const std::string& server_hash) {
285 // Presumes all tests use generic url.
286 SdchProblemCode tmp;
287 scoped_ptr<SdchManager::DictionarySet> set(
288 sdch_manager_.GetDictionarySetByHash(GURL(generic_url), server_hash,
289 &tmp));
290 return !!set.get();
293 void WaitForNoJobs() {
294 if (outstanding_url_request_error_counting_jobs == 0)
295 return;
297 base::RunLoop run_loop;
298 base::Closure quit_closure(run_loop.QuitClosure());
299 empty_url_request_jobs_callback = &quit_closure;
300 run_loop.Run();
301 empty_url_request_jobs_callback = NULL;
304 void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) {
305 sdch_owner().OnGetDictionary(request_url, dictionary_url);
306 WaitForNoJobs();
309 // Create a unique (by hash) dictionary of the given size,
310 // associate it with a unique URL, add it to the manager through
311 // SdchOwner::OnDictionaryFetched(), and return whether that
312 // addition was successful or not.
313 bool CreateAndAddDictionary(size_t size,
314 std::string* server_hash_p,
315 base::Time last_used_time) {
316 GURL dictionary_url(
317 base::StringPrintf("%s/d%d", generic_url, dictionary_creation_index_));
318 std::string dictionary_text(NewSdchDictionary(size - 4));
319 dictionary_text += base::StringPrintf("%04d", dictionary_creation_index_);
320 ++dictionary_creation_index_;
321 std::string client_hash;
322 std::string server_hash;
323 SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash);
325 if (DictionaryPresentInManager(server_hash))
326 return false;
327 sdch_owner().OnDictionaryFetched(last_used_time, 0, dictionary_text,
328 dictionary_url, net_log_, false);
329 if (server_hash_p)
330 *server_hash_p = server_hash;
331 return DictionaryPresentInManager(server_hash);
334 void ResetOwner() {
335 sdch_owner_.reset(new SdchOwner(&sdch_manager_, &url_request_context_));
338 private:
339 int last_jobs_created_;
340 BoundNetLog net_log_;
341 int dictionary_creation_index_;
343 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
344 // url_request_context_}, url_request_context_->job_factory_) require
345 // this order for correct destruction semantics.
346 MockURLRequestJobFactory job_factory_;
347 URLRequestContext url_request_context_;
348 SdchManager sdch_manager_;
349 scoped_refptr<TestingPrefStore> pref_store_;
350 scoped_ptr<SdchOwner> sdch_owner_;
352 DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest);
355 // Does OnGetDictionary result in a fetch when there's enough space, and not
356 // when there's not?
357 TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) {
358 GURL request_url(std::string(generic_url) + "/r1");
360 // Fetch generated when empty.
361 GURL dict_url1(std::string(generic_url) + "/d1");
362 EXPECT_EQ(0, JobsRecentlyCreated());
363 SignalGetDictionaryAndClearJobs(request_url, dict_url1);
364 EXPECT_EQ(1, JobsRecentlyCreated());
366 // Fetch generated when half full.
367 GURL dict_url2(std::string(generic_url) + "/d2");
368 std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2));
369 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1,
370 dict_url1, bound_net_log(), false);
371 EXPECT_EQ(0, JobsRecentlyCreated());
372 SignalGetDictionaryAndClearJobs(request_url, dict_url2);
373 EXPECT_EQ(1, JobsRecentlyCreated());
375 // Fetch not generated when close to completely full.
376 GURL dict_url3(std::string(generic_url) + "/d3");
377 std::string dictionary2(NewSdchDictionary(
378 (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2)));
379 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2,
380 dict_url2, bound_net_log(), false);
381 EXPECT_EQ(0, JobsRecentlyCreated());
382 SignalGetDictionaryAndClearJobs(request_url, dict_url3);
383 EXPECT_EQ(0, JobsRecentlyCreated());
386 // Make sure attempts to add dictionaries do what they should.
387 TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) {
388 GURL request_url(std::string(generic_url) + "/r1");
389 std::string client_hash;
390 std::string server_hash;
392 // In the past, but still fresh for an unused dictionary.
393 base::Time dictionary_last_used_time(base::Time::Now() -
394 base::TimeDelta::FromMinutes(30));
396 // Add successful when empty.
397 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
398 dictionary_last_used_time));
399 EXPECT_EQ(0, JobsRecentlyCreated());
401 // Add successful when half full.
402 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
403 dictionary_last_used_time));
404 EXPECT_EQ(0, JobsRecentlyCreated());
406 // Add unsuccessful when full.
407 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
408 dictionary_last_used_time));
409 EXPECT_EQ(0, JobsRecentlyCreated());
412 // Confirm auto-eviction happens if space is needed.
413 TEST_F(SdchOwnerTest, ConfirmAutoEviction) {
414 base::Time start_time = base::Time::Now();
415 std::string server_hash_d1;
416 std::string server_hash_d2;
417 std::string server_hash_d3;
419 base::SimpleTestClock* test_clock = new base::SimpleTestClock();
420 sdch_owner().SetClockForTesting(make_scoped_ptr(test_clock));
421 test_clock->SetNow(base::Time::Now());
423 // Add two dictionaries, one recent, one more than a day in the past.
424 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
425 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
427 EXPECT_TRUE(
428 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
429 EXPECT_TRUE(
430 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2, stale));
432 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
433 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
435 base::HistogramTester tester;
436 const base::TimeDelta synthetic_delta = base::TimeDelta::FromSeconds(5);
438 test_clock->Advance(synthetic_delta);
440 EXPECT_TRUE(
441 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3, fresh));
442 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
443 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
444 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
446 base::TimeDelta expected_proc_lifetime = synthetic_delta * 3 +
447 base::Time::Now() - start_time;
448 size_t expected_value_base = ((kMaxSizeForTesting / 2) *
449 synthetic_delta.InMilliseconds()) /
450 expected_proc_lifetime.InMilliseconds();
452 const char *kHistogram = "Sdch3.TimeWeightedMemoryUse";
453 tester.ExpectTotalCount(kHistogram, 0);
455 // Dictionary insertions and deletions:
456 // T = 0: insert d1 and d2
457 // T = 5: insert d3, which evicts d2
458 // T = 15: destroy SdchOwner, which evicts d1 and d3
459 // Therefore, d2's lifetime is synthetic_delta, d1's is synthetic_delta * 3,
460 // and d3's is synthetic_delta * 2. The expected_value_base variable is the
461 // base factor for d2's memory consumption, of which d1's and d3's are
462 // multiples.
463 test_clock->Advance(synthetic_delta * 2);
464 ResetOwner();
466 tester.ExpectTotalCount(kHistogram, 3);
467 tester.ExpectBucketCount(kHistogram, expected_value_base, 1);
468 tester.ExpectBucketCount(kHistogram, expected_value_base * 2, 1);
469 tester.ExpectBucketCount(kHistogram, expected_value_base * 3, 1);
472 // Confirm auto-eviction happens if space is needed, with a more complicated
473 // situation
474 TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) {
475 std::string server_hash_d1;
476 std::string server_hash_d2;
477 std::string server_hash_d3;
479 // Add dictionaries, one recent, two more than a day in the past that
480 // between them add up to the space needed.
481 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
482 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
483 EXPECT_TRUE(
484 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
486 EXPECT_TRUE(
487 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, stale));
488 EXPECT_TRUE(
489 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, stale));
491 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
492 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
493 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
495 std::string server_hash_d4;
496 EXPECT_TRUE(
497 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
498 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
499 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
500 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
501 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
504 // Confirm if only one dictionary needs to be evicted it's the oldest.
505 TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) {
506 std::string server_hash_d1;
507 std::string server_hash_d2;
508 std::string server_hash_d3;
510 // Add dictionaries, one recent, one two days in the past, and one
511 // four days in the past.
512 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
513 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
514 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
516 EXPECT_TRUE(
517 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
519 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
520 stale_newer));
522 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
523 stale_older));
525 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
526 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
527 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
529 // The addition of a new dictionary should succeed, evicting only the
530 // oldest one.
532 std::string server_hash_d4;
533 EXPECT_TRUE(
534 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
535 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
536 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
537 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
538 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
541 // Confirm using a dictionary changes eviction behavior properly.
542 TEST_F(SdchOwnerTest, UseChangesEviction) {
543 std::string server_hash_d1;
544 std::string server_hash_d2;
545 std::string server_hash_d3;
547 // Add dictionaries, one recent, one two days in the past, and one
548 // four days in the past.
549 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
550 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
551 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
553 EXPECT_TRUE(
554 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
556 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
557 stale_newer));
559 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
560 stale_older));
562 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
563 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
564 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
566 // Use the oldest dictionary.
567 sdch_owner().OnDictionaryUsed(server_hash_d3);
569 // The addition of a new dictionary should succeed, evicting only the
570 // newer stale one.
571 std::string server_hash_d4;
572 EXPECT_TRUE(
573 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
574 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
575 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
576 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
577 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
580 // Confirm using a dictionary can prevent the addition of a new dictionary.
581 TEST_F(SdchOwnerTest, UsePreventsAddition) {
582 std::string server_hash_d1;
583 std::string server_hash_d2;
584 std::string server_hash_d3;
586 // Add dictionaries, one recent, one two days in the past, and one
587 // four days in the past.
588 base::Time fresh(base::Time::Now() - base::TimeDelta::FromMinutes(30));
589 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
590 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
592 EXPECT_TRUE(
593 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
595 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
596 stale_newer));
598 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
599 stale_older));
601 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
602 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
603 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
605 // Use the older dictionaries.
606 sdch_owner().OnDictionaryUsed(server_hash_d2);
607 sdch_owner().OnDictionaryUsed(server_hash_d3);
609 // The addition of a new dictionary should fail, not evicting anything.
610 std::string server_hash_d4;
611 EXPECT_FALSE(
612 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
613 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
614 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
615 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
616 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4));
619 // Confirm clear gets all the space back.
620 TEST_F(SdchOwnerTest, ClearReturnsSpace) {
621 std::string server_hash_d1;
622 std::string server_hash_d2;
624 // Take up all the space.
625 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
626 base::Time::Now()));
627 // Addition should fail.
628 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
629 base::Time::Now()));
630 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
631 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
632 sdch_manager().ClearData();
633 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
634 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
636 // Addition should now succeed.
637 EXPECT_TRUE(
638 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
641 // Confirm memory pressure gets all the space back.
642 TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) {
643 std::string server_hash_d1;
644 std::string server_hash_d2;
646 // Take up all the space.
647 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
648 base::Time::Now()));
650 // Addition should fail.
651 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
652 base::Time::Now()));
654 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
655 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
657 base::MemoryPressureListener::NotifyMemoryPressure(
658 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
659 // The notification may have (implementation note: does :-}) use a PostTask,
660 // so we drain the local message queue. This should be safe (i.e. not have
661 // an inifinite number of messages) in a unit test.
662 base::RunLoop().RunUntilIdle();
664 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
665 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
667 // Addition should now succeed.
668 EXPECT_TRUE(
669 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
672 // Confirm that use of a pinned dictionary after its removal works properly.
673 TEST_F(SdchOwnerTest, PinRemoveUse) {
674 pref_store().SetInitializationCompleted();
675 sdch_owner().EnablePersistentStorage(&pref_store());
677 std::string server_hash_d1;
678 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1,
679 base::Time::Now()));
681 scoped_ptr<SdchManager::DictionarySet> return_set(
682 sdch_manager().GetDictionarySet(
683 GURL(std::string(generic_url) + "/x.html")));
684 ASSERT_TRUE(return_set.get());
685 EXPECT_TRUE(return_set->GetDictionaryText(server_hash_d1));
687 const base::Value* result = nullptr;
688 const base::DictionaryValue* dict_result = nullptr;
689 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
690 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
691 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
692 EXPECT_TRUE(dict_result->Get("dictionaries." + server_hash_d1, &result));
694 sdch_manager().ClearData();
696 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
697 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
698 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
699 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
701 scoped_ptr<SdchManager::DictionarySet> return_set2(
702 sdch_manager().GetDictionarySet(
703 GURL(std::string(generic_url) + "/x.html")));
704 EXPECT_FALSE(return_set2.get());
706 sdch_manager().OnDictionaryUsed(server_hash_d1);
708 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
709 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
710 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
711 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
714 class SdchOwnerPersistenceTest : public ::testing::Test {
715 public:
716 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
717 pref_store_->SetInitializationCompleted();
719 virtual ~SdchOwnerPersistenceTest() {}
721 void ClearOwner() {
722 owner_.reset(NULL);
725 void ResetOwner(bool delay) {
726 // This has to be done first, since SdchOwner may be observing SdchManager,
727 // and SdchManager can't be destroyed with a live observer.
728 owner_.reset(NULL);
729 manager_.reset(new SdchManager());
730 fetcher_ = new MockSdchDictionaryFetcher();
731 owner_.reset(new SdchOwner(manager_.get(),
732 &url_request_context_));
733 owner_->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting);
734 owner_->SetMinSpaceForDictionaryFetch(
735 SdchOwnerTest::kMinFetchSpaceForTesting);
736 owner_->SetFetcherForTesting(make_scoped_ptr(fetcher_));
737 if (!delay)
738 owner_->EnablePersistentStorage(pref_store_.get());
741 void InsertDictionaryForURL(const GURL& url, const std::string& nonce) {
742 owner_->OnDictionaryFetched(base::Time::Now(), 1,
743 CreateDictionary(url, nonce),
744 url, net_log_, false);
747 bool CompleteLoadFromURL(const GURL& url, const std::string& nonce,
748 bool was_from_cache) {
749 return fetcher_->CompletePendingRequest(url, CreateDictionary(url, nonce),
750 net_log_, was_from_cache);
753 std::string CreateDictionary(const GURL& url, const std::string& nonce) {
754 std::string dict;
755 dict.append("Domain: ");
756 dict.append(url.host());
757 dict.append("\n\n");
758 dict.append(url.spec());
759 dict.append(nonce);
760 return dict;
763 protected:
764 BoundNetLog net_log_;
765 scoped_refptr<TestingPrefStore> pref_store_;
766 scoped_ptr<SdchManager> manager_;
767 MockSdchDictionaryFetcher* fetcher_;
768 scoped_ptr<SdchOwner> owner_;
769 TestURLRequestContext url_request_context_;
772 // Test an empty persistence store.
773 TEST_F(SdchOwnerPersistenceTest, Empty) {
774 ResetOwner(false);
775 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
778 // Test a persistence store with an empty dictionary.
779 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDict) {
780 pref_store_->SetValue("SDCH", make_scoped_ptr(new base::DictionaryValue()),
781 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
782 ResetOwner(false);
783 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
786 // Test a persistence store with a bad version number.
787 TEST_F(SdchOwnerPersistenceTest, Persistent_BadVersion) {
788 scoped_ptr<base::DictionaryValue> sdch_dict(new base::DictionaryValue());
789 sdch_dict->SetInteger("version", 2);
790 pref_store_->SetValue("SDCH", sdch_dict.Pass(),
791 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
793 ResetOwner(false);
794 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
797 // Test a persistence store with an empty dictionaries map.
798 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDictList) {
799 scoped_ptr<base::DictionaryValue> sdch_dict(new base::DictionaryValue());
800 scoped_ptr<base::DictionaryValue> dicts(new base::DictionaryValue());
801 sdch_dict->SetInteger("version", 1);
802 sdch_dict->Set("dictionaries", dicts.Pass());
803 pref_store_->SetValue("SDCH", sdch_dict.Pass(),
804 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
806 ResetOwner(false);
807 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
810 TEST_F(SdchOwnerPersistenceTest, OneDict) {
811 const GURL url("http://www.example.com/dict");
812 ResetOwner(false);
813 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
814 InsertDictionaryForURL(url, "0");
815 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
817 ResetOwner(false);
818 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
819 EXPECT_TRUE(CompleteLoadFromURL(url, "0", true));
820 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
823 TEST_F(SdchOwnerPersistenceTest, TwoDicts) {
824 const GURL url0("http://www.example.com/dict0");
825 const GURL url1("http://www.example.com/dict1");
826 ResetOwner(false);
827 InsertDictionaryForURL(url0, "0");
828 InsertDictionaryForURL(url1, "1");
830 ResetOwner(false);
831 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
832 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", true));
833 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
834 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
835 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url1));
838 TEST_F(SdchOwnerPersistenceTest, OneGoodDictOneBadDict) {
839 const GURL url0("http://www.example.com/dict0");
840 const GURL url1("http://www.example.com/dict1");
841 ResetOwner(false);
842 InsertDictionaryForURL(url0, "0");
843 InsertDictionaryForURL(url1, "1");
845 // Mutate the pref store a bit now. Clear the owner first, to ensure that the
846 // SdchOwner doesn't observe these changes and object. The manual dictionary
847 // manipulation is a bit icky.
848 ClearOwner();
849 base::DictionaryValue* dict = nullptr;
850 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url1, nullptr, &dict));
851 dict->Remove("use_count", nullptr);
853 ResetOwner(false);
854 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
855 EXPECT_FALSE(CompleteLoadFromURL(url1, "1", true));
856 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
857 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
858 EXPECT_FALSE(owner_->HasDictionaryFromURLForTesting(url1));
861 TEST_F(SdchOwnerPersistenceTest, UsingDictionaryUpdatesUseCount) {
862 const GURL url("http://www.example.com/dict");
863 ResetOwner(false);
864 InsertDictionaryForURL(url, "0");
866 std::string hash;
867 int old_count;
869 ClearOwner();
870 base::DictionaryValue* dict = nullptr;
871 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, &hash, &dict));
872 ASSERT_TRUE(dict->GetInteger("use_count", &old_count));
875 ResetOwner(false);
876 ASSERT_TRUE(CompleteLoadFromURL(url, "0", true));
877 owner_->OnDictionaryUsed(hash);
879 int new_count;
881 ClearOwner();
882 base::DictionaryValue* dict = nullptr;
883 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, nullptr, &dict));
884 ASSERT_TRUE(dict->GetInteger("use_count", &new_count));
887 EXPECT_EQ(old_count + 1, new_count);
890 TEST_F(SdchOwnerPersistenceTest, LoadingDictionaryMerges) {
891 const GURL url0("http://www.example.com/dict0");
892 const GURL url1("http://www.example.com/dict1");
894 ResetOwner(false);
895 InsertDictionaryForURL(url1, "1");
897 ResetOwner(true);
898 InsertDictionaryForURL(url0, "0");
899 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
900 owner_->EnablePersistentStorage(pref_store_.get());
901 ASSERT_TRUE(CompleteLoadFromURL(url1, "1", true));
902 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
905 TEST_F(SdchOwnerPersistenceTest, PersistenceMetrics) {
906 const GURL url0("http://www.example.com/dict0");
907 const GURL url1("http://www.example.com/dict1");
908 ResetOwner(false);
910 InsertDictionaryForURL(url0, "0");
911 InsertDictionaryForURL(url1, "1");
913 ResetOwner(false);
915 base::HistogramTester tester;
917 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
918 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", false));
920 tester.ExpectTotalCount("Sdch3.NetworkBytesSpent", 1);
921 tester.ExpectUniqueSample("Sdch3.NetworkBytesSpent",
922 CreateDictionary(url1, "1").size(), 1);
925 } // namespace net