1 // Copyright 2013 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 <string> 6 #include <vector> 7 8 #include "base/logging.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_vector.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/time/time.h" 13 #include "chrome/browser/net/evicted_domain_cookie_counter.h" 14 #include "net/cookies/canonical_cookie.h" 15 #include "net/cookies/cookie_monster.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "url/gurl.h" 18 19 namespace chrome_browser_net { 20 21 using base::Time; 22 using base::TimeDelta; 23 24 namespace { 25 26 const char* google_url1 = "http://www.google.com"; 27 const char* google_url2 = "http://mail.google.com"; 28 const char* other_url1 = "http://www.example.com"; 29 const char* other_url2 = "http://www.example.co.uk"; 30 31 class EvictedDomainCookieCounterTest : public testing::Test { 32 protected: 33 class MockDelegate : public EvictedDomainCookieCounter::Delegate { 34 public: 35 explicit MockDelegate(EvictedDomainCookieCounterTest* tester); 36 37 // EvictedDomainCookieCounter::Delegate implementation. 38 virtual void Report( 39 const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie, 40 const Time& reinstatement_time) OVERRIDE; 41 virtual Time CurrentTime() const OVERRIDE; 42 43 private: 44 EvictedDomainCookieCounterTest* tester_; 45 }; 46 47 EvictedDomainCookieCounterTest(); 48 virtual ~EvictedDomainCookieCounterTest(); 49 50 // testing::Test implementation. 51 virtual void SetUp() OVERRIDE; 52 virtual void TearDown() OVERRIDE; 53 54 // Initialization that allows parameters to be specified. 55 void InitCounter(size_t max_size, size_t purge_count); 56 57 // Wrapper to allocate new cookie and store it in |cookies_|. 58 // If |max_age| == 0, then the cookie does not expire. 59 void CreateNewCookie( 60 const char* url, const std::string& cookie_line, int64 max_age); 61 62 // Clears |cookies_| and creates common cookies for multiple tests. 63 void InitStockCookies(); 64 65 // Sets simulation time to |rel_time|. 66 void GotoTime(int64 rel_time); 67 68 // Simulates time-passage by |delta_second|. 69 void StepTime(int64 delta_second); 70 71 // Simulates cookie addition or update. 72 void Add(net::CanonicalCookie* cookie); 73 74 // Simulates cookie removal. 75 void Remove(net::CanonicalCookie* cookie); 76 77 // Simulates cookie eviction. 78 void Evict(net::CanonicalCookie* cookie); 79 80 // For semi-realism, time considered are relative to |mock_time_base_|. 81 Time mock_time_base_; 82 Time mock_time_; 83 84 // To store allocated cookies for reuse. 85 ScopedVector<net::CanonicalCookie> cookies_; 86 87 scoped_refptr<EvictedDomainCookieCounter> cookie_counter_; 88 89 // Statistics as comma-separated string of duration (in seconds) between 90 // eviction and reinstatement for each cookie, in the order of eviction. 91 std::string google_stat_; 92 std::string other_stat_; 93 }; 94 95 EvictedDomainCookieCounterTest::MockDelegate::MockDelegate( 96 EvictedDomainCookieCounterTest* tester) 97 : tester_(tester) {} 98 99 void EvictedDomainCookieCounterTest::MockDelegate::Report( 100 const EvictedDomainCookieCounter::EvictedCookie& evicted_cookie, 101 const Time& reinstatement_time) { 102 std::string& dest = evicted_cookie.is_google ? 103 tester_->google_stat_ : tester_->other_stat_; 104 if (!dest.empty()) 105 dest.append(","); 106 TimeDelta delta(reinstatement_time - evicted_cookie.eviction_time); 107 dest.append(base::Int64ToString(delta.InSeconds())); 108 } 109 110 Time EvictedDomainCookieCounterTest::MockDelegate::CurrentTime() const { 111 return tester_->mock_time_; 112 } 113 114 EvictedDomainCookieCounterTest::EvictedDomainCookieCounterTest() {} 115 116 EvictedDomainCookieCounterTest::~EvictedDomainCookieCounterTest() {} 117 118 void EvictedDomainCookieCounterTest::SetUp() { 119 mock_time_base_ = Time::Now() - TimeDelta::FromHours(1); 120 mock_time_ = mock_time_base_; 121 } 122 123 void EvictedDomainCookieCounterTest::TearDown() { 124 } 125 126 void EvictedDomainCookieCounterTest::InitCounter(size_t max_size, 127 size_t purge_count) { 128 scoped_ptr<MockDelegate> cookie_counter_delegate(new MockDelegate(this)); 129 cookie_counter_ = new EvictedDomainCookieCounter( 130 NULL, 131 cookie_counter_delegate.PassAs<EvictedDomainCookieCounter::Delegate>(), 132 max_size, 133 purge_count); 134 } 135 136 void EvictedDomainCookieCounterTest::CreateNewCookie( 137 const char* url, const std::string& cookie_line, int64 max_age) { 138 std::string line(cookie_line); 139 if (max_age) 140 line.append(";max-age=" + base::Int64ToString(max_age)); 141 net::CanonicalCookie* cookie = net::CanonicalCookie::Create( 142 GURL(url), line, mock_time_, net::CookieOptions()); 143 DCHECK(cookie); 144 cookies_.push_back(cookie); 145 } 146 147 void EvictedDomainCookieCounterTest::InitStockCookies() { 148 cookies_.clear(); 149 CreateNewCookie(google_url1, "a1=1", 3000); // cookies_[0]. 150 CreateNewCookie(google_url2, "a2=1", 2000); // cookies_[1]. 151 CreateNewCookie(other_url1, "a1=1", 1000); // cookies_[2]. 152 CreateNewCookie(other_url1, "a2=1", 1001); // cookies_[3]. 153 CreateNewCookie(google_url1, "a1=1;Path=/sub", 999); // cookies_[4]. 154 CreateNewCookie(other_url2, "a2=1", 0); // cookies_[5]. 155 } 156 157 void EvictedDomainCookieCounterTest::GotoTime(int64 rel_time) { 158 mock_time_ = mock_time_base_ + TimeDelta::FromSeconds(rel_time); 159 } 160 161 void EvictedDomainCookieCounterTest::StepTime(int64 delta_second) { 162 mock_time_ += TimeDelta::FromSeconds(delta_second); 163 } 164 165 void EvictedDomainCookieCounterTest::Add(net::CanonicalCookie* cookie) { 166 cookie_counter_->OnCookieChanged( 167 *cookie, false, net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT); 168 } 169 170 void EvictedDomainCookieCounterTest::Remove(net::CanonicalCookie* cookie) { 171 cookie_counter_->OnCookieChanged( 172 *cookie, true, net::CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT); 173 } 174 175 void EvictedDomainCookieCounterTest::Evict(net::CanonicalCookie* cookie) { 176 cookie_counter_->OnCookieChanged( 177 *cookie, true, net::CookieMonster::Delegate::CHANGE_COOKIE_EVICTED); 178 } 179 180 // EvictedDomainCookieCounter takes (and owns) a CookieMonster::Delegate for 181 // chaining. To ensure that the chaining indeed occurs, we implement a 182 // dummy CookieMonster::Delegate to increment an integer. 183 TEST_F(EvictedDomainCookieCounterTest, TestChain) { 184 int result = 0; 185 186 class ChangedDelegateDummy : public net::CookieMonster::Delegate { 187 public: 188 explicit ChangedDelegateDummy(int* result) : result_(result) {} 189 190 virtual void OnCookieChanged(const net::CanonicalCookie& cookie, 191 bool removed, 192 ChangeCause cause) OVERRIDE { 193 ++(*result_); 194 } 195 196 virtual void OnLoaded() OVERRIDE {} 197 198 private: 199 virtual ~ChangedDelegateDummy() {} 200 201 int* result_; 202 }; 203 204 scoped_ptr<MockDelegate> cookie_counter_delegate(new MockDelegate(this)); 205 cookie_counter_ = new EvictedDomainCookieCounter( 206 new ChangedDelegateDummy(&result), 207 cookie_counter_delegate.PassAs<EvictedDomainCookieCounter::Delegate>(), 208 10, 209 5); 210 InitStockCookies(); 211 // Perform 6 cookie transactions. 212 for (int i = 0; i < 6; ++i) { 213 Add(cookies_[i]); 214 StepTime(1); 215 Evict(cookies_[i]); 216 StepTime(1); 217 Remove(cookies_[i]); 218 } 219 EXPECT_EQ(18, result); // 6 cookies x 3 operations each. 220 } 221 222 // Basic flow: add cookies, evict, then reinstate. 223 TEST_F(EvictedDomainCookieCounterTest, TestBasicFlow) { 224 InitCounter(10, 4); 225 InitStockCookies(); 226 // Add all cookies at (relative time) t = 0. 227 for (int i = 0; i < 6; ++i) 228 Add(cookies_[i]); 229 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); // No activities on add. 230 EXPECT_EQ(";", google_stat_ + ";" + other_stat_); 231 // Evict cookies at t = [1,3,6,10,15,21]. 232 for (int i = 0; i < 6; ++i) { 233 StepTime(i + 1); 234 Evict(cookies_[i]); 235 } 236 EXPECT_EQ(6u, cookie_counter_->GetStorageSize()); // Storing all evictions. 237 EXPECT_EQ(";", google_stat_ + ";" + other_stat_); 238 // Reinstate cookies at t = [22,23,24,25,26,27]. 239 for (int i = 0; i < 6; ++i) { 240 StepTime(1); 241 Add(cookies_[i]); 242 } 243 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); // Everything is removed. 244 // Expected reinstatement delays: [21,20,18,15,11,6]. 245 EXPECT_EQ("21,20,11;18,15,6", google_stat_ + ";" + other_stat_); 246 } 247 248 // Removed cookies are ignored by EvictedDomainCookieCounter. 249 TEST_F(EvictedDomainCookieCounterTest, TestRemove) { 250 InitCounter(10, 4); 251 InitStockCookies(); 252 // Add all cookies at (relative time) t = 0. 253 for (int i = 0; i < 6; ++i) 254 Add(cookies_[i]); 255 // Remove cookies at t = [1,3,6,10,15,21]. 256 for (int i = 0; i < 6; ++i) { 257 StepTime(i + 1); 258 Remove(cookies_[i]); 259 } 260 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); 261 // Add cookies again at t = [22,23,24,25,26,27]. 262 for (int i = 0; i < 5; ++i) { 263 StepTime(1); 264 Add(cookies_[i]); 265 } 266 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); 267 // No cookies were evicted, so no reinstatement take place. 268 EXPECT_EQ(";", google_stat_ + ";" + other_stat_); 269 } 270 271 // Expired cookies should not be counted by EvictedDomainCookieCounter. 272 TEST_F(EvictedDomainCookieCounterTest, TestExpired) { 273 InitCounter(10, 4); 274 InitStockCookies(); 275 // Add all cookies at (relative time) t = 0. 276 for (int i = 0; i < 6; ++i) 277 Add(cookies_[i]); 278 // Evict cookies at t = [1,3,6,10,15,21]. 279 for (int i = 0; i < 6; ++i) { 280 StepTime(i + 1); 281 Evict(cookies_[i]); 282 } 283 EXPECT_EQ(6u, cookie_counter_->GetStorageSize()); 284 GotoTime(1000); // t = 1000, so cookies_[2,4] expire. 285 286 // Reinstate cookies at t = [1000,1000,(1000),1000,(1000),1000]. 287 InitStockCookies(); // Refresh cookies, so new cookies expire in the future. 288 for (int i = 0; i < 6; ++i) 289 Add(cookies_[i]); 290 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); 291 // Reinstatement delays: [999,997,(994),990,(985),979]. 292 EXPECT_EQ("999,997;990,979", google_stat_ + ";" + other_stat_); 293 } 294 295 // Garbage collection should remove the oldest evicted cookies. 296 TEST_F(EvictedDomainCookieCounterTest, TestGarbageCollection) { 297 InitCounter(4, 2); // Reduced capacity. 298 InitStockCookies(); 299 // Add all cookies at (relative time) t = 0. 300 for (int i = 0; i < 6; ++i) 301 Add(cookies_[i]); 302 // Evict cookies at t = [1,3,6,10]. 303 for (int i = 0; i < 4; ++i) { 304 StepTime(i + 1); 305 Evict(cookies_[i]); 306 } 307 EXPECT_EQ(4u, cookie_counter_->GetStorageSize()); // Reached capacity. 308 StepTime(5); 309 Evict(cookies_[4]); // Evict at t = 15, garbage collection takes place. 310 EXPECT_EQ(2u, cookie_counter_->GetStorageSize()); 311 StepTime(6); 312 Evict(cookies_[5]); // Evict at t = 21. 313 EXPECT_EQ(3u, cookie_counter_->GetStorageSize()); 314 EXPECT_EQ(";", google_stat_ + ";" + other_stat_); 315 // Reinstate cookies at t = [(100),(100),(100),100,100,100]. 316 GotoTime(100); 317 for (int i = 0; i < 6; ++i) 318 Add(cookies_[i]); 319 // Expected reinstatement delays: [(99),(97),(94),90,85,79] 320 EXPECT_EQ("85;90,79", google_stat_ + ";" + other_stat_); 321 } 322 323 // Garbage collection should remove the specified number of evicted cookies 324 // even when there are ties amongst oldest evicted cookies. 325 TEST_F(EvictedDomainCookieCounterTest, TestGarbageCollectionTie) { 326 InitCounter(9, 3); 327 // Add 10 cookies at time [0,1,3,6,...,45] 328 for (int i = 0; i < 10; ++i) { 329 StepTime(i); 330 CreateNewCookie(google_url1, "a" + base::IntToString(i) + "=1", 3000); 331 Add(cookies_[i]); 332 } 333 // Evict 6 cookies at t = [100,...,100]. 334 GotoTime(100); 335 for (int i = 0; i < 6; ++i) 336 Evict(cookies_[i]); 337 EXPECT_EQ(6u, cookie_counter_->GetStorageSize()); 338 // Evict 3 cookies at t = [210,220,230]. 339 GotoTime(200); 340 for (int i = 6; i < 9; ++i) { 341 StepTime(10); 342 Evict(cookies_[i]); 343 } 344 EXPECT_EQ(9u, cookie_counter_->GetStorageSize()); // Reached capacity. 345 // Evict 1 cookie at t = 300, and garbage collection takes place. 346 GotoTime(300); 347 Evict(cookies_[9]); 348 // Some arbitrary 4 out of 6 cookies evicted at t = 100 are gone from storage. 349 EXPECT_EQ(6u, cookie_counter_->GetStorageSize()); // 10 - 4. 350 // Reinstate cookies at t = [400,...,400]. 351 GotoTime(400); 352 for (int i = 0; i < 10; ++i) 353 Add(cookies_[i]); 354 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); 355 // Expected reinstatement delays: 356 // [300,300,300,300,300,300 <= keeping 2 only,190,180,170,100]. 357 EXPECT_EQ("300,300,190,180,170,100;", google_stat_ + ";" + other_stat_); 358 } 359 360 // Garbage collection prioritize removal of expired cookies. 361 TEST_F(EvictedDomainCookieCounterTest, TestGarbageCollectionWithExpiry) { 362 InitCounter(5, 1); 363 InitStockCookies(); 364 // Add all cookies at (relative time) t = 0. 365 for (int i = 0; i < 6; ++i) 366 Add(cookies_[i]); 367 // Evict cookies at t = [1,3,6,10,15]. 368 for (int i = 0; i < 5; ++i) { 369 StepTime(i + 1); 370 Evict(cookies_[i]); 371 } 372 EXPECT_EQ(5u, cookie_counter_->GetStorageSize()); // Reached capacity. 373 GotoTime(1200); // t = 1200, so cookies_[2,3,4] expire. 374 // Evict cookies_[5] (not expired) at t = 1200. 375 Evict(cookies_[5]); 376 // Garbage collection would have taken place, removing 3 expired cookies, 377 // so that there's no need to remove more. 378 EXPECT_EQ(3u, cookie_counter_->GetStorageSize()); 379 // Reinstate cookies at t = [1500,1500,(1500),(1500),(1500),1500]. 380 GotoTime(1500); 381 InitStockCookies(); // Refresh cookies, so new cookies expire in the future. 382 for (int i = 0; i < 6; ++i) 383 Add(cookies_[i]); 384 EXPECT_EQ(0u, cookie_counter_->GetStorageSize()); 385 // Reinstatement delays: [1499,1497,(1494),(1490),(1485),300]. 386 EXPECT_EQ("1499,1497;300", google_stat_ + ";" + other_stat_); 387 } 388 389 } // namespace 390 391 } // namespace chrome_browser_net 392