Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(398)

Side by Side Diff: net/sdch/sdch_owner_unittest.cc

Issue 841883002: Add an eviction mechanism for SDCH dictionaries. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: git cl format Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "base/memory/memory_pressure_listener.h"
6 #include "base/run_loop.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/test/simple_test_clock.h"
9 #include "net/base/net_log.h"
10 #include "net/base/sdch_manager.h"
11 #include "net/sdch/sdch_owner.h"
12 #include "net/url_request/url_request.h"
13 #include "net/url_request/url_request_context.h"
14 #include "net/url_request/url_request_error_job.h"
15 #include "net/url_request/url_request_job.h"
16 #include "net/url_request/url_request_job_factory.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace net {
20
21 static const char* generic_url = "http://www.example.com";
22 static const char* generic_domain = "www.example.com";
23
24 static std::string NewSdchDictionary(size_t dictionary_size) {
25 std::string dictionary;
26 dictionary.append("Domain: ");
27 dictionary.append(generic_domain);
28 dictionary.append("\n");
29 dictionary.append("\n");
30
31 size_t original_dictionary_size = dictionary.size();
32 dictionary.resize(dictionary_size);
33 for (size_t i = original_dictionary_size; i < dictionary_size; ++i)
34 dictionary[i] = static_cast<char>((i % 127) + 1);
35
36 return dictionary;
37 }
38
39 int outstanding_url_request_error_counting_jobs = 0;
40 base::Closure* empty_url_request_jobs_callback = 0;
41
42 // Variation of URLRequestErrorJob to count number of outstanding
43 // instances and notify when that goes to zero.
44 class URLRequestErrorCountingJob : public URLRequestJob {
45 public:
46 URLRequestErrorCountingJob(URLRequest* request,
47 NetworkDelegate* network_delegate,
48 int error)
49 : URLRequestJob(request, network_delegate),
50 error_(error),
51 weak_factory_(this) {
52 ++outstanding_url_request_error_counting_jobs;
53 }
54
55 void Start() override {
56 base::MessageLoop::current()->PostTask(
57 FROM_HERE, base::Bind(&URLRequestErrorCountingJob::StartAsync,
58 weak_factory_.GetWeakPtr()));
59 }
60
61 private:
62 ~URLRequestErrorCountingJob() override {
63 --outstanding_url_request_error_counting_jobs;
64 if (0 == outstanding_url_request_error_counting_jobs &&
65 empty_url_request_jobs_callback) {
66 empty_url_request_jobs_callback->Run();
67 }
68 }
69
70 void StartAsync() {
71 NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, error_));
72 }
73
74 int error_;
75
76 base::WeakPtrFactory<URLRequestErrorCountingJob> weak_factory_;
77 };
78
79 static int error_jobs_created = 0;
80
81 class MockURLRequestJobFactory : public URLRequestJobFactory {
82 public:
83 MockURLRequestJobFactory() {}
84
85 ~MockURLRequestJobFactory() override {}
86
87 URLRequestJob* MaybeCreateJobWithProtocolHandler(
88 const std::string& scheme,
89 URLRequest* request,
90 NetworkDelegate* network_delegate) const override {
91 ++error_jobs_created;
92 return new URLRequestErrorCountingJob(request, network_delegate,
93 ERR_INTERNET_DISCONNECTED);
94 }
95
96 URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
97 NetworkDelegate* network_delegate,
98 const GURL& location) const override {
99 return nullptr;
100 }
101
102 URLRequestJob* MaybeInterceptResponse(
103 URLRequest* request,
104 NetworkDelegate* network_delegate) const override {
105 return nullptr;
106 }
107
108 bool IsHandledProtocol(const std::string& scheme) const override {
109 return scheme == "http";
110 };
111
112 bool IsHandledURL(const GURL& url) const override {
113 return url.SchemeIs("http");
114 }
115
116 bool IsSafeRedirectTarget(const GURL& location) const override {
117 return false;
118 }
119 };
120
121 class SdchOwnerTest : public testing::Test {
122 public:
123 static const size_t kMaxSizeForTesting = 1000 * 50;
124 static const size_t kMinFetchSpaceForTesting = 500;
125
126 SdchOwnerTest()
127 : last_jobs_created_(error_jobs_created),
128 dictionary_creation_index_(0),
129 sdch_owner_(&sdch_manager_, &url_request_context_) {
130 // Any jobs created on this context will immediately error,
131 // which leaves the test in control of signals to SdchOwner.
132 url_request_context_.set_job_factory(&job_factory_);
133
134 // Reduce sizes to reduce time for string operations.
135 sdch_owner_.SetMaxTotalDictionarySize(kMaxSizeForTesting);
136 sdch_owner_.SetMinSpaceForDictionaryFetch(kMinFetchSpaceForTesting);
137 }
138
139 SdchManager& sdch_manager() { return sdch_manager_; }
140 SdchOwner& sdch_owner() { return sdch_owner_; }
141 BoundNetLog& bound_net_log() { return net_log_; }
142
143 int JobsRecentlyCreated() {
144 int result = error_jobs_created - last_jobs_created_;
145 last_jobs_created_ = error_jobs_created;
146 return result;
147 }
148
149 bool DictionaryPresentInManager(const std::string& server_hash) {
150 // Presumes all tests use generic url.
151 SdchProblemCode tmp;
152 scoped_ptr<SdchManager::DictionarySet> set(
153 sdch_manager_.GetDictionarySetByHash(GURL(generic_url), server_hash,
154 &tmp));
155 return !!set.get();
156 }
157
158 void SignalGetDictionaryAndClearJobs(GURL request_url, GURL dictionary_url) {
159 sdch_owner().OnGetDictionary(&sdch_manager_, request_url, dictionary_url);
160 if (outstanding_url_request_error_counting_jobs == 0)
161 return;
162 base::RunLoop run_loop;
163 base::Closure quit_closure(run_loop.QuitClosure());
164 empty_url_request_jobs_callback = &quit_closure;
165 run_loop.Run();
166 empty_url_request_jobs_callback = NULL;
167 }
168
169 // Create a unique (by hash) dictionary of the given size,
170 // associate it with a unique URL, add it to the manager through
171 // SdchOwner::OnDictionaryFetched(), and return whether that
172 // addition was successful or not.
173 bool CreateAndAddDictionary(size_t size, std::string* server_hash_p) {
174 GURL dictionary_url(
175 base::StringPrintf("%s/d%d", generic_url, dictionary_creation_index_));
176 std::string dictionary_text(NewSdchDictionary(size - 4));
177 dictionary_text += base::StringPrintf("%04d", dictionary_creation_index_);
178 ++dictionary_creation_index_;
179 std::string client_hash;
180 std::string server_hash;
181 SdchManager::GenerateHash(dictionary_text, &client_hash, &server_hash);
182
183 if (DictionaryPresentInManager(server_hash))
184 return false;
185 sdch_owner().OnDictionaryFetched(dictionary_text, dictionary_url, net_log_);
186 if (server_hash_p)
187 *server_hash_p = server_hash;
188 return DictionaryPresentInManager(server_hash);
189 }
190
191 private:
192 int last_jobs_created_;
193 BoundNetLog net_log_;
194 int dictionary_creation_index_;
195
196 // The dependencies of these objects (sdch_owner_ -> {sdch_manager_,
197 // url_request_context_}, url_request_context_->job_factory_) require
198 // this order for correct destruction semantics.
199 MockURLRequestJobFactory job_factory_;
200 URLRequestContext url_request_context_;
201 SdchManager sdch_manager_;
202 SdchOwner sdch_owner_;
203 };
204
205 // Does OnGetDictionary result in a fetch when there's enough space, and not
206 // when there's not?
207 TEST_F(SdchOwnerTest, OnGetDictionary_Fetching) {
208 GURL request_url(std::string(generic_url) + "/r1");
209
210 // Fetch generated when empty.
211 GURL dict_url1(std::string(generic_url) + "/d1");
212 EXPECT_EQ(0, JobsRecentlyCreated());
213 SignalGetDictionaryAndClearJobs(request_url, dict_url1);
214 EXPECT_EQ(1, JobsRecentlyCreated());
215
216 // Fetch generated when half full.
217 GURL dict_url2(std::string(generic_url) + "/d2");
218 std::string dictionary1(NewSdchDictionary(kMaxSizeForTesting / 2));
219 sdch_owner().OnDictionaryFetched(dictionary1, dict_url1, bound_net_log());
220 EXPECT_EQ(0, JobsRecentlyCreated());
221 SignalGetDictionaryAndClearJobs(request_url, dict_url2);
222 EXPECT_EQ(1, JobsRecentlyCreated());
223
224 // Fetch not generated when close to completely full.
225 GURL dict_url3(std::string(generic_url) + "/d3");
226 std::string dictionary2(NewSdchDictionary(
227 (kMaxSizeForTesting / 2 - kMinFetchSpaceForTesting / 2)));
228 sdch_owner().OnDictionaryFetched(dictionary2, dict_url2, bound_net_log());
229 EXPECT_EQ(0, JobsRecentlyCreated());
230 SignalGetDictionaryAndClearJobs(request_url, dict_url3);
231 EXPECT_EQ(0, JobsRecentlyCreated());
232 }
233
234 // Make sure attempts to add dictionaries do what they should.
235 TEST_F(SdchOwnerTest, OnDictionaryFetched_Fetching) {
236 GURL request_url(std::string(generic_url) + "/r1");
237 std::string client_hash;
238 std::string server_hash;
239
240 // Add successful when empty.
241 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr));
242 EXPECT_EQ(0, JobsRecentlyCreated());
243
244 // Add successful when half full.
245 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr));
246 EXPECT_EQ(0, JobsRecentlyCreated());
247
248 // Add unsuccessful when full.
249 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, nullptr));
250 EXPECT_EQ(0, JobsRecentlyCreated());
251 }
252
253 // Confirm auto-eviction happens if space is needed.
254 TEST_F(SdchOwnerTest, ConfirmAutoEviction) {
255 std::string server_hash_d1;
256 std::string server_hash_d2;
257 std::string server_hash_d3;
258
259 // Add two dictionaries, one recent, one more than a day in the past.
260 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1));
261
262 scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
263 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2));
264 sdch_owner().SetClockForTesting(clock.Pass());
265
266 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d2));
267
268 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
269 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
270
271 // The addition of a new dictionary should succeed, evicting the old one.
272 clock.reset(new base::SimpleTestClock);
273 clock->SetNow(base::Time::Now());
274 sdch_owner().SetClockForTesting(clock.Pass());
275
276 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d3));
277 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
278 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
279 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
280 }
281
282 // Confirm auto-eviction happens if space is needed, with a more complicated
283 // situation
284 TEST_F(SdchOwnerTest, ConfirmAutoEviction_2) {
285 std::string server_hash_d1;
286 std::string server_hash_d2;
287 std::string server_hash_d3;
288
289 // Add dictionaries, one recent, two more than a day in the past that
290 // between them add up to the space needed.
291 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d1));
292
293 scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
294 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2));
295 sdch_owner().SetClockForTesting(clock.Pass());
296
297 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2));
298 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3));
299
300 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
301 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
302 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
303
304 // The addition of a new dictionary should succeed, evicting the old one.
305 clock.reset(new base::SimpleTestClock);
306 clock->SetNow(base::Time::Now());
307 sdch_owner().SetClockForTesting(clock.Pass());
308
309 std::string server_hash_d4;
310 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4));
311 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
312 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
313 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
314 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
315 }
316
317 // Confirm if only one dictionary needs to be evicted it's the oldest.
318 TEST_F(SdchOwnerTest, ConfirmAutoEviction_Oldest) {
319 std::string server_hash_d1;
320 std::string server_hash_d2;
321 std::string server_hash_d3;
322
323 // Add dictionaries, one recent, one two days in the past, and one
324 // four days in the past.
325 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1));
326
327 scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
328 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2));
329 sdch_owner().SetClockForTesting(clock.Pass());
330 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2));
331
332 clock.reset(new base::SimpleTestClock);
333 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4));
334 sdch_owner().SetClockForTesting(clock.Pass());
335 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3));
336
337 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
338 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
339 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
340
341 // The addition of a new dictionary should succeed, evicting only the
342 // oldest one.
343 clock.reset(new base::SimpleTestClock);
344 clock->SetNow(base::Time::Now());
345 sdch_owner().SetClockForTesting(clock.Pass());
346
347 std::string server_hash_d4;
348 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4));
349 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
350 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
351 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d3));
352 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
353 }
354
355 // Confirm using a dictionary changes eviction behavior properly.
356 TEST_F(SdchOwnerTest, UseChangesEviction) {
357 std::string server_hash_d1;
358 std::string server_hash_d2;
359 std::string server_hash_d3;
360
361 // Add dictionaries, one recent, one two days in the past, and one
362 // four days in the past.
363 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1));
364
365 scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
366 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2));
367 sdch_owner().SetClockForTesting(clock.Pass());
368 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2));
369
370 clock.reset(new base::SimpleTestClock);
371 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4));
372 sdch_owner().SetClockForTesting(clock.Pass());
373 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3));
374
375 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
376 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
377 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
378
379 clock.reset(new base::SimpleTestClock);
380 clock->SetNow(base::Time::Now());
381 sdch_owner().SetClockForTesting(clock.Pass());
382
383 // Use the oldest dictionary.
384 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
385
386 // The addition of a new dictionary should succeed, evicting only the
387 // newer stale one.
388 std::string server_hash_d4;
389 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4));
390 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
391 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
392 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
393 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d4));
394 }
395
396 // Confirm using a dictionary can prevent the addition of a new dictionary.
397 TEST_F(SdchOwnerTest, UsePreventsAddition) {
398 std::string server_hash_d1;
399 std::string server_hash_d2;
400 std::string server_hash_d3;
401
402 // Add dictionaries, one recent, one two days in the past, and one
403 // four days in the past.
404 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d1));
405
406 scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock);
407 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(2));
408 sdch_owner().SetClockForTesting(clock.Pass());
409 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d2));
410
411 clock.reset(new base::SimpleTestClock);
412 clock->SetNow(base::Time::Now() - base::TimeDelta::FromDays(4));
413 sdch_owner().SetClockForTesting(clock.Pass());
414 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting / 4, &server_hash_d3));
415
416 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
417 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
418 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
419
420 clock.reset(new base::SimpleTestClock);
421 clock->SetNow(base::Time::Now());
422 sdch_owner().SetClockForTesting(clock.Pass());
423
424 // Use the older dictionaries.
425 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d2);
426 sdch_owner().OnDictionaryUsed(&sdch_manager(), server_hash_d3);
427
428 // The addition of a new dictionary should fail, not evicting anything.
429 std::string server_hash_d4;
430 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting / 2, &server_hash_d4));
431 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
432 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d2));
433 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d3));
434 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d4));
435 }
436
437 // Confirm clear gets all the space back.
438 TEST_F(SdchOwnerTest, ClearReturnsSpace) {
439 std::string server_hash_d1;
440 std::string server_hash_d2;
441
442 // Take up all the space.
443 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1));
444
445 // Addition should fail.
446 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2));
447
448 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
449 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
450
451 sdch_manager().ClearData();
452 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
453 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
454
455 // Addition should now succeed.
456 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, nullptr));
457 }
458
459 // Confirm memory pressure gets all the space back.
460 TEST_F(SdchOwnerTest, MemoryPressureReturnsSpace) {
461 std::string server_hash_d1;
462 std::string server_hash_d2;
463
464 // Take up all the space.
465 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d1));
466
467 // Addition should fail.
468 EXPECT_FALSE(CreateAndAddDictionary(kMaxSizeForTesting, &server_hash_d2));
469
470 EXPECT_TRUE(DictionaryPresentInManager(server_hash_d1));
471 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
472
473 base::MemoryPressureListener::NotifyMemoryPressure(
474 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
475 // The notification may have (implementation note: does :-}) use a PostTask,
476 // so we drain the local message queue. This should be safe (i.e. not have
477 // an inifinite number of messages) in a unit test.
478 base::RunLoop().RunUntilIdle();
479
480 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d1));
481 EXPECT_FALSE(DictionaryPresentInManager(server_hash_d2));
482
483 // Addition should now succeed.
484 EXPECT_TRUE(CreateAndAddDictionary(kMaxSizeForTesting, nullptr));
485 }
486
487 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698