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"
25 bool GetDictionaryForURL(TestingPrefStore
* store
,
28 base::DictionaryValue
** dict
) {
29 base::Value
* sdch_val
= nullptr;
30 base::DictionaryValue
* sdch_dict
= nullptr;
31 if (!store
->GetMutableValue("SDCH", &sdch_val
))
33 if (!sdch_val
->GetAsDictionary(&sdch_dict
))
36 base::DictionaryValue
* dicts_dict
= nullptr;
37 if (!sdch_dict
->GetDictionary("dictionaries", &dicts_dict
))
40 base::DictionaryValue::Iterator
it(*dicts_dict
);
41 while (!it
.IsAtEnd()) {
42 const base::DictionaryValue
* d
= nullptr;
43 if (!it
.value().GetAsDictionary(&d
))
46 if (d
->GetString("url", &dict_url
) && dict_url
== url
.spec()) {
50 dicts_dict
->GetDictionary(it
.key(), dict
);
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);
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
{
88 URLRequestErrorCountingJob(URLRequest
* request
,
89 NetworkDelegate
* network_delegate
,
91 : URLRequestJob(request
, network_delegate
),
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()));
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();
113 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED
, 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
{
127 MockURLRequestJobFactory() {}
129 ~MockURLRequestJobFactory() override
{}
131 URLRequestJob
* MaybeCreateJobWithProtocolHandler(
132 const std::string
& scheme
,
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
{
146 URLRequestJob
* MaybeInterceptResponse(
148 NetworkDelegate
* network_delegate
) const override
{
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
{
165 class MockSdchDictionaryFetcher
: public SdchDictionaryFetcher
{
167 MockSdchDictionaryFetcher() : SdchDictionaryFetcher(&test_context_
) {}
168 ~MockSdchDictionaryFetcher() {}
170 struct PendingRequest
{
171 PendingRequest(const GURL
& url
,
172 const OnDictionaryFetchedCallback
& callback
)
173 : url_(url
), callback_(callback
) {}
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
));
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
));
196 virtual void Cancel() {
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
)
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
,
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.
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
{
253 static const size_t kMaxSizeForTesting
= 1000 * 50;
254 static const size_t kMinFetchSpaceForTesting
= 500;
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
;
281 bool DictionaryPresentInManager(const std::string
& server_hash
) {
282 // Presumes all tests use generic url.
284 scoped_ptr
<SdchManager::DictionarySet
> set(
285 sdch_manager_
.GetDictionarySetByHash(GURL(generic_url
), server_hash
,
290 void WaitForNoJobs() {
291 if (outstanding_url_request_error_counting_jobs
== 0)
294 base::RunLoop run_loop
;
295 base::Closure
quit_closure(run_loop
.QuitClosure());
296 empty_url_request_jobs_callback
= &quit_closure
;
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
);
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
) {
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
))
324 sdch_owner().OnDictionaryFetched(last_used_time
, 0, dictionary_text
,
325 dictionary_url
, net_log_
, false);
327 *server_hash_p
= server_hash
;
328 return DictionaryPresentInManager(server_hash
);
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
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));
416 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
418 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d2
, stale
));
420 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
421 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
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
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));
442 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
445 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
, stale
));
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
;
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));
475 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
477 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
480 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
490 std::string server_hash_d4
;
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));
512 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
514 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
517 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
529 std::string server_hash_d4
;
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));
551 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
553 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
556 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
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
;
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
,
585 // Addition should fail.
586 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
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.
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
,
608 // Addition should fail.
609 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
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.
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
,
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
{
674 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
675 pref_store_
->SetInitializationCompleted();
677 virtual ~SdchOwnerPersistenceTest() {}
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.
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_
));
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
) {
713 dict
.append("Domain: ");
714 dict
.append(url
.host());
716 dict
.append(url
.spec());
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
) {
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());
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
);
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
);
762 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
765 TEST_F(SdchOwnerPersistenceTest
, OneDict
) {
766 const GURL
url("http://www.example.com/dict");
768 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
769 InsertDictionaryForURL(url
, "0");
770 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
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");
782 InsertDictionaryForURL(url0
, "0");
783 InsertDictionaryForURL(url1
, "1");
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");
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.
804 base::DictionaryValue
* dict
= nullptr;
805 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url1
, nullptr, &dict
));
806 dict
->Remove("use_count", nullptr);
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");
819 InsertDictionaryForURL(url
, "0");
825 base::DictionaryValue
* dict
= nullptr;
826 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url
, &hash
, &dict
));
827 ASSERT_TRUE(dict
->GetInteger("use_count", &old_count
));
831 ASSERT_TRUE(CompleteLoadFromURL(url
, "0", true));
832 owner_
->OnDictionaryUsed(manager_
.get(), hash
);
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");
850 InsertDictionaryForURL(url1
, "1");
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");
865 InsertDictionaryForURL(url0
, "0");
866 InsertDictionaryForURL(url1
, "1");
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);