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"
24 bool GetDictionaryForURL(TestingPrefStore
* store
,
27 base::DictionaryValue
** dict
) {
28 base::Value
* sdch_val
= nullptr;
29 base::DictionaryValue
* sdch_dict
= nullptr;
30 if (!store
->GetMutableValue("SDCH", &sdch_val
))
32 if (!sdch_val
->GetAsDictionary(&sdch_dict
))
35 base::DictionaryValue
* dicts_dict
= nullptr;
36 if (!sdch_dict
->GetDictionary("dictionaries", &dicts_dict
))
39 base::DictionaryValue::Iterator
it(*dicts_dict
);
40 while (!it
.IsAtEnd()) {
41 const base::DictionaryValue
* d
= nullptr;
42 if (!it
.value().GetAsDictionary(&d
))
45 if (d
->GetString("url", &dict_url
) && dict_url
== url
.spec()) {
49 dicts_dict
->GetDictionary(it
.key(), dict
);
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);
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
{
87 URLRequestErrorCountingJob(URLRequest
* request
,
88 NetworkDelegate
* network_delegate
,
90 : URLRequestJob(request
, network_delegate
),
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()));
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();
112 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED
, 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
{
126 MockURLRequestJobFactory() {}
128 ~MockURLRequestJobFactory() override
{}
130 URLRequestJob
* MaybeCreateJobWithProtocolHandler(
131 const std::string
& scheme
,
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
{
145 URLRequestJob
* MaybeInterceptResponse(
147 NetworkDelegate
* network_delegate
) const override
{
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
{
164 class MockSdchDictionaryFetcher
: public SdchDictionaryFetcher
{
166 MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_
) {}
167 ~MockSdchDictionaryFetcher() {}
169 struct PendingRequest
{
170 PendingRequest(const GURL
& url
,
171 const OnDictionaryFetchedCallback
& callback
)
172 : url_(url
), callback_(callback
) {}
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
));
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
));
195 virtual void Cancel() {
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
)
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
);
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.
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
{
250 static const size_t kMaxSizeForTesting
= 1000 * 50;
251 static const size_t kMinFetchSpaceForTesting
= 500;
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
;
278 bool DictionaryPresentInManager(const std::string
& server_hash
) {
279 // Presumes all tests use generic url.
281 scoped_ptr
<SdchManager::DictionarySet
> set(
282 sdch_manager_
.GetDictionarySetByHash(GURL(generic_url
), server_hash
,
287 void WaitForNoJobs() {
288 if (outstanding_url_request_error_counting_jobs
== 0)
291 base::RunLoop run_loop
;
292 base::Closure
quit_closure(run_loop
.QuitClosure());
293 empty_url_request_jobs_callback
= &quit_closure
;
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
);
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
) {
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
))
321 sdch_owner().OnDictionaryFetched(last_used_time
, 0, dictionary_text
,
322 dictionary_url
, net_log_
);
324 *server_hash_p
= server_hash
;
325 return DictionaryPresentInManager(server_hash
);
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
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
,
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
,
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));
413 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
415 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d2
, stale
));
417 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
418 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
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
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));
439 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
442 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
, stale
));
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
;
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));
472 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
474 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
477 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
487 std::string server_hash_d4
;
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));
509 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
511 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
514 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
526 std::string server_hash_d4
;
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));
548 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
550 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
553 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
;
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
,
582 // Addition should fail.
583 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
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.
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
,
605 // Addition should fail.
606 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
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.
624 CreateAndAddDictionary(kMaxSizeForTesting
, nullptr, base::Time::Now()));
627 class SdchOwnerPersistenceTest
: public ::testing::Test
{
629 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
630 pref_store_
->SetInitializationCompleted();
632 virtual ~SdchOwnerPersistenceTest() {}
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.
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_
));
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
),
660 bool CompleteLoadFromURL(const GURL
& url
, const std::string
& nonce
) {
661 return fetcher_
->CompletePendingRequest(url
, CreateDictionary(url
, nonce
),
665 std::string
CreateDictionary(const GURL
& url
, const std::string
& nonce
) {
667 dict
.append("Domain: ");
668 dict
.append(url
.host());
670 dict
.append(url
.spec());
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
) {
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());
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
);
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
);
716 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
719 TEST_F(SdchOwnerPersistenceTest
, OneDict
) {
720 const GURL
url("http://www.example.com/dict");
722 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
723 InsertDictionaryForURL(url
, "0");
724 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
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");
736 InsertDictionaryForURL(url0
, "0");
737 InsertDictionaryForURL(url1
, "1");
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");
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.
758 base::DictionaryValue
* dict
= nullptr;
759 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url1
, nullptr, &dict
));
760 dict
->Remove("use_count", nullptr);
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");
773 InsertDictionaryForURL(url
, "0");
779 base::DictionaryValue
* dict
= nullptr;
780 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url
, &hash
, &dict
));
781 ASSERT_TRUE(dict
->GetInteger("use_count", &old_count
));
785 ASSERT_TRUE(CompleteLoadFromURL(url
, "0"));
786 owner_
->OnDictionaryUsed(manager_
.get(), hash
);
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");
804 InsertDictionaryForURL(url1
, "1");
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());