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_
);
55 void Run() { has_run_
= true; }
60 class SetCookieCallback
: public BaseCallback
{
62 void SetCookie(CookieMonster
* cm
,
64 const std::string
& cookie
) {
65 cm
->SetCookieWithOptionsAsync(
66 gurl
, cookie
, options_
,
67 base::Bind(&SetCookieCallback::Run
, base::Unretained(this)));
72 void Run(bool success
) {
76 CookieOptions options_
;
79 class GetCookiesCallback
: public BaseCallback
{
81 const std::string
& GetCookies(CookieMonster
* cm
, const GURL
& gurl
) {
82 cm
->GetCookiesWithOptionsAsync(
84 base::Bind(&GetCookiesCallback::Run
, base::Unretained(this)));
90 void Run(const std::string
& cookies
) {
95 CookieOptions options_
;
100 TEST(ParsedCookieTest
, TestParseCookies
) {
101 std::string
cookie(kCookieLine
);
102 base::PerfTimeLogger
timer("Parsed_cookie_parse_cookies");
103 for (int i
= 0; i
< kNumCookies
; ++i
) {
104 ParsedCookie
pc(cookie
);
105 EXPECT_TRUE(pc
.IsValid());
110 TEST(ParsedCookieTest
, TestParseBigCookies
) {
111 std::string
cookie(3800, 'z');
112 cookie
+= kCookieLine
;
113 base::PerfTimeLogger
timer("Parsed_cookie_parse_big_cookies");
114 for (int i
= 0; i
< kNumCookies
; ++i
) {
115 ParsedCookie
pc(cookie
);
116 EXPECT_TRUE(pc
.IsValid());
121 TEST_F(CookieMonsterTest
, TestAddCookiesOnSingleHost
) {
122 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
123 std::vector
<std::string
> cookies
;
124 for (int i
= 0; i
< kNumCookies
; i
++) {
125 cookies
.push_back(base::StringPrintf("a%03d=b", i
));
128 SetCookieCallback setCookieCallback
;
130 // Add a bunch of cookies on a single host
131 base::PerfTimeLogger
timer("Cookie_monster_add_single_host");
133 for (std::vector
<std::string
>::const_iterator it
= cookies
.begin();
134 it
!= cookies
.end(); ++it
) {
135 setCookieCallback
.SetCookie(cm
.get(), GURL(kGoogleURL
), *it
);
139 GetCookiesCallback getCookiesCallback
;
141 base::PerfTimeLogger
timer2("Cookie_monster_query_single_host");
142 for (std::vector
<std::string
>::const_iterator it
= cookies
.begin();
143 it
!= cookies
.end(); ++it
) {
144 getCookiesCallback
.GetCookies(cm
.get(), GURL(kGoogleURL
));
148 base::PerfTimeLogger
timer3("Cookie_monster_deleteall_single_host");
149 cm
->DeleteAllAsync(CookieMonster::DeleteCallback());
150 base::MessageLoop::current()->RunUntilIdle();
154 TEST_F(CookieMonsterTest
, TestAddCookieOnManyHosts
) {
155 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
156 std::string
cookie(kCookieLine
);
157 std::vector
<GURL
> gurls
; // just wanna have ffffuunnn
158 for (int i
= 0; i
< kNumCookies
; ++i
) {
159 gurls
.push_back(GURL(base::StringPrintf("https://a%04d.izzle", i
)));
162 SetCookieCallback setCookieCallback
;
164 // Add a cookie on a bunch of host
165 base::PerfTimeLogger
timer("Cookie_monster_add_many_hosts");
166 for (std::vector
<GURL
>::const_iterator it
= gurls
.begin(); it
!= gurls
.end();
168 setCookieCallback
.SetCookie(cm
.get(), *it
, cookie
);
172 GetCookiesCallback getCookiesCallback
;
174 base::PerfTimeLogger
timer2("Cookie_monster_query_many_hosts");
175 for (std::vector
<GURL
>::const_iterator it
= gurls
.begin(); it
!= gurls
.end();
177 getCookiesCallback
.GetCookies(cm
.get(), *it
);
181 base::PerfTimeLogger
timer3("Cookie_monster_deleteall_many_hosts");
182 cm
->DeleteAllAsync(CookieMonster::DeleteCallback());
183 base::MessageLoop::current()->RunUntilIdle();
187 TEST_F(CookieMonsterTest
, TestDomainTree
) {
188 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
189 GetCookiesCallback getCookiesCallback
;
190 SetCookieCallback setCookieCallback
;
191 const char domain_cookie_format_tree
[] = "a=b; domain=%s";
192 const std::string
domain_base("top.com");
194 std::vector
<std::string
> domain_list
;
196 // Create a balanced binary tree of domains on which the cookie is set.
197 domain_list
.push_back(domain_base
);
198 for (int i1
= 0; i1
< 2; i1
++) {
199 std::string
domain_base_1((i1
? "a." : "b.") + domain_base
);
200 EXPECT_EQ("top.com", cm
->GetKey(domain_base_1
));
201 domain_list
.push_back(domain_base_1
);
202 for (int i2
= 0; i2
< 2; i2
++) {
203 std::string
domain_base_2((i2
? "a." : "b.") + domain_base_1
);
204 EXPECT_EQ("top.com", cm
->GetKey(domain_base_2
));
205 domain_list
.push_back(domain_base_2
);
206 for (int i3
= 0; i3
< 2; i3
++) {
207 std::string
domain_base_3((i3
? "a." : "b.") + domain_base_2
);
208 EXPECT_EQ("top.com", cm
->GetKey(domain_base_3
));
209 domain_list
.push_back(domain_base_3
);
210 for (int i4
= 0; i4
< 2; i4
++) {
211 std::string
domain_base_4((i4
? "a." : "b.") + domain_base_3
);
212 EXPECT_EQ("top.com", cm
->GetKey(domain_base_4
));
213 domain_list
.push_back(domain_base_4
);
219 EXPECT_EQ(31u, domain_list
.size());
220 for (std::vector
<std::string
>::const_iterator it
= domain_list
.begin();
221 it
!= domain_list
.end(); it
++) {
222 GURL
gurl("https://" + *it
+ "/");
223 const std::string cookie
=
224 base::StringPrintf(domain_cookie_format_tree
, it
->c_str());
225 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie
);
227 EXPECT_EQ(31u, cm
->GetAllCookies().size());
229 GURL
probe_gurl("https://b.a.b.a.top.com/");
230 std::string cookie_line
= getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
231 EXPECT_EQ(5, CountInString(cookie_line
, '='))
232 << "Cookie line: " << cookie_line
;
233 base::PerfTimeLogger
timer("Cookie_monster_query_domain_tree");
234 for (int i
= 0; i
< kNumCookies
; i
++) {
235 getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
240 TEST_F(CookieMonsterTest
, TestDomainLine
) {
241 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
242 SetCookieCallback setCookieCallback
;
243 GetCookiesCallback getCookiesCallback
;
244 std::vector
<std::string
> domain_list
;
245 GURL
probe_gurl("https://b.a.b.a.top.com/");
246 std::string cookie_line
;
248 // Create a line of 32 domain cookies such that all cookies stored
249 // by effective TLD+1 will apply to probe GURL.
250 // (TLD + 1 is the level above .com/org/net/etc, e.g. "top.com"
251 // or "google.com". "Effective" is added to include sites like
252 // bbc.co.uk, where the effetive TLD+1 is more than one level
253 // below the top level.)
254 domain_list
.push_back("a.top.com");
255 domain_list
.push_back("b.a.top.com");
256 domain_list
.push_back("a.b.a.top.com");
257 domain_list
.push_back("b.a.b.a.top.com");
258 EXPECT_EQ(4u, domain_list
.size());
260 const char domain_cookie_format_line
[] = "a%03d=b; domain=%s";
261 for (int i
= 0; i
< 8; i
++) {
262 for (std::vector
<std::string
>::const_iterator it
= domain_list
.begin();
263 it
!= domain_list
.end(); it
++) {
264 GURL
gurl("https://" + *it
+ "/");
265 const std::string cookie
=
266 base::StringPrintf(domain_cookie_format_line
, i
, it
->c_str());
267 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie
);
271 cookie_line
= getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
272 EXPECT_EQ(32, CountInString(cookie_line
, '='));
273 base::PerfTimeLogger
timer2("Cookie_monster_query_domain_line");
274 for (int i
= 0; i
< kNumCookies
; i
++) {
275 getCookiesCallback
.GetCookies(cm
.get(), probe_gurl
);
280 TEST_F(CookieMonsterTest
, TestImport
) {
281 scoped_refptr
<MockPersistentCookieStore
> store(new MockPersistentCookieStore
);
282 std::vector
<CanonicalCookie
*> initial_cookies
;
283 GetCookiesCallback getCookiesCallback
;
285 // We want to setup a fairly large backing store, with 300 domains of 50
286 // cookies each. Creation times must be unique.
287 int64
time_tick(base::Time::Now().ToInternalValue());
289 for (int domain_num
= 0; domain_num
< 300; domain_num
++) {
290 std::string
domain_name(base::StringPrintf(".Domain_%d.com", domain_num
));
291 std::string
gurl("www" + domain_name
);
292 for (int cookie_num
= 0; cookie_num
< 50; cookie_num
++) {
293 std::string
cookie_line(
294 base::StringPrintf("Cookie_%d=1; Path=/", cookie_num
));
295 AddCookieToList(gurl
, cookie_line
,
296 base::Time::FromInternalValue(time_tick
++),
301 store
->SetLoadExpectation(true, initial_cookies
);
303 scoped_refptr
<CookieMonster
> cm(new CookieMonster(store
.get(), NULL
));
305 // Import will happen on first access.
306 GURL
gurl("www.google.com");
307 CookieOptions options
;
308 base::PerfTimeLogger
timer("Cookie_monster_import_from_store");
309 getCookiesCallback
.GetCookies(cm
.get(), gurl
);
312 // Just confirm keys were set as expected.
313 EXPECT_EQ("domain_1.com", cm
->GetKey("www.Domain_1.com"));
316 TEST_F(CookieMonsterTest
, TestGetKey
) {
317 scoped_refptr
<CookieMonster
> cm(new CookieMonster(NULL
, NULL
));
318 base::PerfTimeLogger
timer("Cookie_monster_get_key");
319 for (int i
= 0; i
< kNumCookies
; i
++)
320 cm
->GetKey("www.google.com");
324 // This test is probing for whether garbage collection happens when it
325 // shouldn't. This will not in general be visible functionally, since
326 // if GC runs twice in a row without any change to the store, the second
327 // GC run will not do anything the first one didn't. That's why this is
328 // a performance test. The test should be considered to pass if all the
329 // times reported are approximately the same--this indicates that no GC
330 // happened repeatedly for any case.
331 TEST_F(CookieMonsterTest
, TestGCTimes
) {
332 SetCookieCallback setCookieCallback
;
334 const struct TestCase
{
335 const char* const name
;
337 size_t num_old_cookies
;
340 // A whole lot of recent cookies; gc shouldn't happen.
342 CookieMonster::kMaxCookies
* 2,
346 // Some old cookies, but still overflowing max.
348 CookieMonster::kMaxCookies
* 2,
349 CookieMonster::kMaxCookies
/ 2,
352 // Old cookies enough to bring us right down to our purge line.
354 CookieMonster::kMaxCookies
* 2,
355 CookieMonster::kMaxCookies
+ CookieMonster::kPurgeCookies
+ 1,
359 // Old cookies enough to bring below our purge line (which we
361 CookieMonster::kMaxCookies
* 2,
362 CookieMonster::kMaxCookies
* 3 / 4,
365 "less_than_gc_thresh",
366 // Few enough cookies that gc shouldn't happen at all.
367 CookieMonster::kMaxCookies
- 5,
371 for (int ci
= 0; ci
< static_cast<int>(arraysize(test_cases
)); ++ci
) {
372 const TestCase
& test_case(test_cases
[ci
]);
373 scoped_refptr
<CookieMonster
> cm(CreateMonsterFromStoreForGC(
374 test_case
.num_cookies
, test_case
.num_old_cookies
,
375 CookieMonster::kSafeFromGlobalPurgeDays
* 2));
377 GURL
gurl("http://google.com");
378 std::string
cookie_line("z=3");
379 // Trigger the Garbage collection we're allowed.
380 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie_line
);
382 base::PerfTimeLogger
timer((std::string("GC_") + test_case
.name
).c_str());
383 for (int i
= 0; i
< kNumCookies
; i
++)
384 setCookieCallback
.SetCookie(cm
.get(), gurl
, cookie_line
);