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
17namespace {
18  class ParsedCookieTest : public testing::Test { };
19  class CookieMonsterTest : public testing::Test { };
20}
21
22static const int kNumCookies = 20000;
23static const char kCookieLine[] = "A  = \"b=;\\\"\"  ;secure;;;";
24
25namespace net {
26
27TEST(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
37TEST(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
48static const GURL kUrlGoogle("http://www.google.izzle");
49
50TEST(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
77TEST(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
105static int CountInString(const std::string& str, char c) {
106  return std::count(str.begin(), str.end(), c);
107}
108
109TEST(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
163TEST(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
202TEST(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
237TEST(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.
252TEST(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