1 // Copyright (c) 2010 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 <algorithm>
6
7 #include "net/base/cookie_monster.h"
8
9 #include "base/perftimer.h"
10 #include "base/string_util.h"
11 #include "base/stringprintf.h"
12 #include "googleurl/src/gurl.h"
13 #include "net/base/cookie_monster.h"
14 #include "net/base/cookie_monster_store_test.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace {
18 class ParsedCookieTest : public testing::Test { };
19 class CookieMonsterTest : public testing::Test { };
20 }
21
22 static const int kNumCookies = 20000;
23 static const char kCookieLine[] = "A = \"b=;\\\"\" ;secure;;;";
24
25 namespace net {
26
TEST(ParsedCookieTest,TestParseCookies)27 TEST(ParsedCookieTest, TestParseCookies) {
28 std::string cookie(kCookieLine);
29 PerfTimeLogger timer("Parsed_cookie_parse_cookies");
30 for (int i = 0; i < kNumCookies; ++i) {
31 CookieMonster::ParsedCookie pc(cookie);
32 EXPECT_TRUE(pc.IsValid());
33 }
34 timer.Done();
35 }
36
TEST(ParsedCookieTest,TestParseBigCookies)37 TEST(ParsedCookieTest, TestParseBigCookies) {
38 std::string cookie(3800, 'z');
39 cookie += kCookieLine;
40 PerfTimeLogger timer("Parsed_cookie_parse_big_cookies");
41 for (int i = 0; i < kNumCookies; ++i) {
42 CookieMonster::ParsedCookie pc(cookie);
43 EXPECT_TRUE(pc.IsValid());
44 }
45 timer.Done();
46 }
47
48 static const GURL kUrlGoogle("http://www.google.izzle");
49
TEST(CookieMonsterTest,TestAddCookiesOnSingleHost)50 TEST(CookieMonsterTest, TestAddCookiesOnSingleHost) {
51 scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
52 std::vector<std::string> cookies;
53 for (int i = 0; i < kNumCookies; i++) {
54 cookies.push_back(base::StringPrintf("a%03d=b", i));
55 }
56
57 // Add a bunch of cookies on a single host
58 PerfTimeLogger timer("Cookie_monster_add_single_host");
59 for (std::vector<std::string>::const_iterator it = cookies.begin();
60 it != cookies.end(); ++it) {
61 EXPECT_TRUE(cm->SetCookie(kUrlGoogle, *it));
62 }
63 timer.Done();
64
65 PerfTimeLogger timer2("Cookie_monster_query_single_host");
66 for (std::vector<std::string>::const_iterator it = cookies.begin();
67 it != cookies.end(); ++it) {
68 cm->GetCookies(kUrlGoogle);
69 }
70 timer2.Done();
71
72 PerfTimeLogger timer3("Cookie_monster_deleteall_single_host");
73 cm->DeleteAll(false);
74 timer3.Done();
75 }
76
TEST(CookieMonsterTest,TestAddCookieOnManyHosts)77 TEST(CookieMonsterTest, TestAddCookieOnManyHosts) {
78 scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
79 std::string cookie(kCookieLine);
80 std::vector<GURL> gurls; // just wanna have ffffuunnn
81 for (int i = 0; i < kNumCookies; ++i) {
82 gurls.push_back(GURL(base::StringPrintf("http://a%04d.izzle", i)));
83 }
84
85 // Add a cookie on a bunch of host
86 PerfTimeLogger timer("Cookie_monster_add_many_hosts");
87 for (std::vector<GURL>::const_iterator it = gurls.begin();
88 it != gurls.end(); ++it) {
89 EXPECT_TRUE(cm->SetCookie(*it, cookie));
90 }
91 timer.Done();
92
93 PerfTimeLogger timer2("Cookie_monster_query_many_hosts");
94 for (std::vector<GURL>::const_iterator it = gurls.begin();
95 it != gurls.end(); ++it) {
96 cm->GetCookies(*it);
97 }
98 timer2.Done();
99
100 PerfTimeLogger timer3("Cookie_monster_deleteall_many_hosts");
101 cm->DeleteAll(false);
102 timer3.Done();
103 }
104
CountInString(const std::string & str,char c)105 static int CountInString(const std::string& str, char c) {
106 return std::count(str.begin(), str.end(), c);
107 }
108
TEST(CookieMonsterTest,TestDomainTree)109 TEST(CookieMonsterTest, TestDomainTree) {
110 scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
111 const char* domain_cookie_format_tree = "a=b; domain=%s";
112 const std::string domain_base("top.com");
113
114 std::vector<std::string> domain_list;
115
116 // Create a balanced binary tree of domains on which the cookie is set.
117 domain_list.push_back(domain_base);
118 for (int i1 = 0; i1 < 2; i1++) {
119 std::string domain_base_1((i1 ? "a." : "b.") + domain_base);
120 EXPECT_EQ("top.com", cm->GetKey(domain_base_1));
121 domain_list.push_back(domain_base_1);
122 for (int i2 = 0; i2 < 2; i2++) {
123 std::string domain_base_2((i2 ? "a." : "b.") + domain_base_1);
124 EXPECT_EQ("top.com", cm->GetKey(domain_base_2));
125 domain_list.push_back(domain_base_2);
126 for (int i3 = 0; i3 < 2; i3++) {
127 std::string domain_base_3((i3 ? "a." : "b.") + domain_base_2);
128 EXPECT_EQ("top.com", cm->GetKey(domain_base_3));
129 domain_list.push_back(domain_base_3);
130 for (int i4 = 0; i4 < 2; i4++) {
131 std::string domain_base_4((i4 ? "a." : "b.") + domain_base_3);
132 EXPECT_EQ("top.com", cm->GetKey(domain_base_4));
133 domain_list.push_back(domain_base_4);
134 }
135 }
136 }
137 }
138
139
140 EXPECT_EQ(31u, domain_list.size());
141 for (std::vector<std::string>::const_iterator it = domain_list.begin();
142 it != domain_list.end(); it++) {
143 GURL gurl("https://" + *it + "/");
144 const std::string cookie = base::StringPrintf(domain_cookie_format_tree,
145 it->c_str());
146 EXPECT_TRUE(cm->SetCookie(gurl, cookie));
147 }
148 EXPECT_EQ(31u, cm->GetAllCookies().size());
149
150 GURL probe_gurl("https://b.a.b.a.top.com/");
151 std::string cookie_line;
152 cookie_line = cm->GetCookies(probe_gurl);
153 EXPECT_EQ(5, CountInString(cookie_line, '=')) << "Cookie line: "
154 << cookie_line;
155 PerfTimeLogger timer("Cookie_monster_query_domain_tree");
156 for (int i = 0; i < kNumCookies; i++) {
157 cm->GetCookies(probe_gurl);
158 }
159 timer.Done();
160
161 }
162
TEST(CookieMonsterTest,TestDomainLine)163 TEST(CookieMonsterTest, TestDomainLine) {
164 scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
165 std::vector<std::string> domain_list;
166 GURL probe_gurl("https://b.a.b.a.top.com/");
167 std::string cookie_line;
168
169 // Create a line of 32 domain cookies such that all cookies stored
170 // by effective TLD+1 will apply to probe GURL.
171 // (TLD + 1 is the level above .com/org/net/etc, e.g. "top.com"
172 // or "google.com". "Effective" is added to include sites like
173 // bbc.co.uk, where the effetive TLD+1 is more than one level
174 // below the top level.)
175 domain_list.push_back("a.top.com");
176 domain_list.push_back("b.a.top.com");
177 domain_list.push_back("a.b.a.top.com");
178 domain_list.push_back("b.a.b.a.top.com");
179 EXPECT_EQ(4u, domain_list.size());
180
181 const char* domain_cookie_format_line = "a%03d=b; domain=%s";
182 for (int i = 0; i < 8; i++) {
183 for (std::vector<std::string>::const_iterator it = domain_list.begin();
184 it != domain_list.end(); it++) {
185 GURL gurl("https://" + *it + "/");
186 const std::string cookie = base::StringPrintf(domain_cookie_format_line,
187 i, it->c_str());
188 EXPECT_TRUE(cm->SetCookie(gurl, cookie));
189 }
190 }
191 EXPECT_EQ(32u, cm->GetAllCookies().size());
192
193 cookie_line = cm->GetCookies(probe_gurl);
194 EXPECT_EQ(32, CountInString(cookie_line, '='));
195 PerfTimeLogger timer2("Cookie_monster_query_domain_line");
196 for (int i = 0; i < kNumCookies; i++) {
197 cm->GetCookies(probe_gurl);
198 }
199 timer2.Done();
200 }
201
TEST(CookieMonsterTest,TestImport)202 TEST(CookieMonsterTest, TestImport) {
203 scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
204 std::vector<CookieMonster::CanonicalCookie*> initial_cookies;
205
206 // We want to setup a fairly large backing store, with 300 domains of 50
207 // cookies each. Creation times must be unique.
208 int64 time_tick(base::Time::Now().ToInternalValue());
209
210 for (int domain_num = 0; domain_num < 300; domain_num++) {
211 std::string domain_name(base::StringPrintf(".Domain_%d.com", domain_num));
212 std::string gurl("www" + domain_name);
213 for (int cookie_num = 0; cookie_num < 50; cookie_num++) {
214 std::string cookie_line(base::StringPrintf("Cookie_%d=1; Path=/",
215 cookie_num));
216 AddCookieToList(gurl, cookie_line,
217 base::Time::FromInternalValue(time_tick++),
218 &initial_cookies);
219 }
220 }
221
222 store->SetLoadExpectation(true, initial_cookies);
223
224 scoped_refptr<CookieMonster> cm(new CookieMonster(store, NULL));
225
226 // Import will happen on first access.
227 GURL gurl("www.google.com");
228 CookieOptions options;
229 PerfTimeLogger timer("Cookie_monster_import_from_store");
230 cm->GetCookiesWithOptions(gurl, options);
231 timer.Done();
232
233 // Just confirm keys were set as expected.
234 EXPECT_EQ("domain_1.com", cm->GetKey("www.Domain_1.com"));
235 }
236
TEST(CookieMonsterTest,TestGetKey)237 TEST(CookieMonsterTest, TestGetKey) {
238 scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
239 PerfTimeLogger timer("Cookie_monster_get_key");
240 for (int i = 0; i < kNumCookies; i++)
241 cm->GetKey("www.google.com");
242 timer.Done();
243 }
244
245 // This test is probing for whether garbage collection happens when it
246 // shouldn't. This will not in general be visible functionally, since
247 // if GC runs twice in a row without any change to the store, the second
248 // GC run will not do anything the first one didn't. That's why this is
249 // a performance test. The test should be considered to pass if all the
250 // times reported are approximately the same--this indicates that no GC
251 // happened repeatedly for any case.
TEST(CookieMonsterTest,TestGCTimes)252 TEST(CookieMonsterTest, TestGCTimes) {
253 const struct TestCase {
254 const char* name;
255 int num_cookies;
256 int num_old_cookies;
257 } test_cases[] = {
258 {
259 // A whole lot of recent cookies; gc shouldn't happen.
260 "all_recent",
261 CookieMonster::kMaxCookies * 2,
262 0,
263 }, {
264 // Some old cookies, but still overflowing max.
265 "mostly_recent",
266 CookieMonster::kMaxCookies * 2,
267 CookieMonster::kMaxCookies / 2,
268 }, {
269 // Old cookies enough to bring us right down to our purge line.
270 "balanced",
271 CookieMonster::kMaxCookies * 2,
272 CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
273 }, {
274 "mostly_old",
275 // Old cookies enough to bring below our purge line (which we
276 // shouldn't do).
277 CookieMonster::kMaxCookies * 2,
278 CookieMonster::kMaxCookies * 3 / 4,
279 }, {
280 "less_than_gc_thresh",
281 // Few enough cookies that gc shouldn't happen at all.
282 CookieMonster::kMaxCookies - 5,
283 0,
284 },
285 };
286 for (int ci = 0; ci < static_cast<int>(ARRAYSIZE_UNSAFE(test_cases)); ++ci) {
287 const TestCase& test_case(test_cases[ci]);
288 scoped_refptr<CookieMonster> cm(
289 CreateMonsterFromStoreForGC(
290 test_case.num_cookies, test_case.num_old_cookies,
291 CookieMonster::kSafeFromGlobalPurgeDays * 2));
292
293 GURL gurl("http://google.com");
294 std::string cookie_line("z=3");
295 // Trigger the Garbage collection we're allowed.
296 EXPECT_TRUE(cm->SetCookie(gurl, cookie_line));
297
298 PerfTimeLogger timer((std::string("GC_") + test_case.name).c_str());
299 for (int i = 0; i < kNumCookies; i++)
300 EXPECT_TRUE(cm->SetCookie(gurl, cookie_line));
301 timer.Done();
302 }
303 }
304
305 } // namespace
306