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_(new SdchOwner(&sdch_manager_
, &url_request_context_
)) {
261 // Any jobs created on this context will immediately error,
262 // which leaves the test in control of signals to SdchOwner.
263 url_request_context_
.set_job_factory(&job_factory_
);
265 // Reduce sizes to reduce time for string operations.
266 sdch_owner_
->SetMaxTotalDictionarySize(kMaxSizeForTesting
);
267 sdch_owner_
->SetMinSpaceForDictionaryFetch(kMinFetchSpaceForTesting
);
270 SdchManager
& sdch_manager() { return sdch_manager_
; }
271 SdchOwner
& sdch_owner() { return *(sdch_owner_
.get()); }
272 BoundNetLog
& bound_net_log() { return net_log_
; }
273 TestingPrefStore
& pref_store() { return *(pref_store_
.get()); }
275 int JobsRecentlyCreated() {
276 int result
= error_jobs_created
- last_jobs_created_
;
277 last_jobs_created_
= error_jobs_created
;
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 sdch_owner_
.reset(new SdchOwner(&sdch_manager_
, &url_request_context_
));
336 int last_jobs_created_
;
337 BoundNetLog net_log_
;
338 int dictionary_creation_index_
;
340 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
341 // url_request_context_}, url_request_context_->job_factory_) require
342 // this order for correct destruction semantics.
343 MockURLRequestJobFactory job_factory_
;
344 URLRequestContext url_request_context_
;
345 SdchManager sdch_manager_
;
346 scoped_refptr
<TestingPrefStore
> pref_store_
;
347 scoped_ptr
<SdchOwner
> sdch_owner_
;
349 DISALLOW_COPY_AND_ASSIGN(SdchOwnerTest
);
352 // Does OnGetDictionary result in a fetch when there's enough space, and not
354 TEST_F(SdchOwnerTest
, OnGetDictionary_Fetching
) {
355 GURL
request_url(std::string(generic_url
) + "/r1");
357 // Fetch generated when empty.
358 GURL
dict_url1(std::string(generic_url
) + "/d1");
359 EXPECT_EQ(0, JobsRecentlyCreated());
360 SignalGetDictionaryAndClearJobs(request_url
, dict_url1
);
361 EXPECT_EQ(1, JobsRecentlyCreated());
363 // Fetch generated when half full.
364 GURL
dict_url2(std::string(generic_url
) + "/d2");
365 std::string
dictionary1(NewSdchDictionary(kMaxSizeForTesting
/ 2));
366 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary1
,
367 dict_url1
, bound_net_log(), false);
368 EXPECT_EQ(0, JobsRecentlyCreated());
369 SignalGetDictionaryAndClearJobs(request_url
, dict_url2
);
370 EXPECT_EQ(1, JobsRecentlyCreated());
372 // Fetch not generated when close to completely full.
373 GURL
dict_url3(std::string(generic_url
) + "/d3");
374 std::string
dictionary2(NewSdchDictionary(
375 (kMaxSizeForTesting
/ 2 - kMinFetchSpaceForTesting
/ 2)));
376 sdch_owner().OnDictionaryFetched(base::Time::Now(), 1, dictionary2
,
377 dict_url2
, bound_net_log(), false);
378 EXPECT_EQ(0, JobsRecentlyCreated());
379 SignalGetDictionaryAndClearJobs(request_url
, dict_url3
);
380 EXPECT_EQ(0, JobsRecentlyCreated());
383 // Make sure attempts to add dictionaries do what they should.
384 TEST_F(SdchOwnerTest
, OnDictionaryFetched_Fetching
) {
385 GURL
request_url(std::string(generic_url
) + "/r1");
386 std::string client_hash
;
387 std::string server_hash
;
389 // In the past, but still fresh for an unused dictionary.
390 base::Time
dictionary_last_used_time(base::Time::Now() -
391 base::TimeDelta::FromMinutes(30));
393 // Add successful when empty.
394 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 2, nullptr,
395 dictionary_last_used_time
));
396 EXPECT_EQ(0, JobsRecentlyCreated());
398 // Add successful when half full.
399 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 2, nullptr,
400 dictionary_last_used_time
));
401 EXPECT_EQ(0, JobsRecentlyCreated());
403 // Add unsuccessful when full.
404 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
/ 2, nullptr,
405 dictionary_last_used_time
));
406 EXPECT_EQ(0, JobsRecentlyCreated());
409 // Confirm auto-eviction happens if space is needed.
410 TEST_F(SdchOwnerTest
, ConfirmAutoEviction
) {
411 base::Time start_time
= base::Time::Now();
412 std::string server_hash_d1
;
413 std::string server_hash_d2
;
414 std::string server_hash_d3
;
416 base::SimpleTestClock
* test_clock
= new base::SimpleTestClock();
417 sdch_owner().SetClockForTesting(make_scoped_ptr(test_clock
));
418 test_clock
->SetNow(base::Time::Now());
420 // Add two dictionaries, one recent, one more than a day in the past.
421 base::Time
fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
422 base::Time
stale(base::Time::Now() - base::TimeDelta::FromHours(25));
425 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
427 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d2
, stale
));
429 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
430 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
432 base::HistogramTester tester
;
433 const base::TimeDelta synthetic_delta
= base::TimeDelta::FromSeconds(5);
435 test_clock
->Advance(synthetic_delta
);
438 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d3
, fresh
));
439 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
440 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
441 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
443 base::TimeDelta expected_proc_lifetime
= synthetic_delta
* 3 +
444 base::Time::Now() - start_time
;
445 size_t expected_value_base
= ((kMaxSizeForTesting
/ 2) *
446 synthetic_delta
.InMilliseconds()) /
447 expected_proc_lifetime
.InMilliseconds();
449 const char *kHistogram
= "Sdch3.TimeWeightedMemoryUse";
450 tester
.ExpectTotalCount(kHistogram
, 0);
452 // Dictionary insertions and deletions:
453 // T = 0: insert d1 and d2
454 // T = 5: insert d3, which evicts d2
455 // T = 15: destroy SdchOwner, which evicts d1 and d3
456 // Therefore, d2's lifetime is synthetic_delta, d1's is synthetic_delta * 3,
457 // and d3's is synthetic_delta * 2. The expected_value_base variable is the
458 // base factor for d2's memory consumption, of which d1's and d3's are
460 test_clock
->Advance(synthetic_delta
* 2);
463 tester
.ExpectTotalCount(kHistogram
, 3);
464 tester
.ExpectBucketCount(kHistogram
, expected_value_base
, 1);
465 tester
.ExpectBucketCount(kHistogram
, expected_value_base
* 2, 1);
466 tester
.ExpectBucketCount(kHistogram
, expected_value_base
* 3, 1);
469 // Confirm auto-eviction happens if space is needed, with a more complicated
471 TEST_F(SdchOwnerTest
, ConfirmAutoEviction_2
) {
472 std::string server_hash_d1
;
473 std::string server_hash_d2
;
474 std::string server_hash_d3
;
476 // Add dictionaries, one recent, two more than a day in the past that
477 // between them add up to the space needed.
478 base::Time
fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
479 base::Time
stale(base::Time::Now() - base::TimeDelta::FromHours(25));
481 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
, fresh
));
484 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
, stale
));
486 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
, stale
));
488 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
489 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
490 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
492 std::string server_hash_d4
;
494 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d4
, fresh
));
495 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
496 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
497 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3
));
498 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4
));
501 // Confirm if only one dictionary needs to be evicted it's the oldest.
502 TEST_F(SdchOwnerTest
, ConfirmAutoEviction_Oldest
) {
503 std::string server_hash_d1
;
504 std::string server_hash_d2
;
505 std::string server_hash_d3
;
507 // Add dictionaries, one recent, one two days in the past, and one
508 // four days in the past.
509 base::Time
fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
510 base::Time
stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
511 base::Time
stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
514 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
516 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
519 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
522 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
523 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
524 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
526 // The addition of a new dictionary should succeed, evicting only the
529 std::string server_hash_d4
;
531 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d4
, fresh
));
532 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
533 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
534 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3
));
535 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4
));
538 // Confirm using a dictionary changes eviction behavior properly.
539 TEST_F(SdchOwnerTest
, UseChangesEviction
) {
540 std::string server_hash_d1
;
541 std::string server_hash_d2
;
542 std::string server_hash_d3
;
544 // Add dictionaries, one recent, one two days in the past, and one
545 // four days in the past.
546 base::Time
fresh(base::Time::Now() - base::TimeDelta::FromHours(23));
547 base::Time
stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
548 base::Time
stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
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 oldest dictionary.
564 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3
);
566 // The addition of a new dictionary should succeed, evicting only the
568 std::string server_hash_d4
;
570 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d4
, fresh
));
571 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
572 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
573 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
574 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4
));
577 // Confirm using a dictionary can prevent the addition of a new dictionary.
578 TEST_F(SdchOwnerTest
, UsePreventsAddition
) {
579 std::string server_hash_d1
;
580 std::string server_hash_d2
;
581 std::string server_hash_d3
;
583 // Add dictionaries, one recent, one two days in the past, and one
584 // four days in the past.
585 base::Time
fresh(base::Time::Now() - base::TimeDelta::FromMinutes(30));
586 base::Time
stale_newer(base::Time::Now() - base::TimeDelta::FromHours(47));
587 base::Time
stale_older(base::Time::Now() - base::TimeDelta::FromHours(71));
590 CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d1
, fresh
));
592 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d2
,
595 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 4, &server_hash_d3
,
598 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
599 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
600 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
602 // Use the older dictionaries.
603 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2
);
604 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3
);
606 // The addition of a new dictionary should fail, not evicting anything.
607 std::string server_hash_d4
;
609 CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d4
, fresh
));
610 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
611 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2
));
612 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3
));
613 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4
));
616 // Confirm clear gets all the space back.
617 TEST_F(SdchOwnerTest
, ClearReturnsSpace
) {
618 std::string server_hash_d1
;
619 std::string server_hash_d2
;
621 // Take up all the space.
622 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d1
,
624 // Addition should fail.
625 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
627 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
628 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
629 sdch_manager().ClearData();
630 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1
));
631 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
633 // Addition should now succeed.
635 CreateAndAddDictionary(kMaxSizeForTesting
, nullptr, base::Time::Now()));
638 // Confirm memory pressure gets all the space back.
639 TEST_F(SdchOwnerTest
, MemoryPressureReturnsSpace
) {
640 std::string server_hash_d1
;
641 std::string server_hash_d2
;
643 // Take up all the space.
644 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d1
,
647 // Addition should fail.
648 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting
, &server_hash_d2
,
651 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1
));
652 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
654 base::MemoryPressureListener::NotifyMemoryPressure(
655 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
);
656 // The notification may have (implementation note: does :-}) use a PostTask,
657 // so we drain the local message queue. This should be safe (i.e. not have
658 // an inifinite number of messages) in a unit test.
659 base::RunLoop().RunUntilIdle();
661 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1
));
662 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2
));
664 // Addition should now succeed.
666 CreateAndAddDictionary(kMaxSizeForTesting
, nullptr, base::Time::Now()));
669 // Confirm that use of a pinned dictionary after its removal works properly.
670 TEST_F(SdchOwnerTest
, PinRemoveUse
) {
671 pref_store().SetInitializationCompleted();
672 sdch_owner().EnablePersistentStorage(&pref_store());
674 std::string server_hash_d1
;
675 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting
/ 2, &server_hash_d1
,
678 scoped_ptr
<SdchManager::DictionarySet
> return_set(
679 sdch_manager().GetDictionarySet(
680 GURL(std::string(generic_url
) + "/x.html")));
681 ASSERT_TRUE(return_set
.get());
682 EXPECT_TRUE(return_set
->GetDictionaryText(server_hash_d1
));
684 const base::Value
* result
= nullptr;
685 const base::DictionaryValue
* dict_result
= nullptr;
686 ASSERT_TRUE(pref_store().GetValue("SDCH", &result
));
687 ASSERT_TRUE(result
->GetAsDictionary(&dict_result
));
688 EXPECT_TRUE(dict_result
->Get("dictionaries", &result
));
689 EXPECT_TRUE(dict_result
->Get("dictionaries." + server_hash_d1
, &result
));
691 sdch_manager().ClearData();
693 ASSERT_TRUE(pref_store().GetValue("SDCH", &result
));
694 ASSERT_TRUE(result
->GetAsDictionary(&dict_result
));
695 EXPECT_TRUE(dict_result
->Get("dictionaries", &result
));
696 EXPECT_FALSE(dict_result
->Get("dictionaries." + server_hash_d1
, &result
));
698 scoped_ptr
<SdchManager::DictionarySet
> return_set2(
699 sdch_manager().GetDictionarySet(
700 GURL(std::string(generic_url
) + "/x.html")));
701 EXPECT_FALSE(return_set2
.get());
703 sdch_manager().OnDictionaryUsed(server_hash_d1
);
705 ASSERT_TRUE(pref_store().GetValue("SDCH", &result
));
706 ASSERT_TRUE(result
->GetAsDictionary(&dict_result
));
707 EXPECT_TRUE(dict_result
->Get("dictionaries", &result
));
708 EXPECT_FALSE(dict_result
->Get("dictionaries." + server_hash_d1
, &result
));
711 class SdchOwnerPersistenceTest
: public ::testing::Test
{
713 SdchOwnerPersistenceTest() : pref_store_(new TestingPrefStore()) {
714 pref_store_
->SetInitializationCompleted();
716 virtual ~SdchOwnerPersistenceTest() {}
722 void ResetOwner(bool delay
) {
723 // This has to be done first, since SdchOwner may be observing SdchManager,
724 // and SdchManager can't be destroyed with a live observer.
726 manager_
.reset(new SdchManager());
727 fetcher_
= new MockSdchDictionaryFetcher();
728 owner_
.reset(new SdchOwner(manager_
.get(),
729 &url_request_context_
));
730 owner_
->SetMaxTotalDictionarySize(SdchOwnerTest::kMaxSizeForTesting
);
731 owner_
->SetMinSpaceForDictionaryFetch(
732 SdchOwnerTest::kMinFetchSpaceForTesting
);
733 owner_
->SetFetcherForTesting(make_scoped_ptr(fetcher_
));
735 owner_
->EnablePersistentStorage(pref_store_
.get());
738 void InsertDictionaryForURL(const GURL
& url
, const std::string
& nonce
) {
739 owner_
->OnDictionaryFetched(base::Time::Now(), 1,
740 CreateDictionary(url
, nonce
),
741 url
, net_log_
, false);
744 bool CompleteLoadFromURL(const GURL
& url
, const std::string
& nonce
,
745 bool was_from_cache
) {
746 return fetcher_
->CompletePendingRequest(url
, CreateDictionary(url
, nonce
),
747 net_log_
, was_from_cache
);
750 std::string
CreateDictionary(const GURL
& url
, const std::string
& nonce
) {
752 dict
.append("Domain: ");
753 dict
.append(url
.host());
755 dict
.append(url
.spec());
761 BoundNetLog net_log_
;
762 scoped_refptr
<TestingPrefStore
> pref_store_
;
763 scoped_ptr
<SdchManager
> manager_
;
764 MockSdchDictionaryFetcher
* fetcher_
;
765 scoped_ptr
<SdchOwner
> owner_
;
766 TestURLRequestContext url_request_context_
;
769 // Test an empty persistence store.
770 TEST_F(SdchOwnerPersistenceTest
, Empty
) {
772 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
775 // Test a persistence store with an empty dictionary.
776 TEST_F(SdchOwnerPersistenceTest
, Persistent_EmptyDict
) {
777 pref_store_
->SetValue("SDCH", new base::DictionaryValue());
779 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
782 // Test a persistence store with a bad version number.
783 TEST_F(SdchOwnerPersistenceTest
, Persistent_BadVersion
) {
784 base::DictionaryValue
* sdch_dict
= new base::DictionaryValue();
785 sdch_dict
->SetInteger("version", 2);
786 pref_store_
->SetValue("SDCH", sdch_dict
);
789 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
792 // Test a persistence store with an empty dictionaries map.
793 TEST_F(SdchOwnerPersistenceTest
, Persistent_EmptyDictList
) {
794 base::DictionaryValue
* sdch_dict
= new base::DictionaryValue();
795 scoped_ptr
<base::DictionaryValue
> dicts(new base::DictionaryValue());
796 sdch_dict
->SetInteger("version", 1);
797 sdch_dict
->Set("dictionaries", dicts
.Pass());
798 pref_store_
->SetValue("SDCH", sdch_dict
);
801 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
804 TEST_F(SdchOwnerPersistenceTest
, OneDict
) {
805 const GURL
url("http://www.example.com/dict");
807 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
808 InsertDictionaryForURL(url
, "0");
809 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
812 EXPECT_EQ(0, owner_
->GetDictionaryCountForTesting());
813 EXPECT_TRUE(CompleteLoadFromURL(url
, "0", true));
814 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
817 TEST_F(SdchOwnerPersistenceTest
, TwoDicts
) {
818 const GURL
url0("http://www.example.com/dict0");
819 const GURL
url1("http://www.example.com/dict1");
821 InsertDictionaryForURL(url0
, "0");
822 InsertDictionaryForURL(url1
, "1");
825 EXPECT_TRUE(CompleteLoadFromURL(url0
, "0", true));
826 EXPECT_TRUE(CompleteLoadFromURL(url1
, "1", true));
827 EXPECT_EQ(2, owner_
->GetDictionaryCountForTesting());
828 EXPECT_TRUE(owner_
->HasDictionaryFromURLForTesting(url0
));
829 EXPECT_TRUE(owner_
->HasDictionaryFromURLForTesting(url1
));
832 TEST_F(SdchOwnerPersistenceTest
, OneGoodDictOneBadDict
) {
833 const GURL
url0("http://www.example.com/dict0");
834 const GURL
url1("http://www.example.com/dict1");
836 InsertDictionaryForURL(url0
, "0");
837 InsertDictionaryForURL(url1
, "1");
839 // Mutate the pref store a bit now. Clear the owner first, to ensure that the
840 // SdchOwner doesn't observe these changes and object. The manual dictionary
841 // manipulation is a bit icky.
843 base::DictionaryValue
* dict
= nullptr;
844 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url1
, nullptr, &dict
));
845 dict
->Remove("use_count", nullptr);
848 EXPECT_TRUE(CompleteLoadFromURL(url0
, "0", true));
849 EXPECT_FALSE(CompleteLoadFromURL(url1
, "1", true));
850 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
851 EXPECT_TRUE(owner_
->HasDictionaryFromURLForTesting(url0
));
852 EXPECT_FALSE(owner_
->HasDictionaryFromURLForTesting(url1
));
855 TEST_F(SdchOwnerPersistenceTest
, UsingDictionaryUpdatesUseCount
) {
856 const GURL
url("http://www.example.com/dict");
858 InsertDictionaryForURL(url
, "0");
864 base::DictionaryValue
* dict
= nullptr;
865 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url
, &hash
, &dict
));
866 ASSERT_TRUE(dict
->GetInteger("use_count", &old_count
));
870 ASSERT_TRUE(CompleteLoadFromURL(url
, "0", true));
871 owner_
->OnDictionaryUsed(manager_
.get(), hash
);
876 base::DictionaryValue
* dict
= nullptr;
877 ASSERT_TRUE(GetDictionaryForURL(pref_store_
.get(), url
, nullptr, &dict
));
878 ASSERT_TRUE(dict
->GetInteger("use_count", &new_count
));
881 EXPECT_EQ(old_count
+ 1, new_count
);
884 TEST_F(SdchOwnerPersistenceTest
, LoadingDictionaryMerges
) {
885 const GURL
url0("http://www.example.com/dict0");
886 const GURL
url1("http://www.example.com/dict1");
889 InsertDictionaryForURL(url1
, "1");
892 InsertDictionaryForURL(url0
, "0");
893 EXPECT_EQ(1, owner_
->GetDictionaryCountForTesting());
894 owner_
->EnablePersistentStorage(pref_store_
.get());
895 ASSERT_TRUE(CompleteLoadFromURL(url1
, "1", true));
896 EXPECT_EQ(2, owner_
->GetDictionaryCountForTesting());
899 TEST_F(SdchOwnerPersistenceTest
, PersistenceMetrics
) {
900 const GURL
url0("http://www.example.com/dict0");
901 const GURL
url1("http://www.example.com/dict1");
904 InsertDictionaryForURL(url0
, "0");
905 InsertDictionaryForURL(url1
, "1");
909 base::HistogramTester tester
;
911 EXPECT_TRUE(CompleteLoadFromURL(url0
, "0", true));
912 EXPECT_TRUE(CompleteLoadFromURL(url1
, "1", false));
914 tester
.ExpectTotalCount("Sdch3.NetworkBytesSpent", 1);
915 tester
.ExpectUniqueSample("Sdch3.NetworkBytesSpent",
916 CreateDictionary(url1
, "1").size(), 1);