Make Ctrl + T from apps focus the new tab's omnibox on Linux and ChromeOS.
[chromium-blink-merge.git] / net / sdch / sdch_owner_unittest.cc
blobe1f778f41e52dc789b89603b3e96d6a7646bc4e9
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/memory/memory_pressure_listener.h"
6 #include "base/prefs/testing_pref_store.h"
7 #include "base/run_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/test/histogram_tester.h"
10 #include "base/test/simple_test_clock.h"
11 #include "base/values.h"
12 #include "net/base/sdch_manager.h"
13 #include "net/log/net_log.h"
14 #include "net/sdch/sdch_owner.h"
15 #include "net/url_request/url_request.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_error_job.h"
18 #include "net/url_request/url_request_job.h"
19 #include "net/url_request/url_request_job_factory.h"
20 #include "net/url_request/url_request_test_util.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 namespace {
25 bool GetDictionaryForURL(TestingPrefStore* store,
26 const GURL& url,
27 std::string* hash,
28 base::DictionaryValue** dict) {
29 base::Value* sdch_val = nullptr;
30 base::DictionaryValue* sdch_dict = nullptr;
31 if (!store->GetMutableValue("SDCH", &sdch_val))
32 return false;
33 if (!sdch_val->GetAsDictionary(&sdch_dict))
34 return false;
36 base::DictionaryValue* dicts_dict = nullptr;
37 if (!sdch_dict->GetDictionary("dictionaries", &dicts_dict))
38 return false;
40 base::DictionaryValue::Iterator it(*dicts_dict);
41 while (!it.IsAtEnd()) {
42 const base::DictionaryValue* d = nullptr;
43 if (!it.value().GetAsDictionary(&d))
44 continue;
45 std::string dict_url;
46 if (d->GetString("url", &dict_url) && dict_url == url.spec()) {
47 if (hash)
48 *hash = it.key();
49 if (dict)
50 dicts_dict->GetDictionary(it.key(), dict);
51 return true;
53 it.Advance();
56 return false;
59 } // namespace
61 namespace net {
63 static const char generic_url[] = "http://www.example.com";
64 static const char generic_domain[] = "www.example.com";
66 static std::string NewSdchDictionary(size_t dictionary_size) {
67 std::string dictionary;
68 dictionary.append("Domain: ");
69 dictionary.append(generic_domain);
70 dictionary.append("\n");
71 dictionary.append("\n");
73 size_t original_dictionary_size = dictionary.size();
74 dictionary.resize(dictionary_size);
75 for (size_t i = original_dictionary_size; i < dictionary_size; ++i)
76 dictionary[i] = static_cast<char>((i % 127) + 1);
78 return dictionary;
81 int outstanding_url_request_error_counting_jobs = 0;
82 base::Closure* empty_url_request_jobs_callback = 0;
84 // Variation of URLRequestErrorJob to count number of outstanding
85 // instances and notify when that goes to zero.
86 class URLRequestErrorCountingJob : public URLRequestJob {
87 public:
88 URLRequestErrorCountingJob(URLRequest* request,
89 NetworkDelegate* network_delegate,
90 int error)
91 : URLRequestJob(request, network_delegate),
92 error_(error),
93 weak_factory_(this) {
94 ++outstanding_url_request_error_counting_jobs;
97 void Start() override {
98 base::MessageLoop::current()->PostTask(
99 FROM_HERE, base::Bind(&URLRequestErrorCountingJob::StartAsync,
100 weak_factory_.GetWeakPtr()));
103 private:
104 ~URLRequestErrorCountingJob() override {
105 --outstanding_url_request_error_counting_jobs;
106 if (0 == outstanding_url_request_error_counting_jobs &&
107 empty_url_request_jobs_callback) {
108 empty_url_request_jobs_callback->Run();
112 void StartAsync() {
113 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, error_));
116 int error_;
118 base::WeakPtrFactory<URLRequestErrorCountingJob> weak_factory_;
120 DISALLOW_COPY_AND_ASSIGN(URLRequestErrorCountingJob);
123 static int error_jobs_created = 0;
125 class MockURLRequestJobFactory : public URLRequestJobFactory {
126 public:
127 MockURLRequestJobFactory() {}
129 ~MockURLRequestJobFactory() override {}
131 URLRequestJob* MaybeCreateJobWithProtocolHandler(
132 const std::string& scheme,
133 URLRequest* request,
134 NetworkDelegate* network_delegate) const override {
135 ++error_jobs_created;
136 return new URLRequestErrorCountingJob(request, network_delegate,
137 ERR_INTERNET_DISCONNECTED);
140 URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
141 NetworkDelegate* network_delegate,
142 const GURL& location) const override {
143 return nullptr;
146 URLRequestJob* MaybeInterceptResponse(
147 URLRequest* request,
148 NetworkDelegate* network_delegate) const override {
149 return nullptr;
152 bool IsHandledProtocol(const std::string& scheme) const override {
153 return scheme == "http";
156 bool IsHandledURL(const GURL& url) const override {
157 return url.SchemeIs("http");
160 bool IsSafeRedirectTarget(const GURL& location) const override {
161 return false;
165 class MockSdchDictionaryFetcher : public SdchDictionaryFetcher {
166 public:
167 MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_) {}
168 ~MockSdchDictionaryFetcher() {}
170 struct PendingRequest {
171 PendingRequest(const GURL& url,
172 const OnDictionaryFetchedCallback& callback)
173 : url_(url), callback_(callback) {}
174 GURL url_;
175 OnDictionaryFetchedCallback callback_;
178 virtual bool Schedule(const GURL& dictionary_url,
179 const OnDictionaryFetchedCallback& callback) {
180 if (!HasPendingRequest(dictionary_url)) {
181 requests_.push_back(PendingRequest(dictionary_url, callback));
182 return true;
184 return false;
187 virtual bool ScheduleReload(const GURL& dictionary_url,
188 const OnDictionaryFetchedCallback& callback) {
189 if (!HasPendingRequest(dictionary_url)) {
190 requests_.push_back(PendingRequest(dictionary_url, callback));
191 return true;
193 return false;
196 virtual void Cancel() {
197 requests_.clear();
200 bool HasPendingRequest(const GURL& dictionary_url) {
201 for (std::vector<PendingRequest>::iterator it = requests_.begin();
202 it != requests_.end(); ++it) {
203 if (it->url_ == dictionary_url)
204 return true;
206 return false;
209 bool CompletePendingRequest(const GURL& dictionary_url,
210 const std::string& dictionary_text,
211 const BoundNetLog& net_log,
212 bool was_from_cache) {
213 for (std::vector<PendingRequest>::iterator it = requests_.begin();
214 it != requests_.end(); ++it) {
215 if (it->url_ == dictionary_url) {
216 it->callback_.Run(dictionary_text, dictionary_url, net_log,
217 was_from_cache);
218 requests_.erase(it);
219 return true;
222 return false;
225 private:
226 TestURLRequestContext test_context_;
227 std::vector<PendingRequest> requests_;
228 DISALLOW_COPY_AND_ASSIGN(MockSdchDictionaryFetcher);
231 // File testing infrastructure summary:
232 // * NewSdchDictionary(): Creates a dictionary of a specific size.
233 // * URLRequestErrorCountingJob: A URLRequestJob that returns an error
234 // and counts the number of outstanding (started but not finished)
235 // jobs, and calls a global callback when that number transitions to zero.
236 // * MockURLRequestJobFactory: Factory to create the above jobs. Tracks
237 // the number of jobs created.
238 // * SdchOwnerTest: Interfaces
239 // * Access manager, owner, and net log
240 // * Return the number of jobs created in a time interval
241 // * Return dictionary present in the manager
242 // * Notify SdchOwner of an incoming dictionary (& wait until jobs clear)
243 // * Attempt to add a dictionary and test for success.
244 // Test patterns:
245 // * Let the owner know about a Get-Dictionary header and test for
246 // appropriate jobs being created.
247 // * Let the owner know that a dictionary was successfully fetched
248 // and test for appropriate outcome.
249 // * Either of the above, having previously added dictionaries to create
250 // a particular initial state.
251 class SdchOwnerTest : public testing::Test {
252 public:
253 static const size_t kMaxSizeForTesting = 1000 * 50;
254 static const size_t kMinFetchSpaceForTesting = 500;
256 SdchOwnerTest()
257 : last_jobs_created_(error_jobs_created),
258 dictionary_creation_index_(0),
259 pref_store_(new TestingPrefStore),
260 sdch_owner_(new SdchOwner(&sdch_manager_, &url_request_context_)) {
261 // Any jobs created on this context will immediately error,
262 // which leaves the test in control of signals to SdchOwner.
263 url_request_context_.set_job_factory(&job_factory_);
265 // Reduce sizes to reduce time for string operations.
266 sdch_owner_->SetMaxTotalDictionarySize(kMaxSizeForTesting);
267 sdch_owner_->SetMinSpaceForDictionaryFetch(kMinFetchSpaceForTesting);
270 SdchManager& sdch_manager() { return sdch_manager_; }
271 SdchOwner& sdch_owner() { return *(sdch_owner_.get()); }
272 BoundNetLog& bound_net_log() { return net_log_; }
273 TestingPrefStore& pref_store() { return *(pref_store_.get()); }
275 int JobsRecentlyCreated() {
276 int result = error_jobs_created - last_jobs_created_;
277 last_jobs_created_ = error_jobs_created;
278 return result;
281 bool DictionaryPresentInManager(const std::string& server_hash) {
282 // Presumes all tests use generic url.
283 SdchProblemCode tmp;
284 scoped_ptr<SdchManager::DictionarySet> set(
285 sdch_manager_.GetDictionarySetByHash(GURL(generic_url), server_hash,
286 &tmp));
287 return !!set.get();
290 void WaitForNoJobs() {
291 if (outstanding_url_request_error_counting_jobs == 0)
292 return;
294 base::RunLoop run_loop;
295 base::Closure quit_closure(run_loop.QuitClosure());
296 empty_url_request_jobs_callback = &quit_closure;
297 run_loop.Run();
298 empty_url_request_jobs_callback = NULL;
301 void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) {
302 sdch_owner().OnGetDictionary(&sdch_manager_, request_url, dictionary_url);
303 WaitForNoJobs();
306 // Create a unique (by hash) dictionary of the given size,
307 // associate it with a unique URL, add it to the manager through
308 // SdchOwner::OnDictionaryFetched(), and return whether that
309 // addition was successful or not.
310 bool CreateAndAddDictionary(size_t size,
311 std::string* server_hash_p,
312 base::Time last_used_time) {
313 GURL dictionary_url(
314 base::StringPrintf("%s/d%d", generic_url, dictionary_creation_index_));
315 std::string dictionary_text(NewSdchDictionary(size - 4));
316 dictionary_text += base::StringPrintf("%04d", dictionary_creation_index_);
317 ++dictionary_creation_index_;
318 std::string client_hash;
319 std::string server_hash;
320 SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash);
322 if (DictionaryPresentInManager(server_hash))
323 return false;
324 sdch_owner().OnDictionaryFetched(last_used_time, 0, dictionary_text,
325 dictionary_url, net_log_, false);
326 if (server_hash_p)
327 *server_hash_p = server_hash;
328 return DictionaryPresentInManager(server_hash);
331 void ResetOwner() {
332 sdch_owner_.reset(new SdchOwner(&sdch_manager_, &url_request_context_));
335 private:
336 int last_jobs_created_;
337 BoundNetLog net_log_;
338 int dictionary_creation_index_;
340 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
341 // url_request_context_}, url_request_context_->job_factory_) require
342 // this order for correct destruction semantics.
343 MockURLRequestJobFactory job_factory_;
344 URLRequestContext url_request_context_;
345 SdchManager sdch_manager_;
346 scoped_refptr<TestingPrefStore> pref_store_;
347 scoped_ptr<SdchOwner> sdch_owner_;
349 DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest);
352 // Does OnGetDictionary result in a fetch when there's enough space, and not
353 // when there's not?
354 TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) {
355 GURL request_url(std::string(generic_url) + "/r1");
357 // Fetch generated when empty.
358 GURL dict_url1(std::string(generic_url) + "/d1");
359 EXPECT_EQ(0, JobsRecentlyCreated());
360 SignalGetDictionaryAndClearJobs(request_url, dict_url1);
361 EXPECT_EQ(1, JobsRecentlyCreated());
363 // Fetch generated when half full.
364 GURL dict_url2(std::string(generic_url) + "/d2");
365 std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2));
366 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1,
367 dict_url1, bound_net_log(), false);
368 EXPECT_EQ(0, JobsRecentlyCreated());
369 SignalGetDictionaryAndClearJobs(request_url, dict_url2);
370 EXPECT_EQ(1, JobsRecentlyCreated());
372 // Fetch not generated when close to completely full.
373 GURL dict_url3(std::string(generic_url) + "/d3");
374 std::string dictionary2(NewSdchDictionary(
375 (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2)));
376 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2,
377 dict_url2, bound_net_log(), false);
378 EXPECT_EQ(0, JobsRecentlyCreated());
379 SignalGetDictionaryAndClearJobs(request_url, dict_url3);
380 EXPECT_EQ(0, JobsRecentlyCreated());
383 // Make sure attempts to add dictionaries do what they should.
384 TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) {
385 GURL request_url(std::string(generic_url) + "/r1");
386 std::string client_hash;
387 std::string server_hash;
389 // In the past, but still fresh for an unused dictionary.
390 base::Time dictionary_last_used_time(base::Time::Now() -
391 base::TimeDelta::FromMinutes(30));
393 // Add successful when empty.
394 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
395 dictionary_last_used_time));
396 EXPECT_EQ(0, JobsRecentlyCreated());
398 // Add successful when half full.
399 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
400 dictionary_last_used_time));
401 EXPECT_EQ(0, JobsRecentlyCreated());
403 // Add unsuccessful when full.
404 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
405 dictionary_last_used_time));
406 EXPECT_EQ(0, JobsRecentlyCreated());
409 // Confirm auto-eviction happens if space is needed.
410 TEST_F(SdchOwnerTest, ConfirmAutoEviction) {
411 base::Time start_time = base::Time::Now();
412 std::string server_hash_d1;
413 std::string server_hash_d2;
414 std::string server_hash_d3;
416 base::SimpleTestClock* test_clock = new base::SimpleTestClock();
417 sdch_owner().SetClockForTesting(make_scoped_ptr(test_clock));
418 test_clock->SetNow(base::Time::Now());
420 // Add two dictionaries, one recent, one more than a day in the past.
421 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
422 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
424 EXPECT_TRUE(
425 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
426 EXPECT_TRUE(
427 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2, stale));
429 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
430 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
432 base::HistogramTester tester;
433 const base::TimeDelta synthetic_delta = base::TimeDelta::FromSeconds(5);
435 test_clock->Advance(synthetic_delta);
437 EXPECT_TRUE(
438 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3, fresh));
439 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
440 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
441 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
443 base::TimeDelta expected_proc_lifetime = synthetic_delta * 3 +
444 base::Time::Now() - start_time;
445 size_t expected_value_base = ((kMaxSizeForTesting / 2) *
446 synthetic_delta.InMilliseconds()) /
447 expected_proc_lifetime.InMilliseconds();
449 const char *kHistogram = "Sdch3.TimeWeightedMemoryUse";
450 tester.ExpectTotalCount(kHistogram, 0);
452 // Dictionary insertions and deletions:
453 // T = 0: insert d1 and d2
454 // T = 5: insert d3, which evicts d2
455 // T = 15: destroy SdchOwner, which evicts d1 and d3
456 // Therefore, d2's lifetime is synthetic_delta, d1's is synthetic_delta * 3,
457 // and d3's is synthetic_delta * 2. The expected_value_base variable is the
458 // base factor for d2's memory consumption, of which d1's and d3's are
459 // multiples.
460 test_clock->Advance(synthetic_delta * 2);
461 ResetOwner();
463 tester.ExpectTotalCount(kHistogram, 3);
464 tester.ExpectBucketCount(kHistogram, expected_value_base, 1);
465 tester.ExpectBucketCount(kHistogram, expected_value_base * 2, 1);
466 tester.ExpectBucketCount(kHistogram, expected_value_base * 3, 1);
469 // Confirm auto-eviction happens if space is needed, with a more complicated
470 // situation
471 TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) {
472 std::string server_hash_d1;
473 std::string server_hash_d2;
474 std::string server_hash_d3;
476 // Add dictionaries, one recent, two more than a day in the past that
477 // between them add up to the space needed.
478 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
479 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
480 EXPECT_TRUE(
481 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
483 EXPECT_TRUE(
484 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, stale));
485 EXPECT_TRUE(
486 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, stale));
488 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
489 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
490 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
492 std::string server_hash_d4;
493 EXPECT_TRUE(
494 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
495 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
496 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
497 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
498 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
501 // Confirm if only one dictionary needs to be evicted it's the oldest.
502 TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) {
503 std::string server_hash_d1;
504 std::string server_hash_d2;
505 std::string server_hash_d3;
507 // Add dictionaries, one recent, one two days in the past, and one
508 // four days in the past.
509 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
510 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
511 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
513 EXPECT_TRUE(
514 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
516 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
517 stale_newer));
519 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
520 stale_older));
522 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
523 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
524 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
526 // The addition of a new dictionary should succeed, evicting only the
527 // oldest one.
529 std::string server_hash_d4;
530 EXPECT_TRUE(
531 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
532 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
533 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
534 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
535 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
538 // Confirm using a dictionary changes eviction behavior properly.
539 TEST_F(SdchOwnerTest, UseChangesEviction) {
540 std::string server_hash_d1;
541 std::string server_hash_d2;
542 std::string server_hash_d3;
544 // Add dictionaries, one recent, one two days in the past, and one
545 // four days in the past.
546 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
547 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
548 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
550 EXPECT_TRUE(
551 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
553 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
554 stale_newer));
556 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
557 stale_older));
559 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
560 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
561 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
563 // Use the oldest dictionary.
564 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
566 // The addition of a new dictionary should succeed, evicting only the
567 // newer stale one.
568 std::string server_hash_d4;
569 EXPECT_TRUE(
570 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
571 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
572 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
573 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
574 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
577 // Confirm using a dictionary can prevent the addition of a new dictionary.
578 TEST_F(SdchOwnerTest, UsePreventsAddition) {
579 std::string server_hash_d1;
580 std::string server_hash_d2;
581 std::string server_hash_d3;
583 // Add dictionaries, one recent, one two days in the past, and one
584 // four days in the past.
585 base::Time fresh(base::Time::Now() - base::TimeDelta::FromMinutes(30));
586 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
587 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
589 EXPECT_TRUE(
590 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
592 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
593 stale_newer));
595 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
596 stale_older));
598 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
599 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
600 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
602 // Use the older dictionaries.
603 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2);
604 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
606 // The addition of a new dictionary should fail, not evicting anything.
607 std::string server_hash_d4;
608 EXPECT_FALSE(
609 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
610 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
611 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
612 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
613 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4));
616 // Confirm clear gets all the space back.
617 TEST_F(SdchOwnerTest, ClearReturnsSpace) {
618 std::string server_hash_d1;
619 std::string server_hash_d2;
621 // Take up all the space.
622 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
623 base::Time::Now()));
624 // Addition should fail.
625 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
626 base::Time::Now()));
627 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
628 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
629 sdch_manager().ClearData();
630 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
631 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
633 // Addition should now succeed.
634 EXPECT_TRUE(
635 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
638 // Confirm memory pressure gets all the space back.
639 TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) {
640 std::string server_hash_d1;
641 std::string server_hash_d2;
643 // Take up all the space.
644 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
645 base::Time::Now()));
647 // Addition should fail.
648 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
649 base::Time::Now()));
651 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
652 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
654 base::MemoryPressureListener::NotifyMemoryPressure(
655 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
656 // The notification may have (implementation note: does :-}) use a PostTask,
657 // so we drain the local message queue. This should be safe (i.e. not have
658 // an inifinite number of messages) in a unit test.
659 base::RunLoop().RunUntilIdle();
661 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
662 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
664 // Addition should now succeed.
665 EXPECT_TRUE(
666 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
669 // Confirm that use of a pinned dictionary after its removal works properly.
670 TEST_F(SdchOwnerTest, PinRemoveUse) {
671 pref_store().SetInitializationCompleted();
672 sdch_owner().EnablePersistentStorage(&pref_store());
674 std::string server_hash_d1;
675 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1,
676 base::Time::Now()));
678 scoped_ptr<SdchManager::DictionarySet> return_set(
679 sdch_manager().GetDictionarySet(
680 GURL(std::string(generic_url) + "/x.html")));
681 ASSERT_TRUE(return_set.get());
682 EXPECT_TRUE(return_set->GetDictionaryText(server_hash_d1));
684 const base::Value* result = nullptr;
685 const base::DictionaryValue* dict_result = nullptr;
686 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
687 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
688 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
689 EXPECT_TRUE(dict_result->Get("dictionaries." + server_hash_d1, &result));
691 sdch_manager().ClearData();
693 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
694 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
695 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
696 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
698 scoped_ptr<SdchManager::DictionarySet> return_set2(
699 sdch_manager().GetDictionarySet(
700 GURL(std::string(generic_url) + "/x.html")));
701 EXPECT_FALSE(return_set2.get());
703 sdch_manager().OnDictionaryUsed(server_hash_d1);
705 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
706 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
707 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
708 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
711 class SdchOwnerPersistenceTest : public ::testing::Test {
712 public:
713 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
714 pref_store_->SetInitializationCompleted();
716 virtual ~SdchOwnerPersistenceTest() {}
718 void ClearOwner() {
719 owner_.reset(NULL);
722 void ResetOwner(bool delay) {
723 // This has to be done first, since SdchOwner may be observing SdchManager,
724 // and SdchManager can't be destroyed with a live observer.
725 owner_.reset(NULL);
726 manager_.reset(new SdchManager());
727 fetcher_ = new MockSdchDictionaryFetcher();
728 owner_.reset(new SdchOwner(manager_.get(),
729 &url_request_context_));
730 owner_->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting);
731 owner_->SetMinSpaceForDictionaryFetch(
732 SdchOwnerTest::kMinFetchSpaceForTesting);
733 owner_->SetFetcherForTesting(make_scoped_ptr(fetcher_));
734 if (!delay)
735 owner_->EnablePersistentStorage(pref_store_.get());
738 void InsertDictionaryForURL(const GURL& url, const std::string& nonce) {
739 owner_->OnDictionaryFetched(base::Time::Now(), 1,
740 CreateDictionary(url, nonce),
741 url, net_log_, false);
744 bool CompleteLoadFromURL(const GURL& url, const std::string& nonce,
745 bool was_from_cache) {
746 return fetcher_->CompletePendingRequest(url, CreateDictionary(url, nonce),
747 net_log_, was_from_cache);
750 std::string CreateDictionary(const GURL& url, const std::string& nonce) {
751 std::string dict;
752 dict.append("Domain: ");
753 dict.append(url.host());
754 dict.append("\n\n");
755 dict.append(url.spec());
756 dict.append(nonce);
757 return dict;
760 protected:
761 BoundNetLog net_log_;
762 scoped_refptr<TestingPrefStore> pref_store_;
763 scoped_ptr<SdchManager> manager_;
764 MockSdchDictionaryFetcher* fetcher_;
765 scoped_ptr<SdchOwner> owner_;
766 TestURLRequestContext url_request_context_;
769 // Test an empty persistence store.
770 TEST_F(SdchOwnerPersistenceTest, Empty) {
771 ResetOwner(false);
772 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
775 // Test a persistence store with an empty dictionary.
776 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDict) {
777 pref_store_->SetValue("SDCH", new base::DictionaryValue());
778 ResetOwner(false);
779 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
782 // Test a persistence store with a bad version number.
783 TEST_F(SdchOwnerPersistenceTest, Persistent_BadVersion) {
784 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
785 sdch_dict->SetInteger("version", 2);
786 pref_store_->SetValue("SDCH", sdch_dict);
788 ResetOwner(false);
789 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
792 // Test a persistence store with an empty dictionaries map.
793 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDictList) {
794 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
795 scoped_ptr<base::DictionaryValue> dicts(new base::DictionaryValue());
796 sdch_dict->SetInteger("version", 1);
797 sdch_dict->Set("dictionaries", dicts.Pass());
798 pref_store_->SetValue("SDCH", sdch_dict);
800 ResetOwner(false);
801 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
804 TEST_F(SdchOwnerPersistenceTest, OneDict) {
805 const GURL url("http://www.example.com/dict");
806 ResetOwner(false);
807 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
808 InsertDictionaryForURL(url, "0");
809 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
811 ResetOwner(false);
812 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
813 EXPECT_TRUE(CompleteLoadFromURL(url, "0", true));
814 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
817 TEST_F(SdchOwnerPersistenceTest, TwoDicts) {
818 const GURL url0("http://www.example.com/dict0");
819 const GURL url1("http://www.example.com/dict1");
820 ResetOwner(false);
821 InsertDictionaryForURL(url0, "0");
822 InsertDictionaryForURL(url1, "1");
824 ResetOwner(false);
825 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
826 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", true));
827 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
828 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
829 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url1));
832 TEST_F(SdchOwnerPersistenceTest, OneGoodDictOneBadDict) {
833 const GURL url0("http://www.example.com/dict0");
834 const GURL url1("http://www.example.com/dict1");
835 ResetOwner(false);
836 InsertDictionaryForURL(url0, "0");
837 InsertDictionaryForURL(url1, "1");
839 // Mutate the pref store a bit now. Clear the owner first, to ensure that the
840 // SdchOwner doesn't observe these changes and object. The manual dictionary
841 // manipulation is a bit icky.
842 ClearOwner();
843 base::DictionaryValue* dict = nullptr;
844 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url1, nullptr, &dict));
845 dict->Remove("use_count", nullptr);
847 ResetOwner(false);
848 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
849 EXPECT_FALSE(CompleteLoadFromURL(url1, "1", true));
850 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
851 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
852 EXPECT_FALSE(owner_->HasDictionaryFromURLForTesting(url1));
855 TEST_F(SdchOwnerPersistenceTest, UsingDictionaryUpdatesUseCount) {
856 const GURL url("http://www.example.com/dict");
857 ResetOwner(false);
858 InsertDictionaryForURL(url, "0");
860 std::string hash;
861 int old_count;
863 ClearOwner();
864 base::DictionaryValue* dict = nullptr;
865 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, &hash, &dict));
866 ASSERT_TRUE(dict->GetInteger("use_count", &old_count));
869 ResetOwner(false);
870 ASSERT_TRUE(CompleteLoadFromURL(url, "0", true));
871 owner_->OnDictionaryUsed(manager_.get(), hash);
873 int new_count;
875 ClearOwner();
876 base::DictionaryValue* dict = nullptr;
877 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, nullptr, &dict));
878 ASSERT_TRUE(dict->GetInteger("use_count", &new_count));
881 EXPECT_EQ(old_count + 1, new_count);
884 TEST_F(SdchOwnerPersistenceTest, LoadingDictionaryMerges) {
885 const GURL url0("http://www.example.com/dict0");
886 const GURL url1("http://www.example.com/dict1");
888 ResetOwner(false);
889 InsertDictionaryForURL(url1, "1");
891 ResetOwner(true);
892 InsertDictionaryForURL(url0, "0");
893 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
894 owner_->EnablePersistentStorage(pref_store_.get());
895 ASSERT_TRUE(CompleteLoadFromURL(url1, "1", true));
896 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
899 TEST_F(SdchOwnerPersistenceTest, PersistenceMetrics) {
900 const GURL url0("http://www.example.com/dict0");
901 const GURL url1("http://www.example.com/dict1");
902 ResetOwner(false);
904 InsertDictionaryForURL(url0, "0");
905 InsertDictionaryForURL(url1, "1");
907 ResetOwner(false);
909 base::HistogramTester tester;
911 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
912 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", false));
914 tester.ExpectTotalCount("Sdch3.NetworkBytesSpent", 1);
915 tester.ExpectUniqueSample("Sdch3.NetworkBytesSpent",
916 CreateDictionary(url1, "1").size(), 1);
919 } // namespace net