[Cronet] Delay StartNetLog and StopNetLog until native request context is initialized
[chromium-blink-merge.git] / net / sdch / sdch_owner_unittest.cc
blobbbaceedc66ec055a02eb95d49222a813c1df3da4
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/simple_test_clock.h"
10 #include "base/values.h"
11 #include "net/base/net_log.h"
12 #include "net/base/sdch_manager.h"
13 #include "net/sdch/sdch_owner.h"
14 #include "net/url_request/url_request.h"
15 #include "net/url_request/url_request_context.h"
16 #include "net/url_request/url_request_error_job.h"
17 #include "net/url_request/url_request_job.h"
18 #include "net/url_request/url_request_job_factory.h"
19 #include "net/url_request/url_request_test_util.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace {
24 bool GetDictionaryForURL(TestingPrefStore* store,
25 const GURL& url,
26 std::string* hash,
27 base::DictionaryValue** dict) {
28 base::Value* sdch_val = nullptr;
29 base::DictionaryValue* sdch_dict = nullptr;
30 if (!store->GetMutableValue("SDCH", &sdch_val))
31 return false;
32 if (!sdch_val->GetAsDictionary(&sdch_dict))
33 return false;
35 base::DictionaryValue* dicts_dict = nullptr;
36 if (!sdch_dict->GetDictionary("dictionaries", &dicts_dict))
37 return false;
39 base::DictionaryValue::Iterator it(*dicts_dict);
40 while (!it.IsAtEnd()) {
41 const base::DictionaryValue* d = nullptr;
42 if (!it.value().GetAsDictionary(&d))
43 continue;
44 std::string dict_url;
45 if (d->GetString("url", &dict_url) && dict_url == url.spec()) {
46 if (hash)
47 *hash = it.key();
48 if (dict)
49 dicts_dict->GetDictionary(it.key(), dict);
50 return true;
52 it.Advance();
55 return false;
58 } // namespace
60 namespace net {
62 static const char generic_url[] = "http://www.example.com";
63 static const char generic_domain[] = "www.example.com";
65 static std::string NewSdchDictionary(size_t dictionary_size) {
66 std::string dictionary;
67 dictionary.append("Domain: ");
68 dictionary.append(generic_domain);
69 dictionary.append("\n");
70 dictionary.append("\n");
72 size_t original_dictionary_size = dictionary.size();
73 dictionary.resize(dictionary_size);
74 for (size_t i = original_dictionary_size; i < dictionary_size; ++i)
75 dictionary[i] = static_cast<char>((i % 127) + 1);
77 return dictionary;
80 int outstanding_url_request_error_counting_jobs = 0;
81 base::Closure* empty_url_request_jobs_callback = 0;
83 // Variation of URLRequestErrorJob to count number of outstanding
84 // instances and notify when that goes to zero.
85 class URLRequestErrorCountingJob : public URLRequestJob {
86 public:
87 URLRequestErrorCountingJob(URLRequest* request,
88 NetworkDelegate* network_delegate,
89 int error)
90 : URLRequestJob(request, network_delegate),
91 error_(error),
92 weak_factory_(this) {
93 ++outstanding_url_request_error_counting_jobs;
96 void Start() override {
97 base::MessageLoop::current()->PostTask(
98 FROM_HERE, base::Bind(&URLRequestErrorCountingJob::StartAsync,
99 weak_factory_.GetWeakPtr()));
102 private:
103 ~URLRequestErrorCountingJob() override {
104 --outstanding_url_request_error_counting_jobs;
105 if (0 == outstanding_url_request_error_counting_jobs &&
106 empty_url_request_jobs_callback) {
107 empty_url_request_jobs_callback->Run();
111 void StartAsync() {
112 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, error_));
115 int error_;
117 base::WeakPtrFactory<URLRequestErrorCountingJob> weak_factory_;
119 DISALLOW_COPY_AND_ASSIGN(URLRequestErrorCountingJob);
122 static int error_jobs_created = 0;
124 class MockURLRequestJobFactory : public URLRequestJobFactory {
125 public:
126 MockURLRequestJobFactory() {}
128 ~MockURLRequestJobFactory() override {}
130 URLRequestJob* MaybeCreateJobWithProtocolHandler(
131 const std::string& scheme,
132 URLRequest* request,
133 NetworkDelegate* network_delegate) const override {
134 ++error_jobs_created;
135 return new URLRequestErrorCountingJob(request, network_delegate,
136 ERR_INTERNET_DISCONNECTED);
139 URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
140 NetworkDelegate* network_delegate,
141 const GURL& location) const override {
142 return nullptr;
145 URLRequestJob* MaybeInterceptResponse(
146 URLRequest* request,
147 NetworkDelegate* network_delegate) const override {
148 return nullptr;
151 bool IsHandledProtocol(const std::string& scheme) const override {
152 return scheme == "http";
155 bool IsHandledURL(const GURL& url) const override {
156 return url.SchemeIs("http");
159 bool IsSafeRedirectTarget(const GURL& location) const override {
160 return false;
164 class MockSdchDictionaryFetcher : public SdchDictionaryFetcher {
165 public:
166 MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_) {}
167 ~MockSdchDictionaryFetcher() {}
169 struct PendingRequest {
170 PendingRequest(const GURL& url,
171 const OnDictionaryFetchedCallback& callback)
172 : url_(url), callback_(callback) {}
173 GURL url_;
174 OnDictionaryFetchedCallback callback_;
177 virtual bool Schedule(const GURL& dictionary_url,
178 const OnDictionaryFetchedCallback& callback) {
179 if (!HasPendingRequest(dictionary_url)) {
180 requests_.push_back(PendingRequest(dictionary_url, callback));
181 return true;
183 return false;
186 virtual bool ScheduleReload(const GURL& dictionary_url,
187 const OnDictionaryFetchedCallback& callback) {
188 if (!HasPendingRequest(dictionary_url)) {
189 requests_.push_back(PendingRequest(dictionary_url, callback));
190 return true;
192 return false;
195 virtual void Cancel() {
196 requests_.clear();
199 bool HasPendingRequest(const GURL& dictionary_url) {
200 for (std::vector<PendingRequest>::iterator it = requests_.begin();
201 it != requests_.end(); ++it) {
202 if (it->url_ == dictionary_url)
203 return true;
205 return false;
208 bool CompletePendingRequest(const GURL& dictionary_url,
209 const std::string& dictionary_text,
210 const BoundNetLog& net_log) {
211 for (std::vector<PendingRequest>::iterator it = requests_.begin();
212 it != requests_.end(); ++it) {
213 if (it->url_ == dictionary_url) {
214 it->callback_.Run(dictionary_text, dictionary_url, net_log);
215 requests_.erase(it);
216 return true;
219 return false;
222 private:
223 TestURLRequestContext test_context_;
224 std::vector<PendingRequest> requests_;
225 DISALLOW_COPY_AND_ASSIGN(MockSdchDictionaryFetcher);
228 // File testing infrastructure summary:
229 // * NewSdchDictionary(): Creates a dictionary of a specific size.
230 // * URLRequestErrorCountingJob: A URLRequestJob that returns an error
231 // and counts the number of outstanding (started but not finished)
232 // jobs, and calls a global callback when that number transitions to zero.
233 // * MockURLRequestJobFactory: Factory to create the above jobs. Tracks
234 // the number of jobs created.
235 // * SdchOwnerTest: Interfaces
236 // * Access manager, owner, and net log
237 // * Return the number of jobs created in a time interval
238 // * Return dictionary present in the manager
239 // * Notify SdchOwner of an incoming dictionary (& wait until jobs clear)
240 // * Attempt to add a dictionary and test for success.
241 // Test patterns:
242 // * Let the owner know about a Get-Dictionary header and test for
243 // appropriate jobs being created.
244 // * Let the owner know that a dictionary was successfully fetched
245 // and test for appropriate outcome.
246 // * Either of the above, having previously added dictionaries to create
247 // a particular initial state.
248 class SdchOwnerTest : public testing::Test {
249 public:
250 static const size_t kMaxSizeForTesting = 1000 * 50;
251 static const size_t kMinFetchSpaceForTesting = 500;
253 SdchOwnerTest()
254 : last_jobs_created_(error_jobs_created),
255 dictionary_creation_index_(0),
256 pref_store_(new TestingPrefStore),
257 sdch_owner_(&sdch_manager_, &url_request_context_) {
258 // Any jobs created on this context will immediately error,
259 // which leaves the test in control of signals to SdchOwner.
260 url_request_context_.set_job_factory(&job_factory_);
262 // Reduce sizes to reduce time for string operations.
263 sdch_owner_.SetMaxTotalDictionarySize(kMaxSizeForTesting);
264 sdch_owner_.SetMinSpaceForDictionaryFetch(kMinFetchSpaceForTesting);
267 SdchManager& sdch_manager() { return sdch_manager_; }
268 SdchOwner& sdch_owner() { return sdch_owner_; }
269 BoundNetLog& bound_net_log() { return net_log_; }
270 TestingPrefStore& pref_store() { return *(pref_store_.get()); }
272 int JobsRecentlyCreated() {
273 int result = error_jobs_created - last_jobs_created_;
274 last_jobs_created_ = error_jobs_created;
275 return result;
278 bool DictionaryPresentInManager(const std::string& server_hash) {
279 // Presumes all tests use generic url.
280 SdchProblemCode tmp;
281 scoped_ptr<SdchManager::DictionarySet> set(
282 sdch_manager_.GetDictionarySetByHash(GURL(generic_url), server_hash,
283 &tmp));
284 return !!set.get();
287 void WaitForNoJobs() {
288 if (outstanding_url_request_error_counting_jobs == 0)
289 return;
291 base::RunLoop run_loop;
292 base::Closure quit_closure(run_loop.QuitClosure());
293 empty_url_request_jobs_callback = &quit_closure;
294 run_loop.Run();
295 empty_url_request_jobs_callback = NULL;
298 void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) {
299 sdch_owner().OnGetDictionary(&sdch_manager_, request_url, dictionary_url);
300 WaitForNoJobs();
303 // Create a unique (by hash) dictionary of the given size,
304 // associate it with a unique URL, add it to the manager through
305 // SdchOwner::OnDictionaryFetched(), and return whether that
306 // addition was successful or not.
307 bool CreateAndAddDictionary(size_t size,
308 std::string* server_hash_p,
309 base::Time last_used_time) {
310 GURL dictionary_url(
311 base::StringPrintf("%s/d%d", generic_url, dictionary_creation_index_));
312 std::string dictionary_text(NewSdchDictionary(size - 4));
313 dictionary_text += base::StringPrintf("%04d", dictionary_creation_index_);
314 ++dictionary_creation_index_;
315 std::string client_hash;
316 std::string server_hash;
317 SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash);
319 if (DictionaryPresentInManager(server_hash))
320 return false;
321 sdch_owner().OnDictionaryFetched(last_used_time, 0, dictionary_text,
322 dictionary_url, net_log_);
323 if (server_hash_p)
324 *server_hash_p = server_hash;
325 return DictionaryPresentInManager(server_hash);
328 private:
329 int last_jobs_created_;
330 BoundNetLog net_log_;
331 int dictionary_creation_index_;
333 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
334 // url_request_context_}, url_request_context_->job_factory_) require
335 // this order for correct destruction semantics.
336 MockURLRequestJobFactory job_factory_;
337 URLRequestContext url_request_context_;
338 SdchManager sdch_manager_;
339 scoped_refptr<TestingPrefStore> pref_store_;
340 SdchOwner sdch_owner_;
342 DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest);
345 // Does OnGetDictionary result in a fetch when there's enough space, and not
346 // when there's not?
347 TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) {
348 GURL request_url(std::string(generic_url) + "/r1");
350 // Fetch generated when empty.
351 GURL dict_url1(std::string(generic_url) + "/d1");
352 EXPECT_EQ(0, JobsRecentlyCreated());
353 SignalGetDictionaryAndClearJobs(request_url, dict_url1);
354 EXPECT_EQ(1, JobsRecentlyCreated());
356 // Fetch generated when half full.
357 GURL dict_url2(std::string(generic_url) + "/d2");
358 std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2));
359 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1, dict_url1,
360 bound_net_log());
361 EXPECT_EQ(0, JobsRecentlyCreated());
362 SignalGetDictionaryAndClearJobs(request_url, dict_url2);
363 EXPECT_EQ(1, JobsRecentlyCreated());
365 // Fetch not generated when close to completely full.
366 GURL dict_url3(std::string(generic_url) + "/d3");
367 std::string dictionary2(NewSdchDictionary(
368 (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2)));
369 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2, dict_url2,
370 bound_net_log());
371 EXPECT_EQ(0, JobsRecentlyCreated());
372 SignalGetDictionaryAndClearJobs(request_url, dict_url3);
373 EXPECT_EQ(0, JobsRecentlyCreated());
376 // Make sure attempts to add dictionaries do what they should.
377 TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) {
378 GURL request_url(std::string(generic_url) + "/r1");
379 std::string client_hash;
380 std::string server_hash;
382 // In the past, but still fresh for an unused dictionary.
383 base::Time dictionary_last_used_time(base::Time::Now() -
384 base::TimeDelta::FromMinutes(30));
386 // Add successful when empty.
387 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
388 dictionary_last_used_time));
389 EXPECT_EQ(0, JobsRecentlyCreated());
391 // Add successful when half full.
392 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
393 dictionary_last_used_time));
394 EXPECT_EQ(0, JobsRecentlyCreated());
396 // Add unsuccessful when full.
397 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr,
398 dictionary_last_used_time));
399 EXPECT_EQ(0, JobsRecentlyCreated());
402 // Confirm auto-eviction happens if space is needed.
403 TEST_F(SdchOwnerTest, ConfirmAutoEviction) {
404 std::string server_hash_d1;
405 std::string server_hash_d2;
406 std::string server_hash_d3;
408 // Add two dictionaries, one recent, one more than a day in the past.
409 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
410 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
412 EXPECT_TRUE(
413 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
414 EXPECT_TRUE(
415 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2, stale));
417 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
418 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
420 EXPECT_TRUE(
421 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3, fresh));
422 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
423 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
424 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
427 // Confirm auto-eviction happens if space is needed, with a more complicated
428 // situation
429 TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) {
430 std::string server_hash_d1;
431 std::string server_hash_d2;
432 std::string server_hash_d3;
434 // Add dictionaries, one recent, two more than a day in the past that
435 // between them add up to the space needed.
436 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
437 base::Time stale(base::Time::Now() - base::TimeDelta::FromHours(25));
438 EXPECT_TRUE(
439 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1, fresh));
441 EXPECT_TRUE(
442 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2, stale));
443 EXPECT_TRUE(
444 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3, stale));
446 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
447 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
448 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
450 std::string server_hash_d4;
451 EXPECT_TRUE(
452 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
453 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
454 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
455 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
456 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
459 // Confirm if only one dictionary needs to be evicted it's the oldest.
460 TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) {
461 std::string server_hash_d1;
462 std::string server_hash_d2;
463 std::string server_hash_d3;
465 // Add dictionaries, one recent, one two days in the past, and one
466 // four days in the past.
467 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
468 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
469 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
471 EXPECT_TRUE(
472 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
474 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
475 stale_newer));
477 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
478 stale_older));
480 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
481 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
482 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
484 // The addition of a new dictionary should succeed, evicting only the
485 // oldest one.
487 std::string server_hash_d4;
488 EXPECT_TRUE(
489 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
490 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
491 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
492 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
493 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
496 // Confirm using a dictionary changes eviction behavior properly.
497 TEST_F(SdchOwnerTest, UseChangesEviction) {
498 std::string server_hash_d1;
499 std::string server_hash_d2;
500 std::string server_hash_d3;
502 // Add dictionaries, one recent, one two days in the past, and one
503 // four days in the past.
504 base::Time fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
505 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
506 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
508 EXPECT_TRUE(
509 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
511 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
512 stale_newer));
514 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
515 stale_older));
517 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
518 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
519 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
521 // Use the oldest dictionary.
522 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
524 // The addition of a new dictionary should succeed, evicting only the
525 // newer stale one.
526 std::string server_hash_d4;
527 EXPECT_TRUE(
528 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
529 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
530 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
531 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
532 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
535 // Confirm using a dictionary can prevent the addition of a new dictionary.
536 TEST_F(SdchOwnerTest, UsePreventsAddition) {
537 std::string server_hash_d1;
538 std::string server_hash_d2;
539 std::string server_hash_d3;
541 // Add dictionaries, one recent, one two days in the past, and one
542 // four days in the past.
543 base::Time fresh(base::Time::Now() - base::TimeDelta::FromMinutes(30));
544 base::Time stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
545 base::Time stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
547 EXPECT_TRUE(
548 CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1, fresh));
550 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2,
551 stale_newer));
553 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3,
554 stale_older));
556 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
557 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
558 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
560 // Use the older dictionaries.
561 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2);
562 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
564 // The addition of a new dictionary should fail, not evicting anything.
565 std::string server_hash_d4;
566 EXPECT_FALSE(
567 CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4, fresh));
568 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
569 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
570 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
571 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4));
574 // Confirm clear gets all the space back.
575 TEST_F(SdchOwnerTest, ClearReturnsSpace) {
576 std::string server_hash_d1;
577 std::string server_hash_d2;
579 // Take up all the space.
580 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
581 base::Time::Now()));
582 // Addition should fail.
583 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
584 base::Time::Now()));
585 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
586 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
587 sdch_manager().ClearData();
588 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
589 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
591 // Addition should now succeed.
592 EXPECT_TRUE(
593 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
596 // Confirm memory pressure gets all the space back.
597 TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) {
598 std::string server_hash_d1;
599 std::string server_hash_d2;
601 // Take up all the space.
602 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1,
603 base::Time::Now()));
605 // Addition should fail.
606 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2,
607 base::Time::Now()));
609 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
610 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
612 base::MemoryPressureListener::NotifyMemoryPressure(
613 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
614 // The notification may have (implementation note: does :-}) use a PostTask,
615 // so we drain the local message queue. This should be safe (i.e. not have
616 // an inifinite number of messages) in a unit test.
617 base::RunLoop().RunUntilIdle();
619 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
620 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
622 // Addition should now succeed.
623 EXPECT_TRUE(
624 CreateAndAddDictionary(kMaxSizeForTesting, nullptr, base::Time::Now()));
627 class SdchOwnerPersistenceTest : public ::testing::Test {
628 public:
629 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
630 pref_store_->SetInitializationCompleted();
632 virtual ~SdchOwnerPersistenceTest() {}
634 void ClearOwner() {
635 owner_.reset(NULL);
638 void ResetOwner(bool delay) {
639 // This has to be done first, since SdchOwner may be observing SdchManager,
640 // and SdchManager can't be destroyed with a live observer.
641 owner_.reset(NULL);
642 manager_.reset(new SdchManager());
643 fetcher_ = new MockSdchDictionaryFetcher();
644 owner_.reset(new SdchOwner(manager_.get(),
645 &url_request_context_));
646 owner_->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting);
647 owner_->SetMinSpaceForDictionaryFetch(
648 SdchOwnerTest::kMinFetchSpaceForTesting);
649 owner_->SetFetcherForTesting(make_scoped_ptr(fetcher_));
650 if (!delay)
651 owner_->EnablePersistentStorage(pref_store_.get());
654 void InsertDictionaryForURL(const GURL& url, const std::string& nonce) {
655 owner_->OnDictionaryFetched(base::Time::Now(), 1,
656 CreateDictionary(url, nonce),
657 url, net_log_);
660 bool CompleteLoadFromURL(const GURL& url, const std::string& nonce) {
661 return fetcher_->CompletePendingRequest(url, CreateDictionary(url, nonce),
662 net_log_);
665 std::string CreateDictionary(const GURL& url, const std::string& nonce) {
666 std::string dict;
667 dict.append("Domain: ");
668 dict.append(url.host());
669 dict.append("\n\n");
670 dict.append(url.spec());
671 dict.append(nonce);
672 return dict;
675 protected:
676 BoundNetLog net_log_;
677 scoped_refptr<TestingPrefStore> pref_store_;
678 scoped_ptr<SdchManager> manager_;
679 MockSdchDictionaryFetcher* fetcher_;
680 scoped_ptr<SdchOwner> owner_;
681 TestURLRequestContext url_request_context_;
684 // Test an empty persistence store.
685 TEST_F(SdchOwnerPersistenceTest, Empty) {
686 ResetOwner(false);
687 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
690 // Test a persistence store with an empty dictionary.
691 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDict) {
692 pref_store_->SetValue("SDCH", new base::DictionaryValue());
693 ResetOwner(false);
694 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
697 // Test a persistence store with a bad version number.
698 TEST_F(SdchOwnerPersistenceTest, Persistent_BadVersion) {
699 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
700 sdch_dict->SetInteger("version", 2);
701 pref_store_->SetValue("SDCH", sdch_dict);
703 ResetOwner(false);
704 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
707 // Test a persistence store with an empty dictionaries map.
708 TEST_F(SdchOwnerPersistenceTest, Persistent_EmptyDictList) {
709 base::DictionaryValue* sdch_dict = new base::DictionaryValue();
710 scoped_ptr<base::DictionaryValue> dicts(new base::DictionaryValue());
711 sdch_dict->SetInteger("version", 1);
712 sdch_dict->Set("dictionaries", dicts.Pass());
713 pref_store_->SetValue("SDCH", sdch_dict);
715 ResetOwner(false);
716 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
719 TEST_F(SdchOwnerPersistenceTest, OneDict) {
720 const GURL url("http://www.example.com/dict");
721 ResetOwner(false);
722 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
723 InsertDictionaryForURL(url, "0");
724 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
726 ResetOwner(false);
727 EXPECT_EQ(0, owner_->GetDictionaryCountForTesting());
728 EXPECT_TRUE(CompleteLoadFromURL(url, "0"));
729 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
732 TEST_F(SdchOwnerPersistenceTest, TwoDicts) {
733 const GURL url0("http://www.example.com/dict0");
734 const GURL url1("http://www.example.com/dict1");
735 ResetOwner(false);
736 InsertDictionaryForURL(url0, "0");
737 InsertDictionaryForURL(url1, "1");
739 ResetOwner(false);
740 EXPECT_TRUE(CompleteLoadFromURL(url0, "0"));
741 EXPECT_TRUE(CompleteLoadFromURL(url1, "1"));
742 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
743 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
744 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url1));
747 TEST_F(SdchOwnerPersistenceTest, OneGoodDictOneBadDict) {
748 const GURL url0("http://www.example.com/dict0");
749 const GURL url1("http://www.example.com/dict1");
750 ResetOwner(false);
751 InsertDictionaryForURL(url0, "0");
752 InsertDictionaryForURL(url1, "1");
754 // Mutate the pref store a bit now. Clear the owner first, to ensure that the
755 // SdchOwner doesn't observe these changes and object. The manual dictionary
756 // manipulation is a bit icky.
757 ClearOwner();
758 base::DictionaryValue* dict = nullptr;
759 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url1, nullptr, &dict));
760 dict->Remove("use_count", nullptr);
762 ResetOwner(false);
763 EXPECT_TRUE(CompleteLoadFromURL(url0, "0"));
764 EXPECT_FALSE(CompleteLoadFromURL(url1, "1"));
765 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
766 EXPECT_TRUE(owner_->HasDictionaryFromURLForTesting(url0));
767 EXPECT_FALSE(owner_->HasDictionaryFromURLForTesting(url1));
770 TEST_F(SdchOwnerPersistenceTest, UsingDictionaryUpdatesUseCount) {
771 const GURL url("http://www.example.com/dict");
772 ResetOwner(false);
773 InsertDictionaryForURL(url, "0");
775 std::string hash;
776 int old_count;
778 ClearOwner();
779 base::DictionaryValue* dict = nullptr;
780 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, &hash, &dict));
781 ASSERT_TRUE(dict->GetInteger("use_count", &old_count));
784 ResetOwner(false);
785 ASSERT_TRUE(CompleteLoadFromURL(url, "0"));
786 owner_->OnDictionaryUsed(manager_.get(), hash);
788 int new_count;
790 ClearOwner();
791 base::DictionaryValue* dict = nullptr;
792 ASSERT_TRUE(GetDictionaryForURL(pref_store_.get(), url, nullptr, &dict));
793 ASSERT_TRUE(dict->GetInteger("use_count", &new_count));
796 EXPECT_EQ(old_count + 1, new_count);
799 TEST_F(SdchOwnerPersistenceTest, LoadingDictionaryMerges) {
800 const GURL url0("http://www.example.com/dict0");
801 const GURL url1("http://www.example.com/dict1");
803 ResetOwner(false);
804 InsertDictionaryForURL(url1, "1");
806 ResetOwner(true);
807 InsertDictionaryForURL(url0, "0");
808 EXPECT_EQ(1, owner_->GetDictionaryCountForTesting());
809 owner_->EnablePersistentStorage(pref_store_.get());
810 ASSERT_TRUE(CompleteLoadFromURL(url1, "1"));
811 EXPECT_EQ(2, owner_->GetDictionaryCountForTesting());
814 } // namespace net