blob: 014a56935da4382ff37f13d1364be80b75b59b37 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/file_util.h"
#include "base/scoped_temp_dir.h"
#include "base/stringprintf.h"
#include "sql/connection.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "webkit/appcache/appcache_database.h"
#include "webkit/appcache/appcache_entry.h"
namespace {
const base::Time kZeroTime;
class TestErrorDelegate : public sql::ErrorDelegate {
public:
virtual ~TestErrorDelegate() { }
virtual int OnError(
int error, sql::Connection* connection, sql::Statement* stmt) {
return error;
}
};
} // namespace
namespace appcache {
class AppCacheDatabaseTest {};
TEST(AppCacheDatabaseTest, LazyOpen) {
// Use an empty file path to use an in-memory sqlite database.
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_FALSE(db.LazyOpen(false));
EXPECT_TRUE(db.LazyOpen(true));
int64 group_id, cache_id, response_id, deleteable_response_rowid;
group_id = cache_id = response_id = deleteable_response_rowid = 0;
EXPECT_TRUE(db.FindLastStorageIds(&group_id, &cache_id, &response_id,
&deleteable_response_rowid));
EXPECT_EQ(0, group_id);
EXPECT_EQ(0, cache_id);
EXPECT_EQ(0, response_id);
EXPECT_EQ(0, deleteable_response_rowid);
std::set<GURL> origins;
EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
EXPECT_TRUE(origins.empty());
}
TEST(AppCacheDatabaseTest, ReCreate) {
// Real files on disk for this test.
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
const FilePath kNestedDir = temp_dir.path().AppendASCII("nested");
const FilePath kOtherFile = kNestedDir.AppendASCII("other_file");
EXPECT_TRUE(file_util::CreateDirectory(kNestedDir));
EXPECT_EQ(3, file_util::WriteFile(kOtherFile, "foo", 3));
AppCacheDatabase db(kDbFile);
EXPECT_FALSE(db.LazyOpen(false));
EXPECT_TRUE(db.LazyOpen(true));
EXPECT_TRUE(file_util::PathExists(kDbFile));
EXPECT_TRUE(file_util::DirectoryExists(kNestedDir));
EXPECT_TRUE(file_util::PathExists(kOtherFile));
EXPECT_TRUE(db.DeleteExistingAndCreateNewDatabase());
EXPECT_TRUE(file_util::PathExists(kDbFile));
EXPECT_FALSE(file_util::DirectoryExists(kNestedDir));
EXPECT_FALSE(file_util::PathExists(kOtherFile));
}
TEST(AppCacheDatabaseTest, EntryRecords) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
// Set an error delegate that will make all operations return false on error.
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
AppCacheDatabase::EntryRecord entry;
entry.cache_id = 1;
entry.url = GURL("http://blah/1");
entry.flags = AppCacheEntry::MASTER;
entry.response_id = 1;
entry.response_size = 100;
EXPECT_TRUE(db.InsertEntry(&entry));
EXPECT_FALSE(db.InsertEntry(&entry));
entry.cache_id = 2;
entry.url = GURL("http://blah/2");
entry.flags = AppCacheEntry::EXPLICIT;
entry.response_id = 2;
entry.response_size = 200;
EXPECT_TRUE(db.InsertEntry(&entry));
entry.cache_id = 2;
entry.url = GURL("http://blah/3");
entry.flags = AppCacheEntry::MANIFEST;
entry.response_id = 3;
entry.response_size = 300;
EXPECT_TRUE(db.InsertEntry(&entry));
std::vector<AppCacheDatabase::EntryRecord> found;
EXPECT_TRUE(db.FindEntriesForCache(1, &found));
EXPECT_EQ(1U, found.size());
EXPECT_EQ(1, found[0].cache_id);
EXPECT_EQ(GURL("http://blah/1"), found[0].url);
EXPECT_EQ(AppCacheEntry::MASTER, found[0].flags);
EXPECT_EQ(1, found[0].response_id);
EXPECT_EQ(100, found[0].response_size);
found.clear();
EXPECT_TRUE(db.AddEntryFlags(GURL("http://blah/1"), 1,
AppCacheEntry::FOREIGN));
EXPECT_TRUE(db.FindEntriesForCache(1, &found));
EXPECT_EQ(1U, found.size());
EXPECT_EQ(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, found[0].flags);
found.clear();
EXPECT_TRUE(db.FindEntriesForCache(2, &found));
EXPECT_EQ(2U, found.size());
EXPECT_EQ(2, found[0].cache_id);
EXPECT_EQ(GURL("http://blah/2"), found[0].url);
EXPECT_EQ(AppCacheEntry::EXPLICIT, found[0].flags);
EXPECT_EQ(2, found[0].response_id);
EXPECT_EQ(200, found[0].response_size);
EXPECT_EQ(2, found[1].cache_id);
EXPECT_EQ(GURL("http://blah/3"), found[1].url);
EXPECT_EQ(AppCacheEntry::MANIFEST, found[1].flags);
EXPECT_EQ(3, found[1].response_id);
EXPECT_EQ(300, found[1].response_size);
found.clear();
EXPECT_TRUE(db.DeleteEntriesForCache(2));
EXPECT_TRUE(db.FindEntriesForCache(2, &found));
EXPECT_TRUE(found.empty());
found.clear();
EXPECT_TRUE(db.DeleteEntriesForCache(1));
EXPECT_FALSE(db.AddEntryFlags(GURL("http://blah/1"), 1,
AppCacheEntry::FOREIGN));
}
TEST(AppCacheDatabaseTest, CacheRecords) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
const AppCacheDatabase::CacheRecord kZeroRecord;
AppCacheDatabase::CacheRecord record;
EXPECT_FALSE(db.FindCache(1, &record));
record.cache_id = 1;
record.group_id = 1;
record.online_wildcard = true;
record.update_time = kZeroTime;
record.cache_size = 100;
EXPECT_TRUE(db.InsertCache(&record));
EXPECT_FALSE(db.InsertCache(&record));
record = kZeroRecord;
EXPECT_TRUE(db.FindCache(1, &record));
EXPECT_EQ(1, record.cache_id);
EXPECT_EQ(1, record.group_id);
EXPECT_TRUE(record.online_wildcard);
EXPECT_TRUE(kZeroTime == record.update_time);
EXPECT_EQ(100, record.cache_size);
record = kZeroRecord;
EXPECT_TRUE(db.FindCacheForGroup(1, &record));
EXPECT_EQ(1, record.cache_id);
EXPECT_EQ(1, record.group_id);
EXPECT_TRUE(record.online_wildcard);
EXPECT_TRUE(kZeroTime == record.update_time);
EXPECT_EQ(100, record.cache_size);
EXPECT_TRUE(db.DeleteCache(1));
EXPECT_FALSE(db.FindCache(1, &record));
EXPECT_FALSE(db.FindCacheForGroup(1, &record));
EXPECT_TRUE(db.DeleteCache(1));
}
TEST(AppCacheDatabaseTest, GroupRecords) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
const GURL kManifestUrl("http://blah/manifest");
const GURL kOrigin(kManifestUrl.GetOrigin());
const base::Time kLastAccessTime = base::Time::Now();
const base::Time kCreationTime =
kLastAccessTime - base::TimeDelta::FromDays(7);
const AppCacheDatabase::GroupRecord kZeroRecord;
AppCacheDatabase::GroupRecord record;
std::vector<AppCacheDatabase::GroupRecord> records;
// Behavior with an empty table
EXPECT_FALSE(db.FindGroup(1, &record));
EXPECT_FALSE(db.FindGroupForManifestUrl(kManifestUrl, &record));
EXPECT_TRUE(db.DeleteGroup(1));
EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
EXPECT_TRUE(records.empty());
EXPECT_FALSE(db.FindGroupForCache(1, &record));
record.group_id = 1;
record.manifest_url = kManifestUrl;
record.origin = kOrigin;
record.last_access_time = kLastAccessTime;
record.creation_time = kCreationTime;
EXPECT_TRUE(db.InsertGroup(&record));
EXPECT_FALSE(db.InsertGroup(&record));
record.group_id = 2;
EXPECT_FALSE(db.InsertGroup(&record));
record = kZeroRecord;
EXPECT_TRUE(db.FindGroup(1, &record));
EXPECT_EQ(1, record.group_id);
EXPECT_EQ(kManifestUrl, record.manifest_url);
EXPECT_EQ(kOrigin, record.origin);
EXPECT_EQ(kCreationTime.ToInternalValue(),
record.creation_time.ToInternalValue());
EXPECT_EQ(kLastAccessTime.ToInternalValue(),
record.last_access_time.ToInternalValue());
record = kZeroRecord;
EXPECT_TRUE(db.FindGroupForManifestUrl(kManifestUrl, &record));
EXPECT_EQ(1, record.group_id);
EXPECT_EQ(kManifestUrl, record.manifest_url);
EXPECT_EQ(kOrigin, record.origin);
EXPECT_EQ(kCreationTime.ToInternalValue(),
record.creation_time.ToInternalValue());
EXPECT_EQ(kLastAccessTime.ToInternalValue(),
record.last_access_time.ToInternalValue());
record.group_id = 2;
record.manifest_url = kOrigin;
record.origin = kOrigin;
record.last_access_time = kLastAccessTime;
record.creation_time = kCreationTime;
EXPECT_TRUE(db.InsertGroup(&record));
record = kZeroRecord;
EXPECT_TRUE(db.FindGroupForManifestUrl(kOrigin, &record));
EXPECT_EQ(2, record.group_id);
EXPECT_EQ(kOrigin, record.manifest_url);
EXPECT_EQ(kOrigin, record.origin);
EXPECT_EQ(kCreationTime.ToInternalValue(),
record.creation_time.ToInternalValue());
EXPECT_EQ(kLastAccessTime.ToInternalValue(),
record.last_access_time.ToInternalValue());
EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
EXPECT_EQ(2U, records.size());
EXPECT_EQ(1, records[0].group_id);
EXPECT_EQ(kManifestUrl, records[0].manifest_url);
EXPECT_EQ(kOrigin, records[0].origin);
EXPECT_EQ(2, records[1].group_id);
EXPECT_EQ(kOrigin, records[1].manifest_url);
EXPECT_EQ(kOrigin, records[1].origin);
EXPECT_TRUE(db.DeleteGroup(1));
records.clear();
EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
EXPECT_EQ(1U, records.size());
EXPECT_EQ(2, records[0].group_id);
EXPECT_EQ(kOrigin, records[0].manifest_url);
EXPECT_EQ(kOrigin, records[0].origin);
EXPECT_EQ(kCreationTime.ToInternalValue(),
record.creation_time.ToInternalValue());
EXPECT_EQ(kLastAccessTime.ToInternalValue(),
record.last_access_time.ToInternalValue());
std::set<GURL> origins;
EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
EXPECT_EQ(1U, origins.size());
EXPECT_EQ(kOrigin, *(origins.begin()));
const GURL kManifest2("http://blah2/manifest");
const GURL kOrigin2(kManifest2.GetOrigin());
record.group_id = 1;
record.manifest_url = kManifest2;
record.origin = kOrigin2;
EXPECT_TRUE(db.InsertGroup(&record));
origins.clear();
EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
EXPECT_EQ(2U, origins.size());
EXPECT_TRUE(origins.end() != origins.find(kOrigin));
EXPECT_TRUE(origins.end() != origins.find(kOrigin2));
AppCacheDatabase::CacheRecord cache_record;
cache_record.cache_id = 1;
cache_record.group_id = 1;
cache_record.online_wildcard = true;
cache_record.update_time = kZeroTime;
EXPECT_TRUE(db.InsertCache(&cache_record));
record = kZeroRecord;
EXPECT_TRUE(db.FindGroupForCache(1, &record));
EXPECT_EQ(1, record.group_id);
EXPECT_EQ(kManifest2, record.manifest_url);
EXPECT_EQ(kOrigin2, record.origin);
}
TEST(AppCacheDatabaseTest, NamespaceRecords) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
const GURL kFooNameSpace1("http://foo/namespace1");
const GURL kFooNameSpace2("http://foo/namespace2");
const GURL kFooFallbackEntry("http://foo/entry");
const GURL kFooOrigin(kFooNameSpace1.GetOrigin());
const GURL kBarNameSpace1("http://bar/namespace1");
const GURL kBarNameSpace2("http://bar/namespace2");
const GURL kBarFallbackEntry("http://bar/entry");
const GURL kBarOrigin(kBarNameSpace1.GetOrigin());
const AppCacheDatabase::NamespaceRecord kZeroRecord;
AppCacheDatabase::NamespaceRecord record;
std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
// Behavior with an empty table
EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
EXPECT_TRUE(fallbacks.empty());
EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
EXPECT_TRUE(fallbacks.empty());
EXPECT_TRUE(db.DeleteNamespacesForCache(1));
// Two records for two differenent caches in the Foo origin.
record.cache_id = 1;
record.origin = kFooOrigin;
record.namespace_url = kFooNameSpace1;
record.target_url = kFooFallbackEntry;
EXPECT_TRUE(db.InsertNamespace(&record));
EXPECT_FALSE(db.InsertNamespace(&record));
record.cache_id = 2;
record.origin = kFooOrigin;
record.namespace_url = kFooNameSpace2;
record.target_url = kFooFallbackEntry;
EXPECT_TRUE(db.InsertNamespace(&record));
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
EXPECT_EQ(1U, fallbacks.size());
EXPECT_EQ(1, fallbacks[0].cache_id);
EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_url);
EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url);
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForCache(2, &intercepts, &fallbacks));
EXPECT_EQ(1U, fallbacks.size());
EXPECT_EQ(2, fallbacks[0].cache_id);
EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_url);
EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url);
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
EXPECT_EQ(2U, fallbacks.size());
EXPECT_EQ(1, fallbacks[0].cache_id);
EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_url);
EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url);
EXPECT_EQ(2, fallbacks[1].cache_id);
EXPECT_EQ(kFooOrigin, fallbacks[1].origin);
EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_url);
EXPECT_EQ(kFooFallbackEntry, fallbacks[1].target_url);
EXPECT_TRUE(db.DeleteNamespacesForCache(1));
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
EXPECT_EQ(1U, fallbacks.size());
EXPECT_EQ(2, fallbacks[0].cache_id);
EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_url);
EXPECT_EQ(kFooFallbackEntry, fallbacks[0].target_url);
// Two more records for the same cache in the Bar origin.
record.cache_id = 3;
record.origin = kBarOrigin;
record.namespace_url = kBarNameSpace1;
record.target_url = kBarFallbackEntry;
EXPECT_TRUE(db.InsertNamespace(&record));
record.cache_id = 3;
record.origin = kBarOrigin;
record.namespace_url = kBarNameSpace2;
record.target_url = kBarFallbackEntry;
EXPECT_TRUE(db.InsertNamespace(&record));
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForCache(3, &intercepts, &fallbacks));
EXPECT_EQ(2U, fallbacks.size());
fallbacks.clear();
EXPECT_TRUE(db.FindNamespacesForOrigin(kBarOrigin, &intercepts, &fallbacks));
EXPECT_EQ(2U, fallbacks.size());
}
TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
const GURL kFooNameSpace1("http://foo/namespace1");
const GURL kFooNameSpace2("http://foo/namespace2");
const GURL kBarNameSpace1("http://bar/namespace1");
const AppCacheDatabase::OnlineWhiteListRecord kZeroRecord;
AppCacheDatabase::OnlineWhiteListRecord record;
std::vector<AppCacheDatabase::OnlineWhiteListRecord> records;
// Behavior with an empty table
EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
EXPECT_TRUE(records.empty());
EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
record.cache_id = 1;
record.namespace_url = kFooNameSpace1;
EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
record.namespace_url = kFooNameSpace2;
EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
records.clear();
EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
EXPECT_EQ(2U, records.size());
EXPECT_EQ(1, records[0].cache_id);
EXPECT_EQ(kFooNameSpace1, records[0].namespace_url);
EXPECT_EQ(1, records[1].cache_id);
EXPECT_EQ(kFooNameSpace2, records[1].namespace_url);
record.cache_id = 2;
record.namespace_url = kBarNameSpace1;
EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
records.clear();
EXPECT_TRUE(db.FindOnlineWhiteListForCache(2, &records));
EXPECT_EQ(1U, records.size());
EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
records.clear();
EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
EXPECT_TRUE(records.empty());
}
TEST(AppCacheDatabaseTest, DeletableResponseIds) {
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
std::vector<int64> ids;
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
EXPECT_TRUE(ids.empty());
ids.push_back(0);
EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
ids.clear();
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
EXPECT_EQ(1U, ids.size());
EXPECT_EQ(0, ids[0]);
int64 unused, deleteable_response_rowid;
unused = deleteable_response_rowid = 0;
EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
&deleteable_response_rowid));
EXPECT_EQ(1, deleteable_response_rowid);
// Expected to fail due to the duplicate id, 0 is already in the table.
ids.clear();
ids.push_back(0);
ids.push_back(1);
EXPECT_FALSE(db.InsertDeletableResponseIds(ids));
ids.clear();
for (int i = 1; i < 10; ++i)
ids.push_back(i);
EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
&deleteable_response_rowid));
EXPECT_EQ(10, deleteable_response_rowid);
ids.clear();
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
EXPECT_EQ(10U, ids.size());
for (int i = 0; i < 10; ++i)
EXPECT_EQ(i, ids[i]);
// Ensure the limit is respected.
ids.clear();
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 5));
EXPECT_EQ(5U, ids.size());
for (int i = 0; i < static_cast<int>(ids.size()); ++i)
EXPECT_EQ(i, ids[i]);
// Ensure the max_rowid is respected (the first rowid is 1).
ids.clear();
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, 5, 100));
EXPECT_EQ(5U, ids.size());
for (int i = 0; i < static_cast<int>(ids.size()); ++i)
EXPECT_EQ(i, ids[i]);
// Ensure that we can delete from the table.
EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
ids.clear();
EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
EXPECT_EQ(5U, ids.size());
for (int i = 0; i < static_cast<int>(ids.size()); ++i)
EXPECT_EQ(i + 5, ids[i]);
}
TEST(AppCacheDatabaseTest, OriginUsage) {
const GURL kManifestUrl("http://blah/manifest");
const GURL kManifestUrl2("http://blah/manifest2");
const GURL kOrigin(kManifestUrl.GetOrigin());
const GURL kOtherOriginManifestUrl("http://other/manifest");
const GURL kOtherOrigin(kOtherOriginManifestUrl.GetOrigin());
const FilePath kEmptyPath;
AppCacheDatabase db(kEmptyPath);
EXPECT_TRUE(db.LazyOpen(true));
scoped_refptr<TestErrorDelegate> error_delegate(new TestErrorDelegate);
db.db_->set_error_delegate(error_delegate);
std::vector<AppCacheDatabase::CacheRecord> cache_records;
EXPECT_EQ(0, db.GetOriginUsage(kOrigin));
EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
EXPECT_TRUE(cache_records.empty());
AppCacheDatabase::GroupRecord group_record;
group_record.group_id = 1;
group_record.manifest_url = kManifestUrl;
group_record.origin = kOrigin;
EXPECT_TRUE(db.InsertGroup(&group_record));
AppCacheDatabase::CacheRecord cache_record;
cache_record.cache_id = 1;
cache_record.group_id = 1;
cache_record.online_wildcard = true;
cache_record.update_time = kZeroTime;
cache_record.cache_size = 100;
EXPECT_TRUE(db.InsertCache(&cache_record));
EXPECT_EQ(100, db.GetOriginUsage(kOrigin));
group_record.group_id = 2;
group_record.manifest_url = kManifestUrl2;
group_record.origin = kOrigin;
EXPECT_TRUE(db.InsertGroup(&group_record));
cache_record.cache_id = 2;
cache_record.group_id = 2;
cache_record.online_wildcard = true;
cache_record.update_time = kZeroTime;
cache_record.cache_size = 1000;
EXPECT_TRUE(db.InsertCache(&cache_record));
EXPECT_EQ(1100, db.GetOriginUsage(kOrigin));
group_record.group_id = 3;
group_record.manifest_url = kOtherOriginManifestUrl;
group_record.origin = kOtherOrigin;
EXPECT_TRUE(db.InsertGroup(&group_record));
cache_record.cache_id = 3;
cache_record.group_id = 3;
cache_record.online_wildcard = true;
cache_record.update_time = kZeroTime;
cache_record.cache_size = 5000;
EXPECT_TRUE(db.InsertCache(&cache_record));
EXPECT_EQ(5000, db.GetOriginUsage(kOtherOrigin));
EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
EXPECT_EQ(2U, cache_records.size());
cache_records.clear();
EXPECT_TRUE(db.FindCachesForOrigin(kOtherOrigin, &cache_records));
EXPECT_EQ(1U, cache_records.size());
std::map<GURL, int64> usage_map;
EXPECT_TRUE(db.GetAllOriginUsage(&usage_map));
EXPECT_EQ(2U, usage_map.size());
EXPECT_EQ(1100, usage_map[kOrigin]);
EXPECT_EQ(5000, usage_map[kOtherOrigin]);
}
TEST(AppCacheDatabaseTest, UpgradeSchema3to4) {
// Real file on disk for this test.
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const FilePath kDbFile = temp_dir.path().AppendASCII("upgrade.db");
const GURL kMockOrigin("http://mockorigin/");
const char kNamespaceUrlFormat[] = "namespace%d";
const char kTargetUrlFormat[] = "target%d";
const int kNumNamespaces = 10;
// Create a v3 schema based database containing some fallback records.
{
const int kVersion3 = 3;
const char kGroupsTable[] = "Groups";
const char kCachesTable[] = "Caches";
const char kEntriesTable[] = "Entries";
const char kFallbackNameSpacesTable[] = "FallbackNameSpaces";
const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
const struct {
const char* table_name;
const char* columns;
} kTables3[] = {
{ kGroupsTable,
"(group_id INTEGER PRIMARY KEY,"
" origin TEXT,"
" manifest_url TEXT,"
" creation_time INTEGER,"
" last_access_time INTEGER)" },
{ kCachesTable,
"(cache_id INTEGER PRIMARY KEY,"
" group_id INTEGER,"
" online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
" update_time INTEGER,"
" cache_size INTEGER)" }, // intentionally not normalized
{ kEntriesTable,
"(cache_id INTEGER,"
" url TEXT,"
" flags INTEGER,"
" response_id INTEGER,"
" response_size INTEGER)" },
{ kFallbackNameSpacesTable,
"(cache_id INTEGER,"
" origin TEXT," // intentionally not normalized
" namespace_url TEXT,"
" fallback_entry_url TEXT)" },
{ kOnlineWhiteListsTable,
"(cache_id INTEGER,"
" namespace_url TEXT)" },
{ kDeletableResponseIdsTable,
"(response_id INTEGER NOT NULL)" },
};
const struct {
const char* index_name;
const char* table_name;
const char* columns;
bool unique;
} kIndexes3[] = {
{ "GroupsOriginIndex",
kGroupsTable,
"(origin)",
false },
{ "GroupsManifestIndex",
kGroupsTable,
"(manifest_url)",
true },
{ "CachesGroupIndex",
kCachesTable,
"(group_id)",
false },
{ "EntriesCacheIndex",
kEntriesTable,
"(cache_id)",
false },
{ "EntriesCacheAndUrlIndex",
kEntriesTable,
"(cache_id, url)",
true },
{ "EntriesResponseIdIndex",
kEntriesTable,
"(response_id)",
true },
{ "FallbackNameSpacesCacheIndex",
kFallbackNameSpacesTable,
"(cache_id)",
false },
{ "FallbackNameSpacesOriginIndex",
kFallbackNameSpacesTable,
"(origin)",
false },
{ "FallbackNameSpacesCacheAndUrlIndex",
kFallbackNameSpacesTable,
"(cache_id, namespace_url)",
true },
{ "OnlineWhiteListCacheIndex",
kOnlineWhiteListsTable,
"(cache_id)",
false },
{ "DeletableResponsesIdIndex",
kDeletableResponseIdsTable,
"(response_id)",
true },
};
const int kTableCount3 = ARRAYSIZE_UNSAFE(kTables3);
const int kIndexCount3 = ARRAYSIZE_UNSAFE(kIndexes3);
sql::Connection connection;
EXPECT_TRUE(connection.Open(kDbFile));
sql::Transaction transaction(&connection);
EXPECT_TRUE(transaction.Begin());
sql::MetaTable meta_table;
EXPECT_TRUE(meta_table.Init(&connection, kVersion3, kVersion3));
for (int i = 0; i < kTableCount3; ++i) {
std::string sql("CREATE TABLE ");
sql += kTables3[i].table_name;
sql += kTables3[i].columns;
EXPECT_TRUE(connection.Execute(sql.c_str()));
}
for (int i = 0; i < kIndexCount3; ++i) {
std::string sql;
if (kIndexes3[i].unique)
sql += "CREATE UNIQUE INDEX ";
else
sql += "CREATE INDEX ";
sql += kIndexes3[i].index_name;
sql += " ON ";
sql += kIndexes3[i].table_name;
sql += kIndexes3[i].columns;
EXPECT_TRUE(connection.Execute(sql.c_str()));
}
const char* kSql =
"INSERT INTO FallbackNameSpaces"
" (cache_id, origin, namespace_url, fallback_entry_url)"
" VALUES (?, ?, ?, ?)";
sql::Statement statement;
statement.Assign(connection.GetUniqueStatement(kSql));
EXPECT_TRUE(statement.is_valid());
for (int i = 0; i < kNumNamespaces; ++i) {
GURL namespace_url(
kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
GURL target_url(
kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
statement.BindInt64(0, i);
statement.BindString(1, kMockOrigin.spec().c_str());
statement.BindString(2, namespace_url.spec().c_str());
statement.BindString(3, target_url.spec().c_str());
ASSERT_TRUE(statement.Run());
statement.Reset();
}
EXPECT_TRUE(transaction.Commit());
}
// Open that database and verify that it got updated.
AppCacheDatabase db(kDbFile);
EXPECT_TRUE(db.LazyOpen(true));
EXPECT_FALSE(db.db_->DoesTableExist("FallbackNameSpaces"));
EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNamesSpacesCacheIndex"));
EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesOriginIndex"));
EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesCacheAndUrlIndex"));
EXPECT_TRUE(db.db_->DoesTableExist("Namespaces"));
EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheIndex"));
EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesOriginIndex"));
EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheAndUrlIndex"));
EXPECT_EQ(4, db.meta_table_->GetVersionNumber());
EXPECT_EQ(4, db.meta_table_->GetCompatibleVersionNumber());
std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
&fallbacks));
EXPECT_TRUE(intercepts.empty());
EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
for (int i = 0; i < kNumNamespaces; ++i) {
GURL expected_namespace_url(
kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
GURL expected_target_url(
kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
EXPECT_EQ(i, fallbacks[i].cache_id);
EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].type);
EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_url);
EXPECT_EQ(expected_target_url, fallbacks[i].target_url);
}
}
} // namespace appcache