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 "base/file_util.h"
6#include "base/files/file_path.h"
7#include "base/files/scoped_temp_dir.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/platform_file.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "net/base/net_errors.h"
15#include "net/base/test_completion_callback.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "third_party/sqlite/sqlite3.h"
18#include "webkit/browser/database/database_tracker.h"
19#include "webkit/browser/quota/mock_special_storage_policy.h"
20#include "webkit/browser/quota/quota_manager.h"
21#include "webkit/common/database/database_identifier.h"
22
23namespace {
24
25const char kOrigin1Url[] = "http://origin1";
26const char kOrigin2Url[] = "http://protected_origin2";
27
28class TestObserver : public webkit_database::DatabaseTracker::Observer {
29 public:
30  TestObserver()
31      : new_notification_received_(false),
32        observe_size_changes_(true),
33        observe_scheduled_deletions_(true) {
34  }
35  TestObserver(bool observe_size_changes, bool observe_scheduled_deletions)
36      : new_notification_received_(false),
37        observe_size_changes_(observe_size_changes),
38        observe_scheduled_deletions_(observe_scheduled_deletions) {
39  }
40
41  virtual ~TestObserver() {}
42  virtual void OnDatabaseSizeChanged(const std::string& origin_identifier,
43                                     const base::string16& database_name,
44                                     int64 database_size) OVERRIDE {
45    if (!observe_size_changes_)
46      return;
47    new_notification_received_ = true;
48    origin_identifier_ = origin_identifier;
49    database_name_ = database_name;
50    database_size_ = database_size;
51  }
52  virtual void OnDatabaseScheduledForDeletion(
53      const std::string& origin_identifier,
54      const base::string16& database_name) OVERRIDE {
55    if (!observe_scheduled_deletions_)
56      return;
57    new_notification_received_ = true;
58    origin_identifier_ = origin_identifier;
59    database_name_ = database_name;
60  }
61  bool DidReceiveNewNotification() {
62    bool temp_new_notification_received = new_notification_received_;
63    new_notification_received_ = false;
64    return temp_new_notification_received;
65  }
66  std::string GetNotificationOriginIdentifier() {
67    return origin_identifier_;
68  }
69  base::string16 GetNotificationDatabaseName() { return database_name_; }
70  int64 GetNotificationDatabaseSize() { return database_size_; }
71
72 private:
73  bool new_notification_received_;
74  bool observe_size_changes_;
75  bool observe_scheduled_deletions_;
76  std::string origin_identifier_;
77  base::string16 database_name_;
78  int64 database_size_;
79};
80
81void CheckNotificationReceived(TestObserver* observer,
82                               const std::string& expected_origin_identifier,
83                               const base::string16& expected_database_name,
84                               int64 expected_database_size) {
85  EXPECT_TRUE(observer->DidReceiveNewNotification());
86  EXPECT_EQ(expected_origin_identifier,
87            observer->GetNotificationOriginIdentifier());
88  EXPECT_EQ(expected_database_name,
89            observer->GetNotificationDatabaseName());
90  EXPECT_EQ(expected_database_size,
91            observer->GetNotificationDatabaseSize());
92}
93
94class TestQuotaManagerProxy : public quota::QuotaManagerProxy {
95 public:
96  TestQuotaManagerProxy()
97      : QuotaManagerProxy(NULL, NULL),
98        registered_client_(NULL) {
99  }
100
101  virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {
102    EXPECT_FALSE(registered_client_);
103    registered_client_ = client;
104  }
105
106  virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
107                                     const GURL& origin,
108                                     quota::StorageType type) OVERRIDE {
109    EXPECT_EQ(quota::QuotaClient::kDatabase, client_id);
110    EXPECT_EQ(quota::kStorageTypeTemporary, type);
111    accesses_[origin] += 1;
112  }
113
114  virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
115                                     const GURL& origin,
116                                     quota::StorageType type,
117                                     int64 delta) OVERRIDE {
118    EXPECT_EQ(quota::QuotaClient::kDatabase, client_id);
119    EXPECT_EQ(quota::kStorageTypeTemporary, type);
120    modifications_[origin].first += 1;
121    modifications_[origin].second += delta;
122  }
123
124  // Not needed for our tests.
125  virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {}
126  virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {}
127
128  void SimulateQuotaManagerDestroyed() {
129    if (registered_client_) {
130      registered_client_->OnQuotaManagerDestroyed();
131      registered_client_ = NULL;
132    }
133  }
134
135  bool WasAccessNotified(const GURL& origin) {
136    return accesses_[origin] != 0;
137  }
138
139  bool WasModificationNotified(const GURL& origin, int64 amount) {
140    return modifications_[origin].first != 0 &&
141           modifications_[origin].second == amount;
142  }
143
144  void reset() {
145    accesses_.clear();
146    modifications_.clear();
147  }
148
149  quota::QuotaClient* registered_client_;
150
151  // Map from origin to count of access notifications.
152  std::map<GURL, int> accesses_;
153
154  // Map from origin to <count, sum of deltas>
155  std::map<GURL, std::pair<int, int64> > modifications_;
156
157 protected:
158  virtual ~TestQuotaManagerProxy() {
159    EXPECT_FALSE(registered_client_);
160  }
161};
162
163
164bool EnsureFileOfSize(const base::FilePath& file_path, int64 length) {
165  base::PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
166  base::PlatformFile file =
167      base::CreatePlatformFile(
168          file_path,
169          base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE,
170          NULL,
171          &error_code);
172  if (error_code != base::PLATFORM_FILE_OK)
173    return false;
174  if (!base::TruncatePlatformFile(file, length))
175    error_code = base::PLATFORM_FILE_ERROR_FAILED;
176  base::ClosePlatformFile(file);
177  return error_code == base::PLATFORM_FILE_OK;
178}
179
180}  // namespace
181
182namespace webkit_database {
183
184// We declare a helper class, and make it a friend of DatabaseTracker using
185// the FRIEND_TEST() macro, and we implement all tests we want to run as
186// static methods of this class. Then we make our TEST() targets call these
187// static functions. This allows us to run each test in normal mode and
188// incognito mode without writing the same code twice.
189class DatabaseTracker_TestHelper_Test {
190 public:
191  static void TestDeleteOpenDatabase(bool incognito_mode) {
192    // Initialize the tracker database.
193    base::ScopedTempDir temp_dir;
194    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
195    scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy =
196        new quota::MockSpecialStoragePolicy;
197    special_storage_policy->AddProtected(GURL(kOrigin2Url));
198    scoped_refptr<DatabaseTracker> tracker(
199        new DatabaseTracker(temp_dir.path(),
200                            incognito_mode,
201                            special_storage_policy.get(),
202                            NULL,
203                            NULL));
204
205    // Create and open three databases.
206    int64 database_size = 0;
207    const std::string kOrigin1 =
208        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
209    const std::string kOrigin2 =
210        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
211    const base::string16 kDB1 = ASCIIToUTF16("db1");
212    const base::string16 kDB2 = ASCIIToUTF16("db2");
213    const base::string16 kDB3 = ASCIIToUTF16("db3");
214    const base::string16 kDescription = ASCIIToUTF16("database_description");
215
216    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
217                            &database_size);
218    tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
219                            &database_size);
220    tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0,
221                            &database_size);
222
223    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
224        base::FilePath::FromWStringHack(UTF16ToWide(
225            tracker->GetOriginDirectory(kOrigin1))))));
226    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
227        base::FilePath::FromWStringHack(UTF16ToWide(
228            tracker->GetOriginDirectory(kOrigin2))))));
229    EXPECT_EQ(1, file_util::WriteFile(
230        tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
231    EXPECT_EQ(2, file_util::WriteFile(
232        tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2));
233    EXPECT_EQ(3, file_util::WriteFile(
234        tracker->GetFullDBFilePath(kOrigin2, kDB3), "aaa", 3));
235    tracker->DatabaseModified(kOrigin1, kDB1);
236    tracker->DatabaseModified(kOrigin2, kDB2);
237    tracker->DatabaseModified(kOrigin2, kDB3);
238
239    // Delete db1. Should also delete origin1.
240    TestObserver observer;
241    tracker->AddObserver(&observer);
242    net::TestCompletionCallback callback;
243    int result = tracker->DeleteDatabase(kOrigin1, kDB1, callback.callback());
244    EXPECT_EQ(net::ERR_IO_PENDING, result);
245    ASSERT_FALSE(callback.have_result());
246    EXPECT_TRUE(observer.DidReceiveNewNotification());
247    EXPECT_EQ(kOrigin1, observer.GetNotificationOriginIdentifier());
248    EXPECT_EQ(kDB1, observer.GetNotificationDatabaseName());
249    tracker->DatabaseClosed(kOrigin1, kDB1);
250    result = callback.GetResult(result);
251    EXPECT_EQ(net::OK, result);
252    EXPECT_FALSE(base::PathExists(
253          tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
254
255    // Recreate db1.
256    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
257                            &database_size);
258    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
259        base::FilePath::FromWStringHack(UTF16ToWide(
260            tracker->GetOriginDirectory(kOrigin1))))));
261    EXPECT_EQ(1, file_util::WriteFile(
262        tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
263    tracker->DatabaseModified(kOrigin1, kDB1);
264
265    // Setup file modification times.  db1 and db2 are modified now, db3 three
266    // days ago.
267    EXPECT_TRUE(file_util::SetLastModifiedTime(
268        tracker->GetFullDBFilePath(kOrigin1, kDB1), base::Time::Now()));
269    EXPECT_TRUE(file_util::SetLastModifiedTime(
270        tracker->GetFullDBFilePath(kOrigin2, kDB2), base::Time::Now()));
271    base::Time three_days_ago = base::Time::Now();
272    three_days_ago -= base::TimeDelta::FromDays(3);
273    EXPECT_TRUE(file_util::SetLastModifiedTime(
274        tracker->GetFullDBFilePath(kOrigin2, kDB3), three_days_ago));
275
276    // Delete databases modified since yesterday. db2 is whitelisted.
277    base::Time yesterday = base::Time::Now();
278    yesterday -= base::TimeDelta::FromDays(1);
279    result = tracker->DeleteDataModifiedSince(
280        yesterday, callback.callback());
281    EXPECT_EQ(net::ERR_IO_PENDING, result);
282    ASSERT_FALSE(callback.have_result());
283    EXPECT_TRUE(observer.DidReceiveNewNotification());
284    tracker->DatabaseClosed(kOrigin1, kDB1);
285    tracker->DatabaseClosed(kOrigin2, kDB2);
286    result = callback.GetResult(result);
287    EXPECT_EQ(net::OK, result);
288    EXPECT_FALSE(base::PathExists(
289        tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
290    EXPECT_TRUE(
291        base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2)));
292    EXPECT_TRUE(
293        base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3)));
294
295    tracker->DatabaseClosed(kOrigin2, kDB3);
296    tracker->RemoveObserver(&observer);
297  }
298
299  static void TestDatabaseTracker(bool incognito_mode) {
300    // Initialize the tracker database.
301    base::ScopedTempDir temp_dir;
302    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
303    scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy =
304        new quota::MockSpecialStoragePolicy;
305    special_storage_policy->AddProtected(GURL(kOrigin2Url));
306    scoped_refptr<DatabaseTracker> tracker(
307        new DatabaseTracker(temp_dir.path(),
308                            incognito_mode,
309                            special_storage_policy.get(),
310                            NULL,
311                            NULL));
312
313    // Add two observers.
314    TestObserver observer1;
315    TestObserver observer2;
316    tracker->AddObserver(&observer1);
317    tracker->AddObserver(&observer2);
318
319    // Open three new databases.
320    int64 database_size = 0;
321    const std::string kOrigin1 =
322        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
323    const std::string kOrigin2 =
324        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
325    const base::string16 kDB1 = ASCIIToUTF16("db1");
326    const base::string16 kDB2 = ASCIIToUTF16("db2");
327    const base::string16 kDB3 = ASCIIToUTF16("db3");
328    const base::string16 kDescription = ASCIIToUTF16("database_description");
329
330    // Get the info for kOrigin1 and kOrigin2
331    DatabaseTracker::CachedOriginInfo* origin1_info =
332        tracker->GetCachedOriginInfo(kOrigin1);
333    DatabaseTracker::CachedOriginInfo* origin2_info =
334        tracker->GetCachedOriginInfo(kOrigin1);
335    EXPECT_TRUE(origin1_info);
336    EXPECT_TRUE(origin2_info);
337
338
339    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
340                            &database_size);
341    EXPECT_EQ(0, database_size);
342    tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
343                            &database_size);
344    EXPECT_EQ(0, database_size);
345    tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0,
346                            &database_size);
347    EXPECT_EQ(0, database_size);
348
349    // Write some data to each file and check that the listeners are
350    // called with the appropriate values.
351    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
352        base::FilePath::FromWStringHack(UTF16ToWide(
353            tracker->GetOriginDirectory(kOrigin1))))));
354    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
355        base::FilePath::FromWStringHack(UTF16ToWide(
356            tracker->GetOriginDirectory(kOrigin2))))));
357    EXPECT_EQ(1, file_util::WriteFile(
358        tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
359    EXPECT_EQ(2, file_util::WriteFile(
360        tracker->GetFullDBFilePath(kOrigin2, kDB2), "aa", 2));
361    EXPECT_EQ(4, file_util::WriteFile(
362        tracker->GetFullDBFilePath(kOrigin1, kDB3), "aaaa", 4));
363    tracker->DatabaseModified(kOrigin1, kDB1);
364    CheckNotificationReceived(&observer1, kOrigin1, kDB1, 1);
365    CheckNotificationReceived(&observer2, kOrigin1, kDB1, 1);
366    tracker->DatabaseModified(kOrigin2, kDB2);
367    CheckNotificationReceived(&observer1, kOrigin2, kDB2, 2);
368    CheckNotificationReceived(&observer2, kOrigin2, kDB2, 2);
369    tracker->DatabaseModified(kOrigin1, kDB3);
370    CheckNotificationReceived(&observer1, kOrigin1, kDB3, 4);
371    CheckNotificationReceived(&observer2, kOrigin1, kDB3, 4);
372
373    // Close all databases
374    tracker->DatabaseClosed(kOrigin1, kDB1);
375    tracker->DatabaseClosed(kOrigin2, kDB2);
376    tracker->DatabaseClosed(kOrigin1, kDB3);
377
378    // Open an existing database and check the reported size
379    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
380                            &database_size);
381    EXPECT_EQ(1, database_size);
382    tracker->DatabaseClosed(kOrigin1, kDB1);
383
384    // Remove an observer; this should clear all caches.
385    tracker->RemoveObserver(&observer2);
386
387    // Close the tracker database and clear all caches.
388    // Then make sure that DatabaseOpened() still returns the correct result.
389    tracker->CloseTrackerDatabaseAndClearCaches();
390    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
391                            &database_size);
392    EXPECT_EQ(1, database_size);
393    tracker->DatabaseClosed(kOrigin1, kDB1);
394
395    // Remove all observers.
396    tracker->RemoveObserver(&observer1);
397
398    // Trying to delete a database in use should fail
399    tracker->DatabaseOpened(kOrigin1, kDB3, kDescription, 0,
400                            &database_size);
401    EXPECT_FALSE(tracker->DeleteClosedDatabase(kOrigin1, kDB3));
402    origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
403    EXPECT_TRUE(origin1_info);
404    EXPECT_EQ(4, origin1_info->GetDatabaseSize(kDB3));
405    tracker->DatabaseClosed(kOrigin1, kDB3);
406
407    // Delete a database and make sure the space used by that origin is updated
408    EXPECT_TRUE(tracker->DeleteClosedDatabase(kOrigin1, kDB3));
409    origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
410    EXPECT_TRUE(origin1_info);
411    EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1));
412    EXPECT_EQ(0, origin1_info->GetDatabaseSize(kDB3));
413
414    // Get all data for all origins
415    std::vector<OriginInfo> origins_info;
416    EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
417    EXPECT_EQ(size_t(2), origins_info.size());
418    EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier());
419    EXPECT_EQ(1, origins_info[0].TotalSize());
420    EXPECT_EQ(1, origins_info[0].GetDatabaseSize(kDB1));
421    EXPECT_EQ(0, origins_info[0].GetDatabaseSize(kDB3));
422
423    EXPECT_EQ(kOrigin2, origins_info[1].GetOriginIdentifier());
424    EXPECT_EQ(2, origins_info[1].TotalSize());
425
426    // Trying to delete an origin with databases in use should fail
427    tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
428                            &database_size);
429    EXPECT_FALSE(tracker->DeleteOrigin(kOrigin1, false));
430    origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
431    EXPECT_TRUE(origin1_info);
432    EXPECT_EQ(1, origin1_info->GetDatabaseSize(kDB1));
433    tracker->DatabaseClosed(kOrigin1, kDB1);
434
435    // Delete an origin that doesn't have any database in use
436    EXPECT_TRUE(tracker->DeleteOrigin(kOrigin1, false));
437    origins_info.clear();
438    EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
439    EXPECT_EQ(size_t(1), origins_info.size());
440    EXPECT_EQ(kOrigin2, origins_info[0].GetOriginIdentifier());
441
442    origin1_info = tracker->GetCachedOriginInfo(kOrigin1);
443    EXPECT_TRUE(origin1_info);
444    EXPECT_EQ(0, origin1_info->TotalSize());
445  }
446
447  static void DatabaseTrackerQuotaIntegration() {
448    const GURL kOrigin(kOrigin1Url);
449    const std::string kOriginId =
450        webkit_database::GetIdentifierFromOrigin(kOrigin);
451    const base::string16 kName = ASCIIToUTF16("name");
452    const base::string16 kDescription = ASCIIToUTF16("description");
453
454    base::ScopedTempDir temp_dir;
455    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
456
457    // Initialize the tracker with a QuotaManagerProxy
458    scoped_refptr<TestQuotaManagerProxy> test_quota_proxy(
459        new TestQuotaManagerProxy);
460    scoped_refptr<DatabaseTracker> tracker(
461        new DatabaseTracker(temp_dir.path(),
462                            false /* incognito */,
463                            NULL,
464                            test_quota_proxy.get(),
465                            NULL));
466    EXPECT_TRUE(test_quota_proxy->registered_client_);
467
468    // Create a database and modify it a couple of times, close it,
469    // then delete it. Observe the tracker notifies accordingly.
470
471    int64 database_size = 0;
472    tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
473                            &database_size);
474    EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
475    test_quota_proxy->reset();
476
477    base::FilePath db_file(tracker->GetFullDBFilePath(kOriginId, kName));
478    EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
479    EXPECT_TRUE(EnsureFileOfSize(db_file, 10));
480    tracker->DatabaseModified(kOriginId, kName);
481    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 10));
482    test_quota_proxy->reset();
483
484    EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
485    tracker->DatabaseModified(kOriginId, kName);
486    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 90));
487    test_quota_proxy->reset();
488
489    tracker->DatabaseClosed(kOriginId, kName);
490    EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
491    EXPECT_EQ(net::OK, tracker->DeleteDatabase(
492        kOriginId, kName, net::CompletionCallback()));
493    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
494    test_quota_proxy->reset();
495
496    // Create a database and modify it, try to delete it while open,
497    // then close it (at which time deletion will actually occur).
498    // Observe the tracker notifies accordingly.
499
500    tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
501                            &database_size);
502    EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
503    test_quota_proxy->reset();
504
505    db_file = tracker->GetFullDBFilePath(kOriginId, kName);
506    EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
507    EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
508    tracker->DatabaseModified(kOriginId, kName);
509    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
510    test_quota_proxy->reset();
511
512    EXPECT_EQ(net::ERR_IO_PENDING,
513              tracker->DeleteDatabase(kOriginId, kName,
514                                      net::CompletionCallback()));
515    EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
516
517    tracker->DatabaseClosed(kOriginId, kName);
518    EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
519    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, -100));
520    test_quota_proxy->reset();
521
522    // Create a database and up the file size without telling
523    // the tracker about the modification, than simulate a
524    // a renderer crash.
525    // Observe the tracker notifies accordingly.
526
527    tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
528                            &database_size);
529    EXPECT_TRUE(test_quota_proxy->WasAccessNotified(kOrigin));
530    test_quota_proxy->reset();
531    db_file = tracker->GetFullDBFilePath(kOriginId, kName);
532    EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
533    EXPECT_TRUE(EnsureFileOfSize(db_file, 100));
534    DatabaseConnections crashed_renderer_connections;
535    crashed_renderer_connections.AddConnection(kOriginId, kName);
536    EXPECT_FALSE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
537    tracker->CloseDatabases(crashed_renderer_connections);
538    EXPECT_TRUE(test_quota_proxy->WasModificationNotified(kOrigin, 100));
539
540    // Cleanup.
541    crashed_renderer_connections.RemoveAllConnections();
542    test_quota_proxy->SimulateQuotaManagerDestroyed();
543  }
544
545  static void DatabaseTrackerClearSessionOnlyDatabasesOnExit() {
546    int64 database_size = 0;
547    const std::string kOrigin1 =
548        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
549    const std::string kOrigin2 =
550        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
551    const base::string16 kDB1 = ASCIIToUTF16("db1");
552    const base::string16 kDB2 = ASCIIToUTF16("db2");
553    const base::string16 kDescription = ASCIIToUTF16("database_description");
554
555    // Initialize the tracker database.
556    base::MessageLoop message_loop;
557    base::ScopedTempDir temp_dir;
558    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
559    base::FilePath origin1_db_dir;
560    base::FilePath origin2_db_dir;
561    {
562      scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy =
563          new quota::MockSpecialStoragePolicy;
564      special_storage_policy->AddSessionOnly(GURL(kOrigin2Url));
565      scoped_refptr<DatabaseTracker> tracker(
566          new DatabaseTracker(temp_dir.path(),
567                              false,
568                              special_storage_policy.get(),
569                              NULL,
570                              base::MessageLoopProxy::current().get()));
571
572      // Open two new databases.
573      tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
574                              &database_size);
575      EXPECT_EQ(0, database_size);
576      tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
577                              &database_size);
578      EXPECT_EQ(0, database_size);
579
580      // Write some data to each file.
581      base::FilePath db_file;
582      db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1);
583      EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
584      EXPECT_TRUE(EnsureFileOfSize(db_file, 1));
585
586      db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2);
587      EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
588      EXPECT_TRUE(EnsureFileOfSize(db_file, 2));
589
590      // Store the origin database directories as long as they still exist.
591      origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName();
592      origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName();
593
594      tracker->DatabaseModified(kOrigin1, kDB1);
595      tracker->DatabaseModified(kOrigin2, kDB2);
596
597      // Close all databases.
598      tracker->DatabaseClosed(kOrigin1, kDB1);
599      tracker->DatabaseClosed(kOrigin2, kDB2);
600
601      tracker->Shutdown();
602    }
603
604    // At this point, the database tracker should be gone. Create a new one.
605    scoped_refptr<DatabaseTracker> tracker(
606        new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL));
607
608    // Get all data for all origins.
609    std::vector<OriginInfo> origins_info;
610    EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
611    // kOrigin1 was not session-only, so it survived. kOrigin2 was session-only
612    // and it got deleted.
613    EXPECT_EQ(size_t(1), origins_info.size());
614    EXPECT_EQ(kOrigin1, origins_info[0].GetOriginIdentifier());
615    EXPECT_TRUE(
616        base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1)));
617    EXPECT_EQ(base::FilePath(), tracker->GetFullDBFilePath(kOrigin2, kDB2));
618
619    // The origin directory of kOrigin1 remains, but the origin directory of
620    // kOrigin2 is deleted.
621    EXPECT_TRUE(base::PathExists(origin1_db_dir));
622    EXPECT_FALSE(base::PathExists(origin2_db_dir));
623  }
624
625  static void DatabaseTrackerSetForceKeepSessionState() {
626    int64 database_size = 0;
627    const std::string kOrigin1 =
628        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin1Url));
629    const std::string kOrigin2 =
630        webkit_database::GetIdentifierFromOrigin(GURL(kOrigin2Url));
631    const base::string16 kDB1 = ASCIIToUTF16("db1");
632    const base::string16 kDB2 = ASCIIToUTF16("db2");
633    const base::string16 kDescription = ASCIIToUTF16("database_description");
634
635    // Initialize the tracker database.
636    base::MessageLoop message_loop;
637    base::ScopedTempDir temp_dir;
638    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
639    base::FilePath origin1_db_dir;
640    base::FilePath origin2_db_dir;
641    {
642      scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy =
643          new quota::MockSpecialStoragePolicy;
644      special_storage_policy->AddSessionOnly(GURL(kOrigin2Url));
645      scoped_refptr<DatabaseTracker> tracker(
646          new DatabaseTracker(temp_dir.path(),
647                              false,
648                              special_storage_policy.get(),
649                              NULL,
650                              base::MessageLoopProxy::current().get()));
651      tracker->SetForceKeepSessionState();
652
653      // Open two new databases.
654      tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
655                              &database_size);
656      EXPECT_EQ(0, database_size);
657      tracker->DatabaseOpened(kOrigin2, kDB2, kDescription, 0,
658                              &database_size);
659      EXPECT_EQ(0, database_size);
660
661      // Write some data to each file.
662      base::FilePath db_file;
663      db_file = tracker->GetFullDBFilePath(kOrigin1, kDB1);
664      EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
665      EXPECT_TRUE(EnsureFileOfSize(db_file, 1));
666
667      db_file = tracker->GetFullDBFilePath(kOrigin2, kDB2);
668      EXPECT_TRUE(file_util::CreateDirectory(db_file.DirName()));
669      EXPECT_TRUE(EnsureFileOfSize(db_file, 2));
670
671      // Store the origin database directories as long as they still exist.
672      origin1_db_dir = tracker->GetFullDBFilePath(kOrigin1, kDB1).DirName();
673      origin2_db_dir = tracker->GetFullDBFilePath(kOrigin2, kDB2).DirName();
674
675      tracker->DatabaseModified(kOrigin1, kDB1);
676      tracker->DatabaseModified(kOrigin2, kDB2);
677
678      // Close all databases.
679      tracker->DatabaseClosed(kOrigin1, kDB1);
680      tracker->DatabaseClosed(kOrigin2, kDB2);
681
682      tracker->Shutdown();
683    }
684
685    // At this point, the database tracker should be gone. Create a new one.
686    scoped_refptr<DatabaseTracker> tracker(
687        new DatabaseTracker(temp_dir.path(), false, NULL, NULL, NULL));
688
689    // Get all data for all origins.
690    std::vector<OriginInfo> origins_info;
691    EXPECT_TRUE(tracker->GetAllOriginsInfo(&origins_info));
692    // No origins were deleted.
693    EXPECT_EQ(size_t(2), origins_info.size());
694    EXPECT_TRUE(
695        base::PathExists(tracker->GetFullDBFilePath(kOrigin1, kDB1)));
696    EXPECT_TRUE(
697        base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2)));
698
699    EXPECT_TRUE(base::PathExists(origin1_db_dir));
700    EXPECT_TRUE(base::PathExists(origin2_db_dir));
701  }
702
703  static void EmptyDatabaseNameIsValid() {
704    const GURL kOrigin(kOrigin1Url);
705    const std::string kOriginId =
706        webkit_database::GetIdentifierFromOrigin(kOrigin);
707    const base::string16 kEmptyName;
708    const base::string16 kDescription(ASCIIToUTF16("description"));
709    const base::string16 kChangedDescription(
710        ASCIIToUTF16("changed_description"));
711
712    // Initialize a tracker database, no need to put it on disk.
713    const bool kUseInMemoryTrackerDatabase = true;
714    base::ScopedTempDir temp_dir;
715    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
716    scoped_refptr<DatabaseTracker> tracker(
717        new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase,
718                            NULL, NULL, NULL));
719
720    // Starts off with no databases.
721    std::vector<OriginInfo> infos;
722    EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
723    EXPECT_TRUE(infos.empty());
724
725    // Create a db with an empty name.
726    int64 database_size = -1;
727    tracker->DatabaseOpened(kOriginId, kEmptyName, kDescription, 0,
728                            &database_size);
729    EXPECT_EQ(0, database_size);
730    tracker->DatabaseModified(kOriginId, kEmptyName);
731    EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
732    EXPECT_EQ(1u, infos.size());
733    EXPECT_EQ(kDescription, infos[0].GetDatabaseDescription(kEmptyName));
734    EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kEmptyName).empty());
735    tracker->DatabaseOpened(kOriginId, kEmptyName, kChangedDescription, 0,
736                            &database_size);
737    infos.clear();
738    EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
739    EXPECT_EQ(1u, infos.size());
740    EXPECT_EQ(kChangedDescription, infos[0].GetDatabaseDescription(kEmptyName));
741    tracker->DatabaseClosed(kOriginId, kEmptyName);
742    tracker->DatabaseClosed(kOriginId, kEmptyName);
743
744    // Deleting it should return to the initial state.
745    EXPECT_EQ(net::OK, tracker->DeleteDatabase(kOriginId, kEmptyName,
746                                               net::CompletionCallback()));
747    infos.clear();
748    EXPECT_TRUE(tracker->GetAllOriginsInfo(&infos));
749    EXPECT_TRUE(infos.empty());
750  }
751
752  static void HandleSqliteError() {
753    const GURL kOrigin(kOrigin1Url);
754    const std::string kOriginId =
755        webkit_database::GetIdentifierFromOrigin(kOrigin);
756    const base::string16 kName(ASCIIToUTF16("name"));
757    const base::string16 kDescription(ASCIIToUTF16("description"));
758
759    // Initialize a tracker database, no need to put it on disk.
760    const bool kUseInMemoryTrackerDatabase = true;
761    base::ScopedTempDir temp_dir;
762    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
763    scoped_refptr<DatabaseTracker> tracker(
764        new DatabaseTracker(temp_dir.path(), kUseInMemoryTrackerDatabase,
765                            NULL, NULL, NULL));
766
767    // Setup to observe OnScheduledForDelete notifications.
768    TestObserver observer(false, true);
769    tracker->AddObserver(&observer);
770
771    // Verify does no harm when there is no such database.
772    tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
773    EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
774    EXPECT_FALSE(observer.DidReceiveNewNotification());
775
776    // --------------------------------------------------------
777    // Create a record of a database in the tracker db and create
778    // a spoof_db_file on disk in the expected location.
779    int64 database_size = 0;
780    tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
781                            &database_size);
782    base::FilePath spoof_db_file = tracker->GetFullDBFilePath(kOriginId, kName);
783    EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
784    EXPECT_TRUE(file_util::CreateDirectory(spoof_db_file.DirName()));
785    EXPECT_TRUE(EnsureFileOfSize(spoof_db_file, 1));
786
787    // Verify does no harm with a non-error is reported.
788    tracker->HandleSqliteError(kOriginId, kName, SQLITE_OK);
789    EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
790    EXPECT_FALSE(observer.DidReceiveNewNotification());
791
792    // Verify that with a connection open, the db is scheduled for deletion,
793    // but that the file still exists.
794    tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
795    EXPECT_TRUE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
796    EXPECT_TRUE(observer.DidReceiveNewNotification());
797    EXPECT_TRUE(base::PathExists(spoof_db_file));
798
799    // Verify that once closed, the file is deleted and the record in the
800    // tracker db is removed.
801    tracker->DatabaseClosed(kOriginId, kName);
802    EXPECT_FALSE(base::PathExists(spoof_db_file));
803    EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
804
805    // --------------------------------------------------------
806    // Create another record of a database in the tracker db and create
807    // a spoof_db_file on disk in the expected location.
808    tracker->DatabaseOpened(kOriginId, kName, kDescription, 0,
809                            &database_size);
810    base::FilePath spoof_db_file2 = tracker->GetFullDBFilePath(kOriginId, kName);
811    EXPECT_FALSE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
812    EXPECT_NE(spoof_db_file, spoof_db_file2);
813    EXPECT_TRUE(file_util::CreateDirectory(spoof_db_file2.DirName()));
814    EXPECT_TRUE(EnsureFileOfSize(spoof_db_file2, 1));
815
816    // Verify that with no connection open, the db is deleted immediately.
817    tracker->DatabaseClosed(kOriginId, kName);
818    tracker->HandleSqliteError(kOriginId, kName, SQLITE_CORRUPT);
819    EXPECT_FALSE(tracker->IsDatabaseScheduledForDeletion(kOriginId, kName));
820    EXPECT_FALSE(observer.DidReceiveNewNotification());
821    EXPECT_TRUE(tracker->GetFullDBFilePath(kOriginId, kName).empty());
822    EXPECT_FALSE(base::PathExists(spoof_db_file2));
823
824    tracker->RemoveObserver(&observer);
825  }
826};
827
828TEST(DatabaseTrackerTest, DeleteOpenDatabase) {
829  DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(false);
830}
831
832TEST(DatabaseTrackerTest, DeleteOpenDatabaseIncognitoMode) {
833  DatabaseTracker_TestHelper_Test::TestDeleteOpenDatabase(true);
834}
835
836TEST(DatabaseTrackerTest, DatabaseTracker) {
837  DatabaseTracker_TestHelper_Test::TestDatabaseTracker(false);
838}
839
840TEST(DatabaseTrackerTest, DatabaseTrackerIncognitoMode) {
841  DatabaseTracker_TestHelper_Test::TestDatabaseTracker(true);
842}
843
844TEST(DatabaseTrackerTest, DatabaseTrackerQuotaIntegration) {
845  // There is no difference in behavior between incognito and not.
846  DatabaseTracker_TestHelper_Test::DatabaseTrackerQuotaIntegration();
847}
848
849TEST(DatabaseTrackerTest, DatabaseTrackerClearSessionOnlyDatabasesOnExit) {
850  // Only works for regular mode.
851  DatabaseTracker_TestHelper_Test::
852      DatabaseTrackerClearSessionOnlyDatabasesOnExit();
853}
854
855TEST(DatabaseTrackerTest, DatabaseTrackerSetForceKeepSessionState) {
856  // Only works for regular mode.
857  DatabaseTracker_TestHelper_Test::DatabaseTrackerSetForceKeepSessionState();
858}
859
860TEST(DatabaseTrackerTest, EmptyDatabaseNameIsValid) {
861  DatabaseTracker_TestHelper_Test::EmptyDatabaseNameIsValid();
862}
863
864TEST(DatabaseTrackerTest, HandleSqliteError) {
865  DatabaseTracker_TestHelper_Test::HandleSqliteError();
866}
867
868}  // namespace webkit_database
869