1 // Copyright (c) 2012 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.
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/test/perf_time_logger.h"
12 #include "net/cookies/canonical_cookie.h"
13 #include "net/cookies/cookie_monster.h"
14 #include "net/cookies/cookie_monster_store_test.h"
15 #include "net/cookies/parsed_cookie.h"
16 #include "testing/gtest/include/gtest/gtest.h"
23 const int kNumCookies
= 20000;
24 const char kCookieLine
[] = "A = \"b=;\\\"\" ;secure;;;";
25 const char kGoogleURL
[] = "http://www.google.izzle";
27 int CountInString(const std::string
& str
, char c
) {
28 return std::count(str
.begin(), str
.end(), c
);
31 class CookieMonsterTest
: public testing::Test
{
33 CookieMonsterTest() : message_loop_(new base::MessageLoopForIO()) {}
36 scoped_ptr
<base::MessageLoop
> message_loop_
;
41 BaseCallback() : has_run_(false) {}
44 void WaitForCallback() {
45 // Note that the performance tests currently all operate on a loaded cookie
46 // store (or, more precisely, one that has no backing persistent store).
47 // Therefore, callbacks will actually always complete synchronously. If the
48 // tests get more advanced we need to add other means of signaling
50 base::MessageLoop::current()->RunUntilIdle();
51 EXPECT_TRUE(has_run_
);
62 class SetCookieCallback
: public BaseCallback
{
65 CookieMonster
* cm
, const GURL
& gurl
, const std::string
& cookie
) {
66 cm
->SetCookieWithOptionsAsync(gurl
, cookie
, options_
, base::Bind(
67 &SetCookieCallback::Run
, base::Unretained(this)));
71 void Run(bool success
) {
75 net::CookieOptions options_
;
78 class GetCookiesCallback
: public BaseCallback
{
80 const std::string
& GetCookies(CookieMonster
* cm
, const GURL
& gurl
) {
81 cm
->GetCookiesWithOptionsAsync(gurl
, options_
, base::Bind(
82 &GetCookiesCallback::Run
, base::Unretained(this)));
88 void Run(const std::string
& cookies
) {
93 net::CookieOptions options_
;
98 TEST(ParsedCookieTest
, TestParseCookies
) {
99 std::string
cookie(kCookieLine
);
100 base::PerfTimeLogger
timer("Parsed_cookie_parse_cookies");
101 for (int i
= 0; i
< kNumCookies
; ++i
) {
102 ParsedCookie
pc(cookie
);
103 EXPECT_TRUE(pc
.IsValid());
108 TEST(ParsedCookieTest
, TestParseBigCookies
) {
109 std::string
cookie(3800, 'z');
110 cookie
+= kCookieLine
;
111 base::PerfTimeLogger
timer("Parsed_cookie_parse_big_cookies");
112 for (int i
= 0; i
< kNumCookies
; ++i
) {
113 ParsedCookie
pc(cookie
);
114 EXPECT_TRUE(pc
.IsValid());
119 TEST_F(CookieMonsterTest
, TestAddCookiesOnSingleHost
) {
120 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
121 std::vector
<std::string
> cookies
;
122 for (int i
= 0; i
< kNumCookies
; i
++) {
123 cookies
.push_back(base::StringPrintf("a%03d=b", i
));
126 SetCookieCallback setCookieCallback
;
128 // Add a bunch of cookies on a single host
129 base::PerfTimeLogger
timer("Cookie_monster_add_single_host");
131 for (std::vector
<std::string
>::const_iterator it
= cookies
.begin();
132 it
!= cookies
.end(); ++it
) {
133 setCookieCallback
.SetCookie(cm
.get(), GURL(kGoogleURL
), *it
);
137 GetCookiesCallback getCookiesCallback
;
139 base::PerfTimeLogger
timer2("Cookie_monster_query_single_host");
140 for (std::vector
<std::string
>::const_iterator it
= cookies
.begin();
141 it
!= cookies
.end(); ++it
) {
142 getCookiesCallback
.GetCookies(cm
.get(), GURL(kGoogleURL
));
146 base::PerfTimeLogger
timer3("Cookie_monster_deleteall_single_host");
147 cm
->DeleteAllAsync(CookieMonster::DeleteCallback());
148 base::MessageLoop::current()->RunUntilIdle();
152 TEST_F(CookieMonsterTest
, TestAddCookieOnManyHosts
) {
153 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
154 std::string
cookie(kCookieLine
);
155 std::vector
<GURL
> gurls
; // just wanna have ffffuunnn
156 for (int i
= 0; i
< kNumCookies
; ++i
) {
157 gurls
.push_back(GURL(base::StringPrintf("https://a%04d.izzle", i
)));
160 SetCookieCallback setCookieCallback
;
162 // Add a cookie on a bunch of host
163 base::PerfTimeLogger
timer("Cookie_monster_add_many_hosts");
164 for (std::vector
<GURL
>::const_iterator it
= gurls
.begin();
165 it
!= gurls
.end(); ++it
) {
166 setCookieCallback
.SetCookie(cm
.get(), *it
, cookie
);
170 GetCookiesCallback getCookiesCallback
;
172 base::PerfTimeLogger
timer2("Cookie_monster_query_many_hosts");
173 for (std::vector
<GURL
>::const_iterator it
= gurls
.begin();
174 it
!= gurls
.end(); ++it
) {
175 getCookiesCallback
.GetCookies(cm
.get(), *it
);
179 base::PerfTimeLogger
timer3("Cookie_monster_deleteall_many_hosts");
180 cm
->DeleteAllAsync(CookieMonster::DeleteCallback());
181 base::MessageLoop::current()->RunUntilIdle();
185 TEST_F(CookieMonsterTest
, TestDomainTree
) {
186 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
187 GetCookiesCallback getCookiesCallback
;
188 SetCookieCallback setCookieCallback
;
189 const char domain_cookie_format_tree
[] = "a=b; domain=%s";
190 const std::string
domain_base("top.com");
192 std::vector
<std::string
> domain_list
;
194 // Create a balanced binary tree of domains on which the cookie is set.
195 domain_list
.push_back(domain_base
);
196 for (int i1
= 0; i1
< 2; i1
++) {
197 std::string
domain_base_1((i1
? "a." : "b.") + domain_base
);
198 EXPECT_EQ("top.com", cm
->GetKey(domain_base_1
));
199 domain_list
.push_back(domain_base_1
);
200 for (int i2
= 0; i2
< 2; i2
++) {
201 std::string
domain_base_2((i2
? "a." : "b.") + domain_base_1
);
202 EXPECT_EQ("top.com", cm
->GetKey(domain_base_2
));
203 domain_list
.push_back(domain_base_2
);
204 for (int i3
= 0; i3
< 2; i3
++) {
205 std::string
domain_base_3((i3
? "a." : "b.") + domain_base_2
);
206 EXPECT_EQ("top.com", cm
->GetKey(domain_base_3
));
207 domain_list
.push_back(domain_base_3
);
208 for (int i4
= 0; i4
< 2; i4
++) {
209 std::string
domain_base_4((i4
? "a." : "b.") + domain_base_3
);
210 EXPECT_EQ("top.com", cm
->GetKey(domain_base_4
));
211 domain_list
.push_back(domain_base_4
);
218 EXPECT_EQ(31u, domain_list
.size());
219 for (std::vector
<std::string
>::const_iterator it
= domain_list
.begin();
220 it
!= domain_list
.end(); it
++) {
221 GURL
gurl("https://" + *it
+ "/");
222 const std::string cookie
= base::StringPrintf(domain_cookie_format_tree
,
224 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie
);
226 EXPECT_EQ(31u, cm
->GetAllCookies().size());
228 GURL
probe_gurl("https://b.a.b.a.top.com/");
229 std::string cookie_line
= getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
230 EXPECT_EQ(5, CountInString(cookie_line
, '='))
231 << "Cookie line: " << cookie_line
;
232 base::PerfTimeLogger
timer("Cookie_monster_query_domain_tree");
233 for (int i
= 0; i
< kNumCookies
; i
++) {
234 getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
239 TEST_F(CookieMonsterTest
, TestDomainLine
) {
240 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
241 SetCookieCallback setCookieCallback
;
242 GetCookiesCallback getCookiesCallback
;
243 std::vector
<std::string
> domain_list
;
244 GURL
probe_gurl("https://b.a.b.a.top.com/");
245 std::string cookie_line
;
247 // Create a line of 32 domain cookies such that all cookies stored
248 // by effective TLD+1 will apply to probe GURL.
249 // (TLD + 1 is the level above .com/org/net/etc, e.g. "top.com"
250 // or "google.com". "Effective" is added to include sites like
251 // bbc.co.uk, where the effetive TLD+1 is more than one level
252 // below the top level.)
253 domain_list
.push_back("a.top.com");
254 domain_list
.push_back("b.a.top.com");
255 domain_list
.push_back("a.b.a.top.com");
256 domain_list
.push_back("b.a.b.a.top.com");
257 EXPECT_EQ(4u, domain_list
.size());
259 const char domain_cookie_format_line
[] = "a%03d=b; domain=%s";
260 for (int i
= 0; i
< 8; i
++) {
261 for (std::vector
<std::string
>::const_iterator it
= domain_list
.begin();
262 it
!= domain_list
.end(); it
++) {
263 GURL
gurl("https://" + *it
+ "/");
264 const std::string cookie
= base::StringPrintf(domain_cookie_format_line
,
266 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie
);
270 cookie_line
= getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
271 EXPECT_EQ(32, CountInString(cookie_line
, '='));
272 base::PerfTimeLogger
timer2("Cookie_monster_query_domain_line");
273 for (int i
= 0; i
< kNumCookies
; i
++) {
274 getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
279 TEST_F(CookieMonsterTest
, TestImport
) {
280 scoped_refptr
<MockPersistentCookieStore
> store(new MockPersistentCookieStore
);
281 std::vector
<CanonicalCookie
*> initial_cookies
;
282 GetCookiesCallback getCookiesCallback
;
284 // We want to setup a fairly large backing store, with 300 domains of 50
285 // cookies each. Creation times must be unique.
286 int64
time_tick(base::Time::Now().ToInternalValue());
288 for (int domain_num
= 0; domain_num
< 300; domain_num
++) {
289 std::string
domain_name(base::StringPrintf(".Domain_%d.com", domain_num
));
290 std::string
gurl("www" + domain_name
);
291 for (int cookie_num
= 0; cookie_num
< 50; cookie_num
++) {
292 std::string
cookie_line(base::StringPrintf("Cookie_%d=1; Path=/",
294 AddCookieToList(gurl
, cookie_line
,
295 base::Time::FromInternalValue(time_tick
++),
300 store
->SetLoadExpectation(true, initial_cookies
);
302 scoped_refptr
<CookieMonster
> cm(new CookieMonster(store
.get(), NULL
));
304 // Import will happen on first access.
305 GURL
gurl("www.google.com");
306 CookieOptions options
;
307 base::PerfTimeLogger
timer("Cookie_monster_import_from_store");
308 getCookiesCallback
.GetCookies(cm
.get(), gurl
);
311 // Just confirm keys were set as expected.
312 EXPECT_EQ("domain_1.com", cm
->GetKey("www.Domain_1.com"));
315 TEST_F(CookieMonsterTest
, TestGetKey
) {
316 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
317 base::PerfTimeLogger
timer("Cookie_monster_get_key");
318 for (int i
= 0; i
< kNumCookies
; i
++)
319 cm
->GetKey("www.google.com");
323 // This test is probing for whether garbage collection happens when it
324 // shouldn't. This will not in general be visible functionally, since
325 // if GC runs twice in a row without any change to the store, the second
326 // GC run will not do anything the first one didn't. That's why this is
327 // a performance test. The test should be considered to pass if all the
328 // times reported are approximately the same--this indicates that no GC
329 // happened repeatedly for any case.
330 TEST_F(CookieMonsterTest
, TestGCTimes
) {
331 SetCookieCallback setCookieCallback
;
333 const struct TestCase
{
334 const char* const name
;
336 size_t num_old_cookies
;
339 // A whole lot of recent cookies; gc shouldn't happen.
341 CookieMonster::kMaxCookies
* 2,
344 // Some old cookies, but still overflowing max.
346 CookieMonster::kMaxCookies
* 2,
347 CookieMonster::kMaxCookies
/ 2,
349 // Old cookies enough to bring us right down to our purge line.
351 CookieMonster::kMaxCookies
* 2,
352 CookieMonster::kMaxCookies
+ CookieMonster::kPurgeCookies
+ 1,
355 // Old cookies enough to bring below our purge line (which we
357 CookieMonster::kMaxCookies
* 2,
358 CookieMonster::kMaxCookies
* 3 / 4,
360 "less_than_gc_thresh",
361 // Few enough cookies that gc shouldn't happen at all.
362 CookieMonster::kMaxCookies
- 5,
366 for (int ci
= 0; ci
< static_cast<int>(arraysize(test_cases
)); ++ci
) {
367 const TestCase
& test_case(test_cases
[ci
]);
368 scoped_refptr
<CookieMonster
> cm(
369 CreateMonsterFromStoreForGC(
370 test_case
.num_cookies
, test_case
.num_old_cookies
,
371 CookieMonster::kSafeFromGlobalPurgeDays
* 2));
373 GURL
gurl("http://google.com");
374 std::string
cookie_line("z=3");
375 // Trigger the Garbage collection we're allowed.
376 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie_line
);
378 base::PerfTimeLogger
timer((std::string("GC_") + test_case
.name
).c_str());
379 for (int i
= 0; i
< kNumCookies
; i
++)
380 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie_line
);