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.
10 #include "base/callback.h"
11 #include "base/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/message_loop/message_loop.h"
14 #include "sql/connection.h"
15 #include "sql/meta_table.h"
16 #include "sql/statement.h"
17 #include "sql/transaction.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 #include "webkit/browser/quota/mock_special_storage_policy.h"
21 #include "webkit/browser/quota/quota_database.h"
26 const base::Time kZeroTime;
30 class QuotaDatabaseTest : public testing::Test {
32 typedef QuotaDatabase::QuotaTableEntry QuotaTableEntry;
33 typedef QuotaDatabase::QuotaTableCallback QuotaTableCallback;
34 typedef QuotaDatabase::OriginInfoTableCallback
35 OriginInfoTableCallback;
37 void LazyOpen(const base::FilePath& kDbFile) {
38 QuotaDatabase db(kDbFile);
39 EXPECT_FALSE(db.LazyOpen(false));
40 ASSERT_TRUE(db.LazyOpen(true));
41 EXPECT_TRUE(db.db_.get());
42 EXPECT_TRUE(kDbFile.empty() || base::PathExists(kDbFile));
45 void UpgradeSchemaV2toV3(const base::FilePath& kDbFile) {
46 const QuotaTableEntry entries[] = {
47 QuotaTableEntry("a", kStorageTypeTemporary, 1),
48 QuotaTableEntry("b", kStorageTypeTemporary, 2),
49 QuotaTableEntry("c", kStorageTypePersistent, 3),
52 CreateV2Database(kDbFile, entries, ARRAYSIZE_UNSAFE(entries));
54 QuotaDatabase db(kDbFile);
55 EXPECT_TRUE(db.LazyOpen(true));
56 EXPECT_TRUE(db.db_.get());
58 typedef EntryVerifier<QuotaTableEntry> Verifier;
59 Verifier verifier(entries, entries + ARRAYSIZE_UNSAFE(entries));
60 EXPECT_TRUE(db.DumpQuotaTable(
61 new QuotaTableCallback(
62 base::Bind(&Verifier::Run,
63 base::Unretained(&verifier)))));
64 EXPECT_TRUE(verifier.table.empty());
67 void HostQuota(const base::FilePath& kDbFile) {
68 QuotaDatabase db(kDbFile);
69 ASSERT_TRUE(db.LazyOpen(true));
71 const char* kHost = "foo.com";
72 const int kQuota1 = 13579;
73 const int kQuota2 = kQuota1 + 1024;
76 EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, "a));
77 EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, "a));
79 // Insert quota for temporary.
80 EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota1));
81 EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, "a));
82 EXPECT_EQ(kQuota1, quota);
84 // Update quota for temporary.
85 EXPECT_TRUE(db.SetHostQuota(kHost, kStorageTypeTemporary, kQuota2));
86 EXPECT_TRUE(db.GetHostQuota(kHost, kStorageTypeTemporary, "a));
87 EXPECT_EQ(kQuota2, quota);
89 // Quota for persistent must not be updated.
90 EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypePersistent, "a));
92 // Delete temporary storage quota.
93 EXPECT_TRUE(db.DeleteHostQuota(kHost, kStorageTypeTemporary));
94 EXPECT_FALSE(db.GetHostQuota(kHost, kStorageTypeTemporary, "a));
97 void GlobalQuota(const base::FilePath& kDbFile) {
98 QuotaDatabase db(kDbFile);
99 ASSERT_TRUE(db.LazyOpen(true));
101 const char* kTempQuotaKey = QuotaDatabase::kTemporaryQuotaOverrideKey;
102 const char* kAvailSpaceKey = QuotaDatabase::kDesiredAvailableSpaceKey;
105 const int64 kValue1 = 456;
106 const int64 kValue2 = 123000;
107 EXPECT_FALSE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
108 EXPECT_FALSE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
110 EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue1));
111 EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
112 EXPECT_EQ(kValue1, value);
114 EXPECT_TRUE(db.SetQuotaConfigValue(kTempQuotaKey, kValue2));
115 EXPECT_TRUE(db.GetQuotaConfigValue(kTempQuotaKey, &value));
116 EXPECT_EQ(kValue2, value);
118 EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue1));
119 EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
120 EXPECT_EQ(kValue1, value);
122 EXPECT_TRUE(db.SetQuotaConfigValue(kAvailSpaceKey, kValue2));
123 EXPECT_TRUE(db.GetQuotaConfigValue(kAvailSpaceKey, &value));
124 EXPECT_EQ(kValue2, value);
127 void OriginLastAccessTimeLRU(const base::FilePath& kDbFile) {
128 QuotaDatabase db(kDbFile);
129 ASSERT_TRUE(db.LazyOpen(true));
131 std::set<GURL> exceptions;
133 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
135 EXPECT_TRUE(origin.is_empty());
137 const GURL kOrigin1("http://a/");
138 const GURL kOrigin2("http://b/");
139 const GURL kOrigin3("http://c/");
140 const GURL kOrigin4("http://p/");
142 // Adding three temporary storages, and
143 EXPECT_TRUE(db.SetOriginLastAccessTime(
144 kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
145 EXPECT_TRUE(db.SetOriginLastAccessTime(
146 kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
147 EXPECT_TRUE(db.SetOriginLastAccessTime(
148 kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(30)));
151 EXPECT_TRUE(db.SetOriginLastAccessTime(
152 kOrigin4, kStorageTypePersistent, base::Time::FromInternalValue(40)));
154 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
156 EXPECT_EQ(kOrigin1.spec(), origin.spec());
158 // Test that unlimited origins are exluded from eviction, but
159 // protected origins are not excluded.
160 scoped_refptr<MockSpecialStoragePolicy> policy(
161 new MockSpecialStoragePolicy);
162 policy->AddUnlimited(kOrigin1);
163 policy->AddProtected(kOrigin2);
164 EXPECT_TRUE(db.GetLRUOrigin(
165 kStorageTypeTemporary, exceptions, policy.get(), &origin));
166 EXPECT_EQ(kOrigin2.spec(), origin.spec());
168 exceptions.insert(kOrigin1);
169 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
171 EXPECT_EQ(kOrigin2.spec(), origin.spec());
173 exceptions.insert(kOrigin2);
174 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
176 EXPECT_EQ(kOrigin3.spec(), origin.spec());
178 exceptions.insert(kOrigin3);
179 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
181 EXPECT_TRUE(origin.is_empty());
183 EXPECT_TRUE(db.SetOriginLastAccessTime(
184 kOrigin1, kStorageTypeTemporary, base::Time::Now()));
186 // Delete origin/type last access time information.
187 EXPECT_TRUE(db.DeleteOriginInfo(kOrigin3, kStorageTypeTemporary));
189 // Querying again to see if the deletion has worked.
191 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
193 EXPECT_EQ(kOrigin2.spec(), origin.spec());
195 exceptions.insert(kOrigin1);
196 exceptions.insert(kOrigin2);
197 EXPECT_TRUE(db.GetLRUOrigin(kStorageTypeTemporary, exceptions,
199 EXPECT_TRUE(origin.is_empty());
202 void OriginLastModifiedSince(const base::FilePath& kDbFile) {
203 QuotaDatabase db(kDbFile);
204 ASSERT_TRUE(db.LazyOpen(true));
206 std::set<GURL> origins;
207 EXPECT_TRUE(db.GetOriginsModifiedSince(
208 kStorageTypeTemporary, &origins, base::Time()));
209 EXPECT_TRUE(origins.empty());
211 const GURL kOrigin1("http://a/");
212 const GURL kOrigin2("http://b/");
213 const GURL kOrigin3("http://c/");
215 // Report last mod time for the test origins.
216 EXPECT_TRUE(db.SetOriginLastModifiedTime(
217 kOrigin1, kStorageTypeTemporary, base::Time::FromInternalValue(0)));
218 EXPECT_TRUE(db.SetOriginLastModifiedTime(
219 kOrigin2, kStorageTypeTemporary, base::Time::FromInternalValue(10)));
220 EXPECT_TRUE(db.SetOriginLastModifiedTime(
221 kOrigin3, kStorageTypeTemporary, base::Time::FromInternalValue(20)));
223 EXPECT_TRUE(db.GetOriginsModifiedSince(
224 kStorageTypeTemporary, &origins, base::Time()));
225 EXPECT_EQ(3U, origins.size());
226 EXPECT_EQ(1U, origins.count(kOrigin1));
227 EXPECT_EQ(1U, origins.count(kOrigin2));
228 EXPECT_EQ(1U, origins.count(kOrigin3));
230 EXPECT_TRUE(db.GetOriginsModifiedSince(
231 kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
232 EXPECT_EQ(2U, origins.size());
233 EXPECT_EQ(0U, origins.count(kOrigin1));
234 EXPECT_EQ(1U, origins.count(kOrigin2));
235 EXPECT_EQ(1U, origins.count(kOrigin3));
237 EXPECT_TRUE(db.GetOriginsModifiedSince(
238 kStorageTypeTemporary, &origins, base::Time::FromInternalValue(15)));
239 EXPECT_EQ(1U, origins.size());
240 EXPECT_EQ(0U, origins.count(kOrigin1));
241 EXPECT_EQ(0U, origins.count(kOrigin2));
242 EXPECT_EQ(1U, origins.count(kOrigin3));
244 EXPECT_TRUE(db.GetOriginsModifiedSince(
245 kStorageTypeTemporary, &origins, base::Time::FromInternalValue(25)));
246 EXPECT_TRUE(origins.empty());
248 // Update origin1's mod time but for persistent storage.
249 EXPECT_TRUE(db.SetOriginLastModifiedTime(
250 kOrigin1, kStorageTypePersistent, base::Time::FromInternalValue(30)));
252 // Must have no effects on temporary origins info.
253 EXPECT_TRUE(db.GetOriginsModifiedSince(
254 kStorageTypeTemporary, &origins, base::Time::FromInternalValue(5)));
255 EXPECT_EQ(2U, origins.size());
256 EXPECT_EQ(0U, origins.count(kOrigin1));
257 EXPECT_EQ(1U, origins.count(kOrigin2));
258 EXPECT_EQ(1U, origins.count(kOrigin3));
260 // One more update for persistent origin2.
261 EXPECT_TRUE(db.SetOriginLastModifiedTime(
262 kOrigin2, kStorageTypePersistent, base::Time::FromInternalValue(40)));
264 EXPECT_TRUE(db.GetOriginsModifiedSince(
265 kStorageTypePersistent, &origins, base::Time::FromInternalValue(25)));
266 EXPECT_EQ(2U, origins.size());
267 EXPECT_EQ(1U, origins.count(kOrigin1));
268 EXPECT_EQ(1U, origins.count(kOrigin2));
269 EXPECT_EQ(0U, origins.count(kOrigin3));
271 EXPECT_TRUE(db.GetOriginsModifiedSince(
272 kStorageTypePersistent, &origins, base::Time::FromInternalValue(35)));
273 EXPECT_EQ(1U, origins.size());
274 EXPECT_EQ(0U, origins.count(kOrigin1));
275 EXPECT_EQ(1U, origins.count(kOrigin2));
276 EXPECT_EQ(0U, origins.count(kOrigin3));
279 void RegisterInitialOriginInfo(const base::FilePath& kDbFile) {
280 QuotaDatabase db(kDbFile);
282 const GURL kOrigins[] = {
286 std::set<GURL> origins(kOrigins, kOrigins + ARRAYSIZE_UNSAFE(kOrigins));
288 EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
291 EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
292 kStorageTypeTemporary,
294 EXPECT_EQ(0, used_count);
296 EXPECT_TRUE(db.SetOriginLastAccessTime(
297 GURL("http://a/"), kStorageTypeTemporary,
298 base::Time::FromDoubleT(1.0)));
300 EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
301 kStorageTypeTemporary,
303 EXPECT_EQ(1, used_count);
305 EXPECT_TRUE(db.RegisterInitialOriginInfo(origins, kStorageTypeTemporary));
308 EXPECT_TRUE(db.FindOriginUsedCount(GURL("http://a/"),
309 kStorageTypeTemporary,
311 EXPECT_EQ(1, used_count);
314 template <typename EntryType>
315 struct EntryVerifier {
316 std::set<EntryType> table;
318 template <typename Iterator>
319 EntryVerifier(Iterator itr, Iterator end)
322 bool Run(const EntryType& entry) {
323 EXPECT_EQ(1u, table.erase(entry));
328 void DumpQuotaTable(const base::FilePath& kDbFile) {
329 QuotaTableEntry kTableEntries[] = {
330 QuotaTableEntry("http://go/", kStorageTypeTemporary, 1),
331 QuotaTableEntry("http://oo/", kStorageTypeTemporary, 2),
332 QuotaTableEntry("http://gle/", kStorageTypePersistent, 3)
334 QuotaTableEntry* begin = kTableEntries;
335 QuotaTableEntry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
337 QuotaDatabase db(kDbFile);
338 EXPECT_TRUE(db.LazyOpen(true));
339 AssignQuotaTable(db.db_.get(), begin, end);
342 typedef EntryVerifier<QuotaTableEntry> Verifier;
343 Verifier verifier(begin, end);
344 EXPECT_TRUE(db.DumpQuotaTable(
345 new QuotaTableCallback(
346 base::Bind(&Verifier::Run,
347 base::Unretained(&verifier)))));
348 EXPECT_TRUE(verifier.table.empty());
351 void DumpOriginInfoTable(const base::FilePath& kDbFile) {
352 base::Time now(base::Time::Now());
353 typedef QuotaDatabase::OriginInfoTableEntry Entry;
354 Entry kTableEntries[] = {
355 Entry(GURL("http://go/"), kStorageTypeTemporary, 2147483647, now, now),
356 Entry(GURL("http://oo/"), kStorageTypeTemporary, 0, now, now),
357 Entry(GURL("http://gle/"), kStorageTypeTemporary, 1, now, now),
359 Entry* begin = kTableEntries;
360 Entry* end = kTableEntries + ARRAYSIZE_UNSAFE(kTableEntries);
362 QuotaDatabase db(kDbFile);
363 EXPECT_TRUE(db.LazyOpen(true));
364 AssignOriginInfoTable(db.db_.get(), begin, end);
367 typedef EntryVerifier<Entry> Verifier;
368 Verifier verifier(begin, end);
369 EXPECT_TRUE(db.DumpOriginInfoTable(
370 new OriginInfoTableCallback(
371 base::Bind(&Verifier::Run,
372 base::Unretained(&verifier)))));
373 EXPECT_TRUE(verifier.table.empty());
377 template <typename Iterator>
378 void AssignQuotaTable(sql::Connection* db, Iterator itr, Iterator end) {
379 ASSERT_NE(db, (sql::Connection*)NULL);
380 for (; itr != end; ++itr) {
382 "INSERT INTO HostQuotaTable"
383 " (host, type, quota)"
385 sql::Statement statement;
386 statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
387 ASSERT_TRUE(statement.is_valid());
389 statement.BindString(0, itr->host);
390 statement.BindInt(1, static_cast<int>(itr->type));
391 statement.BindInt64(2, itr->quota);
392 EXPECT_TRUE(statement.Run());
396 template <typename Iterator>
397 void AssignOriginInfoTable(sql::Connection* db, Iterator itr, Iterator end) {
398 ASSERT_NE(db, (sql::Connection*)NULL);
399 for (; itr != end; ++itr) {
401 "INSERT INTO OriginInfoTable"
402 " (origin, type, used_count, last_access_time, last_modified_time)"
403 " VALUES (?, ?, ?, ?, ?)";
404 sql::Statement statement;
405 statement.Assign(db->GetCachedStatement(SQL_FROM_HERE, kSql));
406 ASSERT_TRUE(statement.is_valid());
408 statement.BindString(0, itr->origin.spec());
409 statement.BindInt(1, static_cast<int>(itr->type));
410 statement.BindInt(2, itr->used_count);
411 statement.BindInt64(3, itr->last_access_time.ToInternalValue());
412 statement.BindInt64(4, itr->last_modified_time.ToInternalValue());
413 EXPECT_TRUE(statement.Run());
417 bool OpenDatabase(sql::Connection* db, const base::FilePath& kDbFile) {
418 if (kDbFile.empty()) {
419 return db->OpenInMemory();
421 if (!file_util::CreateDirectory(kDbFile.DirName()))
423 if (!db->Open(kDbFile))
429 // Create V2 database and populate some data.
430 void CreateV2Database(
431 const base::FilePath& kDbFile,
432 const QuotaTableEntry* entries,
433 size_t entries_size) {
434 scoped_ptr<sql::Connection> db(new sql::Connection);
435 scoped_ptr<sql::MetaTable> meta_table(new sql::MetaTable);
437 // V2 schema definitions.
438 static const int kCurrentVersion = 2;
439 static const int kCompatibleVersion = 2;
440 static const char kHostQuotaTable[] = "HostQuotaTable";
441 static const char kOriginLastAccessTable[] = "OriginLastAccessTable";
442 static const QuotaDatabase::TableSchema kTables[] = {
444 "(host TEXT NOT NULL,"
445 " type INTEGER NOT NULL,"
447 " UNIQUE(host, type))" },
448 { kOriginLastAccessTable,
449 "(origin TEXT NOT NULL,"
450 " type INTEGER NOT NULL,"
451 " used_count INTEGER,"
452 " last_access_time INTEGER,"
453 " UNIQUE(origin, type))" },
455 static const QuotaDatabase::IndexSchema kIndexes[] = {
460 { "OriginLastAccessIndex",
461 kOriginLastAccessTable,
462 "(origin, last_access_time)",
466 ASSERT_TRUE(OpenDatabase(db.get(), kDbFile));
467 EXPECT_TRUE(QuotaDatabase::CreateSchema(
468 db.get(), meta_table.get(),
469 kCurrentVersion, kCompatibleVersion,
470 kTables, ARRAYSIZE_UNSAFE(kTables),
471 kIndexes, ARRAYSIZE_UNSAFE(kIndexes)));
473 // V2 and V3 QuotaTable are compatible, so we can simply use
474 // AssignQuotaTable to poplulate v2 database here.
475 db->BeginTransaction();
476 AssignQuotaTable(db.get(), entries, entries + entries_size);
477 db->CommitTransaction();
480 base::MessageLoop message_loop_;
483 TEST_F(QuotaDatabaseTest, LazyOpen) {
484 base::ScopedTempDir data_dir;
485 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
486 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
488 LazyOpen(base::FilePath());
491 TEST_F(QuotaDatabaseTest, UpgradeSchema) {
492 base::ScopedTempDir data_dir;
493 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
494 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
495 UpgradeSchemaV2toV3(kDbFile);
498 TEST_F(QuotaDatabaseTest, HostQuota) {
499 base::ScopedTempDir data_dir;
500 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
501 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
503 HostQuota(base::FilePath());
506 TEST_F(QuotaDatabaseTest, GlobalQuota) {
507 base::ScopedTempDir data_dir;
508 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
509 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
510 GlobalQuota(kDbFile);
511 GlobalQuota(base::FilePath());
514 TEST_F(QuotaDatabaseTest, OriginLastAccessTimeLRU) {
515 base::ScopedTempDir data_dir;
516 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
517 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
518 OriginLastAccessTimeLRU(kDbFile);
519 OriginLastAccessTimeLRU(base::FilePath());
522 TEST_F(QuotaDatabaseTest, OriginLastModifiedSince) {
523 base::ScopedTempDir data_dir;
524 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
525 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
526 OriginLastModifiedSince(kDbFile);
527 OriginLastModifiedSince(base::FilePath());
530 TEST_F(QuotaDatabaseTest, BootstrapFlag) {
531 base::ScopedTempDir data_dir;
532 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
534 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
535 QuotaDatabase db(kDbFile);
537 EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
538 EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(true));
539 EXPECT_TRUE(db.IsOriginDatabaseBootstrapped());
540 EXPECT_TRUE(db.SetOriginDatabaseBootstrapped(false));
541 EXPECT_FALSE(db.IsOriginDatabaseBootstrapped());
544 TEST_F(QuotaDatabaseTest, RegisterInitialOriginInfo) {
545 base::ScopedTempDir data_dir;
546 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
547 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
548 RegisterInitialOriginInfo(kDbFile);
549 RegisterInitialOriginInfo(base::FilePath());
552 TEST_F(QuotaDatabaseTest, DumpQuotaTable) {
553 base::ScopedTempDir data_dir;
554 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
555 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
556 DumpQuotaTable(kDbFile);
557 DumpQuotaTable(base::FilePath());
560 TEST_F(QuotaDatabaseTest, DumpOriginInfoTable) {
561 base::ScopedTempDir data_dir;
562 ASSERT_TRUE(data_dir.CreateUniqueTempDir());
563 const base::FilePath kDbFile = data_dir.path().AppendASCII("quota_manager.db");
564 DumpOriginInfoTable(kDbFile);
565 DumpOriginInfoTable(base::FilePath());