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.
5 #include "content/browser/net/sqlite_persistent_cookie_store.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/test/sequenced_worker_pool_owner.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "base/time/time.h"
22 #include "net/cookies/canonical_cookie.h"
23 #include "net/cookies/cookie_constants.h"
24 #include "sql/connection.h"
25 #include "sql/meta_table.h"
26 #include "testing/gtest/include/gtest/gtest.h"
33 const base::FilePath::CharType kCookieFilename[] = FILE_PATH_LITERAL("Cookies");
37 typedef std::vector<net::CanonicalCookie*> CanonicalCookieVector;
39 class SQLitePersistentCookieStoreTest : public testing::Test {
41 SQLitePersistentCookieStoreTest()
42 : pool_owner_(new base::SequencedWorkerPoolOwner(3, "Background Pool")),
43 loaded_event_(false, false),
44 key_loaded_event_(false, false),
45 db_thread_event_(false, false) {
48 void OnLoaded(const CanonicalCookieVector& cookies) {
50 loaded_event_.Signal();
53 void OnKeyLoaded(const CanonicalCookieVector& cookies) {
55 key_loaded_event_.Signal();
58 void Load(CanonicalCookieVector* cookies) {
59 EXPECT_FALSE(loaded_event_.IsSignaled());
60 store_->Load(base::Bind(&SQLitePersistentCookieStoreTest::OnLoaded,
61 base::Unretained(this)));
67 base::WaitableEvent event(false, false);
68 store_->Flush(base::Bind(&base::WaitableEvent::Signal,
69 base::Unretained(&event)));
73 scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
74 return pool_owner_->pool()->GetSequencedTaskRunner(
75 pool_owner_->pool()->GetNamedSequenceToken("background"));
78 scoped_refptr<base::SequencedTaskRunner> client_task_runner() {
79 return pool_owner_->pool()->GetSequencedTaskRunner(
80 pool_owner_->pool()->GetNamedSequenceToken("client"));
85 // Make sure we wait until the destructor has run by shutting down the pool
86 // resetting the owner (whose destructor blocks on the pool completion).
87 pool_owner_->pool()->Shutdown();
88 // Create a new pool for the few tests that create multiple stores. In other
89 // cases this is wasted but harmless.
90 pool_owner_.reset(new base::SequencedWorkerPoolOwner(3, "Background Pool"));
93 void CreateAndLoad(bool restore_old_session_cookies,
94 CanonicalCookieVector* cookies) {
95 store_ = new SQLitePersistentCookieStore(
96 temp_dir_.path().Append(kCookieFilename),
98 background_task_runner(),
99 restore_old_session_cookies,
104 void InitializeStore(bool restore_old_session_cookies) {
105 CanonicalCookieVector cookies;
106 CreateAndLoad(restore_old_session_cookies, &cookies);
107 EXPECT_EQ(0U, cookies.size());
110 // We have to create this method to wrap WaitableEvent::Wait, since we cannot
111 // bind a non-void returning method as a Closure.
112 void WaitOnDBEvent() {
113 db_thread_event_.Wait();
116 // Adds a persistent cookie to store_.
117 void AddCookie(const std::string& name,
118 const std::string& value,
119 const std::string& domain,
120 const std::string& path,
121 const base::Time& creation) {
123 net::CanonicalCookie(GURL(), name, value, domain, path, creation,
124 creation, creation, false, false,
125 net::COOKIE_PRIORITY_DEFAULT));
128 virtual void SetUp() OVERRIDE {
129 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
132 virtual void TearDown() OVERRIDE {
134 pool_owner_->pool()->Shutdown();
138 base::MessageLoop main_loop_;
139 scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
140 base::WaitableEvent loaded_event_;
141 base::WaitableEvent key_loaded_event_;
142 base::WaitableEvent db_thread_event_;
143 CanonicalCookieVector cookies_;
144 base::ScopedTempDir temp_dir_;
145 scoped_refptr<SQLitePersistentCookieStore> store_;
148 TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) {
149 InitializeStore(false);
150 AddCookie("A", "B", "foo.bar", "/", base::Time::Now());
153 // Load up the store and verify that it has good data in it.
154 CanonicalCookieVector cookies;
155 CreateAndLoad(false, &cookies);
156 ASSERT_EQ(1U, cookies.size());
157 ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
158 ASSERT_STREQ("A", cookies[0]->Name().c_str());
159 ASSERT_STREQ("B", cookies[0]->Value().c_str());
161 STLDeleteElements(&cookies);
163 // Now corrupt the meta table.
166 ASSERT_TRUE(db.Open(temp_dir_.path().Append(kCookieFilename)));
167 sql::MetaTable meta_table_;
168 meta_table_.Init(&db, 1, 1);
169 ASSERT_TRUE(db.Execute("DELETE FROM meta"));
173 // Upon loading, the database should be reset to a good, blank state.
174 CreateAndLoad(false, &cookies);
175 ASSERT_EQ(0U, cookies.size());
177 // Verify that, after, recovery, the database persists properly.
178 AddCookie("X", "Y", "foo.bar", "/", base::Time::Now());
180 CreateAndLoad(false, &cookies);
181 ASSERT_EQ(1U, cookies.size());
182 ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
183 ASSERT_STREQ("X", cookies[0]->Name().c_str());
184 ASSERT_STREQ("Y", cookies[0]->Value().c_str());
185 STLDeleteElements(&cookies);
188 // Test if data is stored as expected in the SQLite database.
189 TEST_F(SQLitePersistentCookieStoreTest, TestPersistance) {
190 InitializeStore(false);
191 AddCookie("A", "B", "foo.bar", "/", base::Time::Now());
192 // Replace the store effectively destroying the current one and forcing it
193 // to write its data to disk. Then we can see if after loading it again it
196 // Reload and test for persistence
197 CanonicalCookieVector cookies;
198 CreateAndLoad(false, &cookies);
199 ASSERT_EQ(1U, cookies.size());
200 ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
201 ASSERT_STREQ("A", cookies[0]->Name().c_str());
202 ASSERT_STREQ("B", cookies[0]->Value().c_str());
204 // Now delete the cookie and check persistence again.
205 store_->DeleteCookie(*cookies[0]);
207 STLDeleteElements(&cookies);
209 // Reload and check if the cookie has been removed.
210 CreateAndLoad(false, &cookies);
211 ASSERT_EQ(0U, cookies.size());
214 // Test that priority load of cookies for a specfic domain key could be
215 // completed before the entire store is loaded
216 TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) {
217 InitializeStore(false);
218 base::Time t = base::Time::Now();
219 AddCookie("A", "B", "foo.bar", "/", t);
220 t += base::TimeDelta::FromInternalValue(10);
221 AddCookie("A", "B", "www.aaa.com", "/", t);
222 t += base::TimeDelta::FromInternalValue(10);
223 AddCookie("A", "B", "travel.aaa.com", "/", t);
224 t += base::TimeDelta::FromInternalValue(10);
225 AddCookie("A", "B", "www.bbb.com", "/", t);
228 store_ = new SQLitePersistentCookieStore(
229 temp_dir_.path().Append(kCookieFilename),
230 client_task_runner(),
231 background_task_runner(),
233 // Posting a blocking task to db_thread_ makes sure that the DB thread waits
234 // until both Load and LoadCookiesForKey have been posted to its task queue.
235 background_task_runner()->PostTask(
237 base::Bind(&SQLitePersistentCookieStoreTest::WaitOnDBEvent,
238 base::Unretained(this)));
239 store_->Load(base::Bind(&SQLitePersistentCookieStoreTest::OnLoaded,
240 base::Unretained(this)));
241 store_->LoadCookiesForKey("aaa.com",
242 base::Bind(&SQLitePersistentCookieStoreTest::OnKeyLoaded,
243 base::Unretained(this)));
244 background_task_runner()->PostTask(
246 base::Bind(&SQLitePersistentCookieStoreTest::WaitOnDBEvent,
247 base::Unretained(this)));
249 // Now the DB-thread queue contains:
251 // 1. Wait (on db_event)
253 // 2. "Init And Chain-Load First Domain"
254 // 3. Priority Load (aaa.com)
255 // 4. Wait (on db_event)
256 db_thread_event_.Signal();
257 key_loaded_event_.Wait();
258 ASSERT_EQ(loaded_event_.IsSignaled(), false);
259 std::set<std::string> cookies_loaded;
260 for (CanonicalCookieVector::const_iterator it = cookies_.begin();
261 it != cookies_.end();
263 cookies_loaded.insert((*it)->Domain().c_str());
265 STLDeleteElements(&cookies_);
266 ASSERT_GT(4U, cookies_loaded.size());
267 ASSERT_EQ(true, cookies_loaded.find("www.aaa.com") != cookies_loaded.end());
269 cookies_loaded.find("travel.aaa.com") != cookies_loaded.end());
271 db_thread_event_.Signal();
272 loaded_event_.Wait();
273 for (CanonicalCookieVector::const_iterator it = cookies_.begin();
274 it != cookies_.end();
276 cookies_loaded.insert((*it)->Domain().c_str());
278 ASSERT_EQ(4U, cookies_loaded.size());
279 ASSERT_EQ(cookies_loaded.find("foo.bar") != cookies_loaded.end(),
281 ASSERT_EQ(cookies_loaded.find("www.bbb.com") != cookies_loaded.end(), true);
282 STLDeleteElements(&cookies_);
285 // Test that we can force the database to be written by calling Flush().
286 TEST_F(SQLitePersistentCookieStoreTest, TestFlush) {
287 InitializeStore(false);
288 // File timestamps don't work well on all platforms, so we'll determine
289 // whether the DB file has been modified by checking its size.
290 base::FilePath path = temp_dir_.path().Append(kCookieFilename);
291 base::PlatformFileInfo info;
292 ASSERT_TRUE(file_util::GetFileInfo(path, &info));
293 int64 base_size = info.size;
295 // Write some large cookies, so the DB will have to expand by several KB.
296 for (char c = 'a'; c < 'z'; ++c) {
297 // Each cookie needs a unique timestamp for creation_utc (see DB schema).
298 base::Time t = base::Time::Now() + base::TimeDelta::FromMicroseconds(c);
299 std::string name(1, c);
300 std::string value(1000, c);
301 AddCookie(name, value, "foo.bar", "/", t);
306 // We forced a write, so now the file will be bigger.
307 ASSERT_TRUE(file_util::GetFileInfo(path, &info));
308 ASSERT_GT(info.size, base_size);
311 // Test loading old session cookies from the disk.
312 TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) {
313 InitializeStore(true);
315 // Add a session cookie.
317 net::CanonicalCookie(
318 GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
319 base::Time(), base::Time::Now(), false, false,
320 net::COOKIE_PRIORITY_DEFAULT));
322 // Force the store to write its data to the disk.
325 // Create a store that loads session cookies and test that the session cookie
327 CanonicalCookieVector cookies;
328 CreateAndLoad(true, &cookies);
330 ASSERT_EQ(1U, cookies.size());
331 ASSERT_STREQ("sessioncookie.com", cookies[0]->Domain().c_str());
332 ASSERT_STREQ("C", cookies[0]->Name().c_str());
333 ASSERT_STREQ("D", cookies[0]->Value().c_str());
334 ASSERT_EQ(net::COOKIE_PRIORITY_DEFAULT, cookies[0]->Priority());
336 STLDeleteElements(&cookies);
339 // Test loading old session cookies from the disk.
340 TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) {
341 InitializeStore(true);
343 // Add a session cookie.
345 net::CanonicalCookie(
346 GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
347 base::Time(), base::Time::Now(), false, false,
348 net::COOKIE_PRIORITY_DEFAULT));
350 // Force the store to write its data to the disk.
353 // Create a store that doesn't load old session cookies and test that the
354 // session cookie was not loaded.
355 CanonicalCookieVector cookies;
356 CreateAndLoad(false, &cookies);
357 ASSERT_EQ(0U, cookies.size());
359 // The store should also delete the session cookie. Wait until that has been
363 // Create a store that loads old session cookies and test that the session
365 CreateAndLoad(true, &cookies);
366 ASSERT_EQ(0U, cookies.size());
369 TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) {
370 InitializeStore(true);
371 static const char kSessionName[] = "session";
372 static const char kPersistentName[] = "persistent";
374 // Add a session cookie.
376 net::CanonicalCookie(
377 GURL(), kSessionName, "val", "sessioncookie.com", "/",
378 base::Time::Now(), base::Time(), base::Time::Now(), false, false,
379 net::COOKIE_PRIORITY_DEFAULT));
380 // Add a persistent cookie.
382 net::CanonicalCookie(
383 GURL(), kPersistentName, "val", "sessioncookie.com", "/",
384 base::Time::Now() - base::TimeDelta::FromDays(1),
385 base::Time::Now() + base::TimeDelta::FromDays(1),
386 base::Time::Now(), false, false,
387 net::COOKIE_PRIORITY_DEFAULT));
389 // Force the store to write its data to the disk.
392 // Create a store that loads session cookie and test that the IsPersistent
393 // attribute is restored.
394 CanonicalCookieVector cookies;
395 CreateAndLoad(true, &cookies);
396 ASSERT_EQ(2U, cookies.size());
398 std::map<std::string, net::CanonicalCookie*> cookie_map;
399 for (CanonicalCookieVector::const_iterator it = cookies.begin();
402 cookie_map[(*it)->Name()] = *it;
405 std::map<std::string, net::CanonicalCookie*>::const_iterator it =
406 cookie_map.find(kSessionName);
407 ASSERT_TRUE(it != cookie_map.end());
408 EXPECT_FALSE(cookie_map[kSessionName]->IsPersistent());
410 it = cookie_map.find(kPersistentName);
411 ASSERT_TRUE(it != cookie_map.end());
412 EXPECT_TRUE(cookie_map[kPersistentName]->IsPersistent());
414 STLDeleteElements(&cookies);
417 TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) {
418 static const char kLowName[] = "low";
419 static const char kMediumName[] = "medium";
420 static const char kHighName[] = "high";
421 static const char kCookieDomain[] = "sessioncookie.com";
422 static const char kCookieValue[] = "value";
423 static const char kCookiePath[] = "/";
425 InitializeStore(true);
427 // Add a low-priority persistent cookie.
429 net::CanonicalCookie(
430 GURL(), kLowName, kCookieValue, kCookieDomain, kCookiePath,
431 base::Time::Now() - base::TimeDelta::FromMinutes(1),
432 base::Time::Now() + base::TimeDelta::FromDays(1),
433 base::Time::Now(), false, false,
434 net::COOKIE_PRIORITY_LOW));
436 // Add a medium-priority persistent cookie.
438 net::CanonicalCookie(
439 GURL(), kMediumName, kCookieValue, kCookieDomain, kCookiePath,
440 base::Time::Now() - base::TimeDelta::FromMinutes(2),
441 base::Time::Now() + base::TimeDelta::FromDays(1),
442 base::Time::Now(), false, false,
443 net::COOKIE_PRIORITY_MEDIUM));
445 // Add a high-priority peristent cookie.
447 net::CanonicalCookie(
448 GURL(), kHighName, kCookieValue, kCookieDomain, kCookiePath,
449 base::Time::Now() - base::TimeDelta::FromMinutes(3),
450 base::Time::Now() + base::TimeDelta::FromDays(1),
451 base::Time::Now(), false, false,
452 net::COOKIE_PRIORITY_HIGH));
454 // Force the store to write its data to the disk.
457 // Create a store that loads session cookie and test that the priority
458 // attribute values are restored.
459 CanonicalCookieVector cookies;
460 CreateAndLoad(true, &cookies);
461 ASSERT_EQ(3U, cookies.size());
463 // Put the cookies into a map, by name, so we can easily find them.
464 std::map<std::string, net::CanonicalCookie*> cookie_map;
465 for (CanonicalCookieVector::const_iterator it = cookies.begin();
468 cookie_map[(*it)->Name()] = *it;
471 // Validate that each cookie has the correct priority.
472 std::map<std::string, net::CanonicalCookie*>::const_iterator it =
473 cookie_map.find(kLowName);
474 ASSERT_TRUE(it != cookie_map.end());
475 EXPECT_EQ(net::COOKIE_PRIORITY_LOW, cookie_map[kLowName]->Priority());
477 it = cookie_map.find(kMediumName);
478 ASSERT_TRUE(it != cookie_map.end());
479 EXPECT_EQ(net::COOKIE_PRIORITY_MEDIUM, cookie_map[kMediumName]->Priority());
481 it = cookie_map.find(kHighName);
482 ASSERT_TRUE(it != cookie_map.end());
483 EXPECT_EQ(net::COOKIE_PRIORITY_HIGH, cookie_map[kHighName]->Priority());
485 STLDeleteElements(&cookies);
488 } // namespace content