PlzNavigate: Improvements to RFHM commit logic.
[chromium-blink-merge.git] / net / sdch / sdch_owner_unittest.cc
blobe670aee712041726cb9887ac5c7502c4e31f3ea1
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_(&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_; }
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 private:
332 int last_jobs_created_;
333 BoundNetLog net_log_;
334 int dictionary_creation_index_;
336 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
337 // url_request_context_}, url_request_context_->job_factory_) require
338 // this order for correct destruction semantics.
339 MockURLRequestJobFactory job_factory_;
340 URLRequestContext url_request_context_;
341 SdchManager sdch_manager_;
342 scoped_refptr<TestingPrefStore> pref_store_;
343 SdchOwner sdch_owner_;
345 DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest);
348 // Does OnGetDictionary result in a fetch when there's enough space, and not
349 // when there's not?
350 TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) {
351 GURL request_url(std::string(generic_url) + "/r1");
353 // Fetch generated when empty.
354 GURL dict_url1(std::string(generic_url) + "/d1");
355 EXPECT_EQ(0, JobsRecentlyCreated());
356 SignalGetDictionaryAndClearJobs(request_url, dict_url1);
357 EXPECT_EQ(1, JobsRecentlyCreated());
359 // Fetch generated when half full.
360 GURL dict_url2(std::string(generic_url) + "/d2");
361 std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2));
362 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1,
363 dict_url1, bound_net_log(), false);
364 EXPECT_EQ(0, JobsRecentlyCreated());
365 SignalGetDictionaryAndClearJobs(request_url, dict_url2);
366 EXPECT_EQ(1, JobsRecentlyCreated());
368 // Fetch not generated when close to completely full.
369 GURL dict_url3(std::string(generic_url) + "/d3");
370 std::string dictionary2(NewSdchDictionary(
371 (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2)));
372 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2,
373 dict_url2, bound_net_log(), false);
374 EXPECT_EQ(0, JobsRecentlyCreated());
375 SignalGetDictionaryAndClearJobs(request_url, dict_url3);
376 EXPECT_EQ(0, JobsRecentlyCreated());
379 // Make sure attempts to add dictionaries do what they should.
380 TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) {
381 GURL request_url(std::string(generic_url) + "/r1");
382 std::string client_hash;
383 std::string server_hash;
385 // In the past, but still fresh for an unused dictionary.
386 base::Time dictionary_last_used_time(base::Time::Now() -
387 base::TimeDelta::FromMinutes(30));
389 // Add successful when empty.
390 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
391 dictionary_last_used_time));
392 EXPECT_EQ(0, JobsRecentlyCreated());
394 // Add successful when half full.
395 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
396 dictionary_last_used_time));
397 EXPECT_EQ(0, JobsRecentlyCreated());
399 // Add unsuccessful when full.
400 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
401 dictionary_last_used_time));
402 EXPECT_EQ(0, JobsRecentlyCreated());
405 // Confirm auto-eviction happens if space is needed.
406 TEST_F(SdchOwnerTest, ConfirmAutoEviction) {
407 std::string server_hash_d1;
408 std::string server_hash_d2;
409 std::string server_hash_d3;
411 // Add two dictionaries, one recent, one more than a day in the past.
412 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
413 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
415 EXPECT_TRUE(
416 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
417 EXPECT_TRUE(
418 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2, stale));
420 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
421 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
423 EXPECT_TRUE(
424 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3, fresh));
425 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
426 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
427 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
430 // Confirm auto-eviction happens if space is needed, with a more complicated
431 // situation
432 TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) {
433 std::string server_hash_d1;
434 std::string server_hash_d2;
435 std::string server_hash_d3;
437 // Add dictionaries, one recent, two more than a day in the past that
438 // between them add up to the space needed.
439 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
440 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
441 EXPECT_TRUE(
442 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
444 EXPECT_TRUE(
445 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, stale));
446 EXPECT_TRUE(
447 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, stale));
449 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
450 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
451 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
453 std::string server_hash_d4;
454 EXPECT_TRUE(
455 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
456 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
457 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
458 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
459 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
462 // Confirm if only one dictionary needs to be evicted it's the oldest.
463 TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) {
464 std::string server_hash_d1;
465 std::string server_hash_d2;
466 std::string server_hash_d3;
468 // Add dictionaries, one recent, one two days in the past, and one
469 // four days in the past.
470 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
471 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
472 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
474 EXPECT_TRUE(
475 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
477 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
478 stale_newer));
480 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
481 stale_older));
483 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
484 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
485 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
487 // The addition of a new dictionary should succeed, evicting only the
488 // oldest one.
490 std::string server_hash_d4;
491 EXPECT_TRUE(
492 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
493 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
494 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
495 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
496 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
499 // Confirm using a dictionary changes eviction behavior properly.
500 TEST_F(SdchOwnerTest, UseChangesEviction) {
501 std::string server_hash_d1;
502 std::string server_hash_d2;
503 std::string server_hash_d3;
505 // Add dictionaries, one recent, one two days in the past, and one
506 // four days in the past.
507 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
508 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
509 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
511 EXPECT_TRUE(
512 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
514 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
515 stale_newer));
517 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
518 stale_older));
520 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
521 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
522 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
524 // Use the oldest dictionary.
525 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
527 // The addition of a new dictionary should succeed, evicting only the
528 // newer stale 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_FALSE(DictionaryPresentInManager(server_hash_d2));
534 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
535 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
538 // Confirm using a dictionary can prevent the addition of a new dictionary.
539 TEST_F(SdchOwnerTest, UsePreventsAddition) {
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::FromMinutes(30));
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 older dictionaries.
564 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2);
565 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
567 // The addition of a new dictionary should fail, not evicting anything.
568 std::string server_hash_d4;
569 EXPECT_FALSE(
570 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
571 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
572 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
573 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
574 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4));
577 // Confirm clear gets all the space back.
578 TEST_F(SdchOwnerTest, ClearReturnsSpace) {
579 std::string server_hash_d1;
580 std::string server_hash_d2;
582 // Take up all the space.
583 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
584 base::Time::Now()));
585 // Addition should fail.
586 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
587 base::Time::Now()));
588 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
589 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
590 sdch_manager().ClearData();
591 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
592 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
594 // Addition should now succeed.
595 EXPECT_TRUE(
596 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
599 // Confirm memory pressure gets all the space back.
600 TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) {
601 std::string server_hash_d1;
602 std::string server_hash_d2;
604 // Take up all the space.
605 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
606 base::Time::Now()));
608 // Addition should fail.
609 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
610 base::Time::Now()));
612 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
613 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
615 base::MemoryPressureListener::NotifyMemoryPressure(
616 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
617 // The notification may have (implementation note: does :-}) use a PostTask,
618 // so we drain the local message queue. This should be safe (i.e. not have
619 // an inifinite number of messages) in a unit test.
620 base::RunLoop().RunUntilIdle();
622 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
623 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
625 // Addition should now succeed.
626 EXPECT_TRUE(
627 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
630 // Confirm that use of a pinned dictionary after its removal works properly.
631 TEST_F(SdchOwnerTest, PinRemoveUse) {
632 pref_store().SetInitializationCompleted();
633 sdch_owner().EnablePersistentStorage(&pref_store());
635 std::string server_hash_d1;
636 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1,
637 base::Time::Now()));
639 scoped_ptr<SdchManager::DictionarySet> return_set(
640 sdch_manager().GetDictionarySet(
641 GURL(std::string(generic_url) + "/x.html")));
642 ASSERT_TRUE(return_set.get());
643 EXPECT_TRUE(return_set->GetDictionary(server_hash_d1));
645 const base::Value* result = nullptr;
646 const base::DictionaryValue* dict_result = nullptr;
647 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
648 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
649 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
650 EXPECT_TRUE(dict_result->Get("dictionaries." + server_hash_d1, &result));
652 sdch_manager().ClearData();
654 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
655 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
656 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
657 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
659 scoped_ptr<SdchManager::DictionarySet> return_set2(
660 sdch_manager().GetDictionarySet(
661 GURL(std::string(generic_url) + "/x.html")));
662 EXPECT_FALSE(return_set2.get());
664 sdch_manager().OnDictionaryUsed(server_hash_d1);
666 ASSERT_TRUE(pref_store().GetValue("SDCH", &result));
667 ASSERT_TRUE(result->GetAsDictionary(&dict_result));
668 EXPECT_TRUE(dict_result->Get("dictionaries", &result));
669 EXPECT_FALSE(dict_result->Get("dictionaries." + server_hash_d1, &result));
672 class SdchOwnerPersistenceTest : public ::testing::Test {
673 public:
674 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
675 pref_store_->SetInitializationCompleted();
677 virtual ~SdchOwnerPersistenceTest() {}
679 void ClearOwner() {
680 owner_.reset(NULL);
683 void ResetOwner(bool delay) {
684 // This has to be done first, since SdchOwner may be observing SdchManager,
685 // and SdchManager can't be destroyed with a live observer.
686 owner_.reset(NULL);
687 manager_.reset(new SdchManager());
688 fetcher_ = new MockSdchDictionaryFetcher();
689 owner_.reset(new SdchOwner(manager_.get(),
690 &url_request_context_));
691 owner_->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting);
692 owner_->SetMinSpaceForDictionaryFetch(
693 SdchOwnerTest::kMinFetchSpaceForTesting);
694 owner_->SetFetcherForTesting(make_scoped_ptr(fetcher_));
695 if (!delay)
696 owner_->EnablePersistentStorage(pref_store_.get());
699 void InsertDictionaryForURL(const GURL& url, const std::string& nonce) {
700 owner_->OnDictionaryFetched(base::Time::Now(), 1,
701 CreateDictionary(url, nonce),
702 url, net_log_, false);
705 bool CompleteLoadFromURL(const GURL& url, const std::string& nonce,
706 bool was_from_cache) {
707 return fetcher_->CompletePendingRequest(url, CreateDictionary(url, nonce),
708 net_log_, was_from_cache);
711 std::string CreateDictionary(const GURL& url, const std::string& nonce) {
712 std::string dict;
713 dict.append("Domain: ");
714 dict.append(url.host());
715 dict.append("\n\n");
716 dict.append(url.spec());
717 dict.append(nonce);
718 return dict;
721 protected:
722 BoundNetLog net_log_;
723 scoped_refptr<TestingPrefStore> pref_store_;
724 scoped_ptr<SdchManager> manager_;
725 MockSdchDictionaryFetcher* fetcher_;
726 scoped_ptr<SdchOwner> owner_;
727 TestURLRequestContext url_request_context_;
730 // Test an empty persistence store.
731 TEST_F(SdchOwnerPersistenceTest, Empty) {
732 ResetOwner(false);
733 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
736 // Test a persistence store with an empty dictionary.
737 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDict) {
738 pref_store_->SetValue("SDCH", new base::DictionaryValue());
739 ResetOwner(false);
740 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
743 // Test a persistence store with a bad version number.
744 TEST_F(SdchOwnerPersistenceTest, Persistent_BadVersion) {
745 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
746 sdch_dict->SetInteger("version", 2);
747 pref_store_->SetValue("SDCH", sdch_dict);
749 ResetOwner(false);
750 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
753 // Test a persistence store with an empty dictionaries map.
754 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDictList) {
755 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
756 scoped_ptr<base::DictionaryValue> dicts(new base::DictionaryValue());
757 sdch_dict->SetInteger("version", 1);
758 sdch_dict->Set("dictionaries", dicts.Pass());
759 pref_store_->SetValue("SDCH", sdch_dict);
761 ResetOwner(false);
762 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
765 TEST_F(SdchOwnerPersistenceTest, OneDict) {
766 const GURL url("http://www.example.com/dict");
767 ResetOwner(false);
768 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
769 InsertDictionaryForURL(url, "0");
770 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
772 ResetOwner(false);
773 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
774 EXPECT_TRUE(CompleteLoadFromURL(url, "0", true));
775 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
778 TEST_F(SdchOwnerPersistenceTest, TwoDicts) {
779 const GURL url0("http://www.example.com/dict0");
780 const GURL url1("http://www.example.com/dict1");
781 ResetOwner(false);
782 InsertDictionaryForURL(url0, "0");
783 InsertDictionaryForURL(url1, "1");
785 ResetOwner(false);
786 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
787 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", true));
788 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
789 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
790 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url1));
793 TEST_F(SdchOwnerPersistenceTest, OneGoodDictOneBadDict) {
794 const GURL url0("http://www.example.com/dict0");
795 const GURL url1("http://www.example.com/dict1");
796 ResetOwner(false);
797 InsertDictionaryForURL(url0, "0");
798 InsertDictionaryForURL(url1, "1");
800 // Mutate the pref store a bit now. Clear the owner first, to ensure that the
801 // SdchOwner doesn't observe these changes and object. The manual dictionary
802 // manipulation is a bit icky.
803 ClearOwner();
804 base::DictionaryValue* dict = nullptr;
805 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url1, nullptr, &dict));
806 dict->Remove("use_count", nullptr);
808 ResetOwner(false);
809 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
810 EXPECT_FALSE(CompleteLoadFromURL(url1, "1", true));
811 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
812 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
813 EXPECT_FALSE(owner_->HasDictionaryFromURLForTesting(url1));
816 TEST_F(SdchOwnerPersistenceTest, UsingDictionaryUpdatesUseCount) {
817 const GURL url("http://www.example.com/dict");
818 ResetOwner(false);
819 InsertDictionaryForURL(url, "0");
821 std::string hash;
822 int old_count;
824 ClearOwner();
825 base::DictionaryValue* dict = nullptr;
826 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, &hash, &dict));
827 ASSERT_TRUE(dict->GetInteger("use_count", &old_count));
830 ResetOwner(false);
831 ASSERT_TRUE(CompleteLoadFromURL(url, "0", true));
832 owner_->OnDictionaryUsed(manager_.get(), hash);
834 int new_count;
836 ClearOwner();
837 base::DictionaryValue* dict = nullptr;
838 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, nullptr, &dict));
839 ASSERT_TRUE(dict->GetInteger("use_count", &new_count));
842 EXPECT_EQ(old_count + 1, new_count);
845 TEST_F(SdchOwnerPersistenceTest, LoadingDictionaryMerges) {
846 const GURL url0("http://www.example.com/dict0");
847 const GURL url1("http://www.example.com/dict1");
849 ResetOwner(false);
850 InsertDictionaryForURL(url1, "1");
852 ResetOwner(true);
853 InsertDictionaryForURL(url0, "0");
854 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
855 owner_->EnablePersistentStorage(pref_store_.get());
856 ASSERT_TRUE(CompleteLoadFromURL(url1, "1", true));
857 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
860 TEST_F(SdchOwnerPersistenceTest, PersistenceMetrics) {
861 const GURL url0("http://www.example.com/dict0");
862 const GURL url1("http://www.example.com/dict1");
863 ResetOwner(false);
865 InsertDictionaryForURL(url0, "0");
866 InsertDictionaryForURL(url1, "1");
868 ResetOwner(false);
870 base::HistogramTester tester;
872 EXPECT_TRUE(CompleteLoadFromURL(url0, "0", true));
873 EXPECT_TRUE(CompleteLoadFromURL(url1, "1", false));
875 tester.ExpectTotalCount("Sdch3.NetworkBytesSpent", 1);
876 tester.ExpectUniqueSample("Sdch3.NetworkBytesSpent",
877 CreateDictionary(url1, "1").size(), 1);
880 } // namespace net