- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / net / sqlite_persistent_cookie_store_unittest.cc
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.
4
5 #include "content/browser/net/sqlite_persistent_cookie_store.h"
6
7 #include <map>
8 #include <set>
9
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"
27 #include "url/gurl.h"
28
29 namespace content {
30
31 namespace {
32
33 const base::FilePath::CharType kCookieFilename[] = FILE_PATH_LITERAL("Cookies");
34
35 }  // namespace
36
37 typedef std::vector<net::CanonicalCookie*> CanonicalCookieVector;
38
39 class SQLitePersistentCookieStoreTest : public testing::Test {
40  public:
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) {
46   }
47
48   void OnLoaded(const CanonicalCookieVector& cookies) {
49     cookies_ = cookies;
50     loaded_event_.Signal();
51   }
52
53   void OnKeyLoaded(const CanonicalCookieVector& cookies) {
54     cookies_ = cookies;
55     key_loaded_event_.Signal();
56   }
57
58   void Load(CanonicalCookieVector* cookies) {
59     EXPECT_FALSE(loaded_event_.IsSignaled());
60     store_->Load(base::Bind(&SQLitePersistentCookieStoreTest::OnLoaded,
61                             base::Unretained(this)));
62     loaded_event_.Wait();
63     *cookies = cookies_;
64   }
65
66   void Flush() {
67     base::WaitableEvent event(false, false);
68     store_->Flush(base::Bind(&base::WaitableEvent::Signal,
69                              base::Unretained(&event)));
70     event.Wait();
71   }
72
73   scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
74     return pool_owner_->pool()->GetSequencedTaskRunner(
75         pool_owner_->pool()->GetNamedSequenceToken("background"));
76   }
77
78   scoped_refptr<base::SequencedTaskRunner> client_task_runner() {
79     return pool_owner_->pool()->GetSequencedTaskRunner(
80         pool_owner_->pool()->GetNamedSequenceToken("client"));
81   }
82
83   void DestroyStore() {
84     store_ = NULL;
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"));
91   }
92
93   void CreateAndLoad(bool restore_old_session_cookies,
94                      CanonicalCookieVector* cookies) {
95     store_ = new SQLitePersistentCookieStore(
96         temp_dir_.path().Append(kCookieFilename),
97         client_task_runner(),
98         background_task_runner(),
99         restore_old_session_cookies,
100         NULL);
101     Load(cookies);
102   }
103
104   void InitializeStore(bool restore_old_session_cookies) {
105     CanonicalCookieVector cookies;
106     CreateAndLoad(restore_old_session_cookies, &cookies);
107     EXPECT_EQ(0U, cookies.size());
108   }
109
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();
114   }
115
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) {
122     store_->AddCookie(
123         net::CanonicalCookie(GURL(), name, value, domain, path, creation,
124                              creation, creation, false, false,
125                              net::COOKIE_PRIORITY_DEFAULT));
126   }
127
128   virtual void SetUp() OVERRIDE {
129     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
130   }
131
132   virtual void TearDown() OVERRIDE {
133     DestroyStore();
134     pool_owner_->pool()->Shutdown();
135   }
136
137  protected:
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_;
146 };
147
148 TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) {
149   InitializeStore(false);
150   AddCookie("A", "B", "foo.bar", "/", base::Time::Now());
151   DestroyStore();
152
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());
160   DestroyStore();
161   STLDeleteElements(&cookies);
162
163   // Now corrupt the meta table.
164   {
165     sql::Connection db;
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"));
170     db.Close();
171   }
172
173   // Upon loading, the database should be reset to a good, blank state.
174   CreateAndLoad(false, &cookies);
175   ASSERT_EQ(0U, cookies.size());
176
177   // Verify that, after, recovery, the database persists properly.
178   AddCookie("X", "Y", "foo.bar", "/", base::Time::Now());
179   DestroyStore();
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);
186 }
187
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
194   // is still there.
195   DestroyStore();
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());
203
204   // Now delete the cookie and check persistence again.
205   store_->DeleteCookie(*cookies[0]);
206   DestroyStore();
207   STLDeleteElements(&cookies);
208
209   // Reload and check if the cookie has been removed.
210   CreateAndLoad(false, &cookies);
211   ASSERT_EQ(0U, cookies.size());
212 }
213
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);
226   DestroyStore();
227
228   store_ = new SQLitePersistentCookieStore(
229       temp_dir_.path().Append(kCookieFilename),
230       client_task_runner(),
231       background_task_runner(),
232       false, NULL);
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(
236       FROM_HERE,
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(
245       FROM_HERE,
246       base::Bind(&SQLitePersistentCookieStoreTest::WaitOnDBEvent,
247                  base::Unretained(this)));
248
249   // Now the DB-thread queue contains:
250   // (active:)
251   // 1. Wait (on db_event)
252   // (pending:)
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();
262        ++it) {
263     cookies_loaded.insert((*it)->Domain().c_str());
264   }
265   STLDeleteElements(&cookies_);
266   ASSERT_GT(4U, cookies_loaded.size());
267   ASSERT_EQ(true, cookies_loaded.find("www.aaa.com") != cookies_loaded.end());
268   ASSERT_EQ(true,
269             cookies_loaded.find("travel.aaa.com") != cookies_loaded.end());
270
271   db_thread_event_.Signal();
272   loaded_event_.Wait();
273   for (CanonicalCookieVector::const_iterator it = cookies_.begin();
274        it != cookies_.end();
275        ++it) {
276     cookies_loaded.insert((*it)->Domain().c_str());
277   }
278   ASSERT_EQ(4U, cookies_loaded.size());
279   ASSERT_EQ(cookies_loaded.find("foo.bar") != cookies_loaded.end(),
280             true);
281   ASSERT_EQ(cookies_loaded.find("www.bbb.com") != cookies_loaded.end(), true);
282   STLDeleteElements(&cookies_);
283 }
284
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;
294
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);
302   }
303
304   Flush();
305
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);
309 }
310
311 // Test loading old session cookies from the disk.
312 TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) {
313   InitializeStore(true);
314
315   // Add a session cookie.
316   store_->AddCookie(
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));
321
322   // Force the store to write its data to the disk.
323   DestroyStore();
324
325   // Create a store that loads session cookies and test that the session cookie
326   // was loaded.
327   CanonicalCookieVector cookies;
328   CreateAndLoad(true, &cookies);
329
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());
335
336   STLDeleteElements(&cookies);
337 }
338
339 // Test loading old session cookies from the disk.
340 TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) {
341   InitializeStore(true);
342
343   // Add a session cookie.
344   store_->AddCookie(
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));
349
350   // Force the store to write its data to the disk.
351   DestroyStore();
352
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());
358
359   // The store should also delete the session cookie. Wait until that has been
360   // done.
361   DestroyStore();
362
363   // Create a store that loads old session cookies and test that the session
364   // cookie is gone.
365   CreateAndLoad(true, &cookies);
366   ASSERT_EQ(0U, cookies.size());
367 }
368
369 TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) {
370   InitializeStore(true);
371   static const char kSessionName[] = "session";
372   static const char kPersistentName[] = "persistent";
373
374   // Add a session cookie.
375   store_->AddCookie(
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.
381   store_->AddCookie(
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));
388
389   // Force the store to write its data to the disk.
390   DestroyStore();
391
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());
397
398   std::map<std::string, net::CanonicalCookie*> cookie_map;
399   for (CanonicalCookieVector::const_iterator it = cookies.begin();
400        it != cookies.end();
401        ++it) {
402     cookie_map[(*it)->Name()] = *it;
403   }
404
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());
409
410   it = cookie_map.find(kPersistentName);
411   ASSERT_TRUE(it != cookie_map.end());
412   EXPECT_TRUE(cookie_map[kPersistentName]->IsPersistent());
413
414   STLDeleteElements(&cookies);
415 }
416
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[] = "/";
424
425   InitializeStore(true);
426
427   // Add a low-priority persistent cookie.
428   store_->AddCookie(
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));
435
436   // Add a medium-priority persistent cookie.
437   store_->AddCookie(
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));
444
445   // Add a high-priority peristent cookie.
446   store_->AddCookie(
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));
453
454   // Force the store to write its data to the disk.
455   DestroyStore();
456
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());
462
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();
466        it != cookies.end();
467        ++it) {
468     cookie_map[(*it)->Name()] = *it;
469   }
470
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());
476
477   it = cookie_map.find(kMediumName);
478   ASSERT_TRUE(it != cookie_map.end());
479   EXPECT_EQ(net::COOKIE_PRIORITY_MEDIUM, cookie_map[kMediumName]->Priority());
480
481   it = cookie_map.find(kHighName);
482   ASSERT_TRUE(it != cookie_map.end());
483   EXPECT_EQ(net::COOKIE_PRIORITY_HIGH, cookie_map[kHighName]->Priority());
484
485   STLDeleteElements(&cookies);
486 }
487
488 }  // namespace content