| // Copyright 2014 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 <stdint.h> |
| |
| #include <limits> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/appcache/appcache_database.h" |
| #include "content/browser/appcache/appcache_entry.h" |
| #include "sql/database.h" |
| #include "sql/meta_table.h" |
| #include "sql/statement.h" |
| #include "sql/test/scoped_error_expecter.h" |
| #include "sql/test/test_helpers.h" |
| #include "sql/transaction.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/sqlite/sqlite3.h" |
| |
| namespace { |
| |
| const base::Time kZeroTime; |
| |
| } // namespace |
| |
| namespace content { |
| |
| class AppCacheDatabaseTest : public testing::Test { |
| public: |
| AppCacheDatabaseTest() { |
| appcache_require_origin_trial_feature_.InitAndDisableFeature( |
| blink::features::kAppCacheRequireOriginTrial); |
| } |
| |
| int64_t GetCacheManifestParserVersion(const content::AppCacheDatabase& db, |
| int64_t cache_id) { |
| static const char kSql[] = |
| "SELECT manifest_parser_version, cache_id FROM Caches " |
| "WHERE " |
| "cache_id = ?"; |
| sql::Statement statement(db.db_->GetCachedStatement(SQL_FROM_HERE, kSql)); |
| statement.BindInt64(0, cache_id); |
| EXPECT_TRUE(statement.Step()); |
| return statement.ColumnInt64(0); |
| } |
| |
| std::string GetCacheManifestScope(const content::AppCacheDatabase& db, |
| int64_t cache_id) { |
| static const char kSql[] = |
| "SELECT manifest_scope, cache_id FROM Caches " |
| "WHERE " |
| "cache_id = ?"; |
| sql::Statement statement(db.db_->GetCachedStatement(SQL_FROM_HERE, kSql)); |
| statement.BindInt64(0, cache_id); |
| EXPECT_TRUE(statement.Step()); |
| return statement.ColumnString(0); |
| } |
| |
| private: |
| base::test::ScopedFeatureList appcache_require_origin_trial_feature_; |
| }; |
| |
| TEST_F(AppCacheDatabaseTest, LazyOpen) { |
| // Use an empty file path to use an in-memory sqlite database. |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| |
| EXPECT_FALSE(db.LazyOpen(false)); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| int64_t 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<url::Origin> origins; |
| EXPECT_TRUE(db.FindOriginsWithGroups(&origins)); |
| EXPECT_TRUE(origins.empty()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, ReCreate) { |
| // Real files on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = temp_dir.GetPath().AppendASCII("appcache.db"); |
| const base::FilePath kNestedDir = temp_dir.GetPath().AppendASCII("nested"); |
| const base::FilePath kOtherFile = kNestedDir.AppendASCII("other_file"); |
| EXPECT_TRUE(base::CreateDirectory(kNestedDir)); |
| EXPECT_TRUE(base::WriteFile(kOtherFile, "foo")); |
| |
| AppCacheDatabase db(kDbFile); |
| EXPECT_FALSE(db.LazyOpen(false)); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| EXPECT_TRUE(base::DirectoryExists(kNestedDir)); |
| EXPECT_TRUE(base::PathExists(kOtherFile)); |
| |
| EXPECT_TRUE(db.DeleteExistingAndCreateNewDatabase()); |
| |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| EXPECT_FALSE(base::DirectoryExists(kNestedDir)); |
| EXPECT_FALSE(base::PathExists(kOtherFile)); |
| } |
| |
| #ifdef NDEBUG |
| // Only run in release builds because sql::Database and familiy |
| // crank up DLOG(FATAL)'ness and this test presents it with |
| // intentionally bad data which causes debug builds to exit instead |
| // of run to completion. In release builds, errors the are delivered |
| // to the consumer so we can test the error handling of the consumer. |
| // TODO: crbug/328576 |
| TEST_F(AppCacheDatabaseTest, QuickIntegrityCheck) { |
| // Real files on disk for this test too, a corrupt database file. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath mock_dir = temp_dir.GetPath().AppendASCII("mock"); |
| ASSERT_TRUE(base::CreateDirectory(mock_dir)); |
| |
| const base::FilePath kDbFile = mock_dir.AppendASCII("appcache.db"); |
| const base::FilePath kOtherFile = mock_dir.AppendASCII("other_file"); |
| EXPECT_TRUE(base::WriteFile(kOtherFile, "foo")); |
| |
| // First create a valid db file. |
| { |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| EXPECT_TRUE(base::PathExists(kOtherFile)); |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| } |
| |
| // Break it. |
| ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile)); |
| |
| // Reopening will notice the corruption and delete/recreate the directory. |
| { |
| sql::test::ScopedErrorExpecter expecter; |
| expecter.ExpectError(SQLITE_CORRUPT); |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| EXPECT_FALSE(base::PathExists(kOtherFile)); |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| EXPECT_TRUE(expecter.SawExpectedErrors()); |
| } |
| } |
| #endif // NDEBUG |
| |
| TEST_F(AppCacheDatabaseTest, WasCorrutionDetected) { |
| // Real files on disk for this test too, a corrupt database file. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = temp_dir.GetPath().AppendASCII("appcache.db"); |
| |
| // First create a valid db file. |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| EXPECT_FALSE(db.was_corruption_detected()); |
| |
| // Break it. |
| ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile)); |
| |
| // See the the corruption is detected and reported. |
| { |
| sql::test::ScopedErrorExpecter expecter; |
| expecter.ExpectError(SQLITE_CORRUPT); |
| std::map<url::Origin, int64_t> usage_map; |
| EXPECT_FALSE(db.GetAllOriginUsage(&usage_map)); |
| EXPECT_TRUE(db.was_corruption_detected()); |
| EXPECT_TRUE(base::PathExists(kDbFile)); |
| EXPECT_TRUE(expecter.SawExpectedErrors()); |
| } |
| } |
| |
| TEST_F(AppCacheDatabaseTest, ExperimentalFlags) { |
| const char kExperimentFlagsKey[] = "ExperimentFlags"; |
| std::string kInjectedFlags("exp1,exp2"); |
| |
| // Real files on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = temp_dir.GetPath().AppendASCII("appcache.db"); |
| const base::FilePath kOtherFile = |
| temp_dir.GetPath().AppendASCII("other_file"); |
| EXPECT_TRUE(base::WriteFile(kOtherFile, "foo")); |
| EXPECT_TRUE(base::PathExists(kOtherFile)); |
| |
| // Inject a non empty flags value, and verify it got there. |
| { |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| EXPECT_TRUE(db.meta_table_->SetValue(kExperimentFlagsKey, kInjectedFlags)); |
| std::string flags; |
| EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags)); |
| EXPECT_EQ(kInjectedFlags, flags); |
| } |
| |
| // If flags don't match the expected value, empty string by default, |
| // the database should be recreated and other files should be cleared out. |
| { |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(false)); |
| std::string flags; |
| EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags)); |
| EXPECT_TRUE(flags.empty()); |
| EXPECT_FALSE(base::PathExists(kOtherFile)); |
| } |
| } |
| |
| TEST_F(AppCacheDatabaseTest, EntryRecords) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| sql::test::ScopedErrorExpecter expecter; |
| // TODO(shess): Suppressing SQLITE_CONSTRAINT because the code |
| // expects that and handles the resulting error. Consider revising |
| // the code to use INSERT OR IGNORE (which would not throw |
| // SQLITE_CONSTRAINT) and then check ChangeCount() to see if any |
| // changes were made. |
| expecter.ExpectError(SQLITE_CONSTRAINT); |
| |
| 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; |
| entry.padding_size = 10; |
| 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; |
| entry.padding_size = 20; |
| 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; |
| entry.padding_size = 30; |
| 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); |
| EXPECT_EQ(10, found[0].padding_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(20, found[0].padding_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); |
| EXPECT_EQ(30, found[1].padding_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)); |
| |
| ASSERT_TRUE(expecter.SawExpectedErrors()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, CacheRecords) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| sql::test::ScopedErrorExpecter expecter; |
| // TODO(shess): See EntryRecords test. |
| expecter.ExpectError(SQLITE_CONSTRAINT); |
| |
| 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; |
| record.padding_size = 10; |
| record.manifest_parser_version = 20; |
| record.manifest_scope = std::string("/foo/"); |
| 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); |
| EXPECT_EQ(10, record.padding_size); |
| EXPECT_EQ(20, record.manifest_parser_version); |
| EXPECT_EQ("/foo/", record.manifest_scope); |
| |
| 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_EQ(10, record.padding_size); |
| EXPECT_EQ(20, record.manifest_parser_version); |
| EXPECT_EQ("/foo/", record.manifest_scope); |
| |
| EXPECT_TRUE(db.DeleteCache(1)); |
| EXPECT_FALSE(db.FindCache(1, &record)); |
| EXPECT_FALSE(db.FindCacheForGroup(1, &record)); |
| |
| EXPECT_TRUE(db.DeleteCache(1)); |
| |
| ASSERT_TRUE(expecter.SawExpectedErrors()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, GroupRecords) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| sql::test::ScopedErrorExpecter expecter; |
| // TODO(shess): See EntryRecords test. |
| expecter.ExpectError(SQLITE_CONSTRAINT); |
| |
| const GURL kManifestUrl("http://blah/manifest"); |
| const url::Origin kOrigin(url::Origin::Create(kManifestUrl)); |
| 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.GetURL(); |
| record.origin = kOrigin; |
| record.last_access_time = kLastAccessTime; |
| record.creation_time = kCreationTime; |
| EXPECT_TRUE(db.InsertGroup(&record)); |
| |
| record = kZeroRecord; |
| EXPECT_TRUE(db.FindGroupForManifestUrl(kOrigin.GetURL(), &record)); |
| EXPECT_EQ(2, record.group_id); |
| EXPECT_EQ(kOrigin.GetURL(), 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.GetURL(), 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.GetURL(), 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<url::Origin> origins; |
| EXPECT_TRUE(db.FindOriginsWithGroups(&origins)); |
| EXPECT_EQ(1U, origins.size()); |
| EXPECT_EQ(kOrigin, *(origins.begin())); |
| |
| const GURL kManifest2("http://blah2/manifest"); |
| const url::Origin kOrigin2(url::Origin::Create(kManifest2)); |
| 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; |
| cache_record.manifest_parser_version = 1; |
| cache_record.manifest_scope = std::string("/"); |
| 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); |
| |
| ASSERT_TRUE(expecter.SawExpectedErrors()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, GroupAccessAndEvictionTimes) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| const GURL kManifestUrl("http://blah/manifest"); |
| const url::Origin kOrigin(url::Origin::Create(kManifestUrl)); |
| const base::Time kDayOne = |
| base::Time() + base::TimeDelta::FromDays(1); |
| const base::Time kDayTwo = kDayOne + base::TimeDelta::FromDays(1); |
| |
| // See that the methods behave as expected with an empty db. |
| // To accommodate lazy updating, for consistency, none of them fail |
| // given ids not found in the db. |
| EXPECT_TRUE(db.UpdateEvictionTimes(1, kDayOne, kDayTwo)); |
| EXPECT_TRUE(db.UpdateLastAccessTime(1, kDayOne)); |
| EXPECT_TRUE(db.CommitLazyLastAccessTimes()); |
| EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayTwo)); |
| EXPECT_TRUE(db.CommitLazyLastAccessTimes()); |
| |
| // Insert a group at DAY1 |
| AppCacheDatabase::GroupRecord record; |
| record.group_id = 1; |
| record.manifest_url = kManifestUrl; |
| record.origin = kOrigin; |
| record.creation_time = kDayOne; |
| record.last_access_time = kDayOne; |
| record.last_full_update_check_time = kDayOne; |
| record.first_evictable_error_time = kDayOne; |
| EXPECT_TRUE(db.InsertGroup(&record)); |
| |
| // Verify the round trip. |
| record = AppCacheDatabase::GroupRecord(); |
| EXPECT_TRUE(db.FindGroup(1, &record)); |
| EXPECT_EQ(kDayOne, record.last_access_time); |
| EXPECT_EQ(kDayOne, record.last_full_update_check_time); |
| EXPECT_EQ(kDayOne, record.first_evictable_error_time); |
| |
| // Update the times to DAY2 and verify. |
| EXPECT_TRUE(db.UpdateEvictionTimes(1, kDayTwo, kDayTwo)); |
| EXPECT_TRUE(db.UpdateLastAccessTime(1, kDayTwo)); |
| record = AppCacheDatabase::GroupRecord(); |
| EXPECT_TRUE(db.FindGroup(1, &record)); |
| EXPECT_EQ(kDayTwo, record.last_access_time); |
| EXPECT_EQ(kDayTwo, record.last_full_update_check_time); |
| EXPECT_EQ(kDayTwo, record.first_evictable_error_time); |
| |
| // Lazy update back to DAY1 and verify its reflected without having committed. |
| EXPECT_TRUE(db.lazy_last_access_times_.empty()); |
| EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayOne)); |
| EXPECT_FALSE(db.lazy_last_access_times_.empty()); |
| record = AppCacheDatabase::GroupRecord(); |
| EXPECT_TRUE(db.FindGroup(1, &record)); |
| EXPECT_EQ(kDayOne, record.last_access_time); |
| |
| // Commit the lazy value and verify it sticks. |
| EXPECT_TRUE(db.CommitLazyLastAccessTimes()); |
| EXPECT_TRUE(db.lazy_last_access_times_.empty()); |
| record = AppCacheDatabase::GroupRecord(); |
| EXPECT_TRUE(db.FindGroup(1, &record)); |
| EXPECT_EQ(kDayOne, record.last_access_time); |
| |
| // Verify a bad lazy group id doesn't fail to commit the good ones on DAY2. |
| EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayTwo)); |
| EXPECT_TRUE(db.LazyUpdateLastAccessTime(2, kDayTwo)); |
| EXPECT_EQ(2u, db.lazy_last_access_times_.size()); |
| EXPECT_TRUE(db.CommitLazyLastAccessTimes()); |
| EXPECT_TRUE(db.lazy_last_access_times_.empty()); |
| record = AppCacheDatabase::GroupRecord(); |
| EXPECT_TRUE(db.FindGroup(1, &record)); |
| EXPECT_EQ(kDayTwo, record.last_access_time); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, NamespaceRecords) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| sql::test::ScopedErrorExpecter expecter; |
| // TODO(shess): See EntryRecords test. |
| expecter.ExpectError(SQLITE_CONSTRAINT); |
| |
| const GURL kFooNameSpace1("http://foo/namespace1"); |
| const GURL kFooNameSpace2("http://foo/namespace2"); |
| const GURL kFooFallbackEntry("http://foo/entry"); |
| const url::Origin kFooOrigin(url::Origin::Create(kFooNameSpace1)); |
| const GURL kBarNameSpace1("http://bar/namespace1"); |
| const GURL kBarNameSpace2("http://bar/namespace2"); |
| const GURL kBarFallbackEntry("http://bar/entry"); |
| const url::Origin kBarOrigin(url::Origin::Create(kBarNameSpace1)); |
| |
| 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_.namespace_url = kFooNameSpace1; |
| record.namespace_.target_url = kFooFallbackEntry; |
| EXPECT_TRUE(db.InsertNamespace(&record)); |
| EXPECT_FALSE(db.InsertNamespace(&record)); |
| |
| record.cache_id = 2; |
| record.origin = kFooOrigin; |
| record.namespace_.namespace_url = kFooNameSpace2; |
| record.namespace_.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_.namespace_url); |
| EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.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_.namespace_url); |
| EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.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_.namespace_url); |
| EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); |
| EXPECT_EQ(2, fallbacks[1].cache_id); |
| EXPECT_EQ(kFooOrigin, fallbacks[1].origin); |
| EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_.namespace_url); |
| EXPECT_EQ(kFooFallbackEntry, fallbacks[1].namespace_.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_.namespace_url); |
| EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url); |
| |
| // Two more records for the same cache in the Bar origin. |
| record.cache_id = 3; |
| record.origin = kBarOrigin; |
| record.namespace_.namespace_url = kBarNameSpace1; |
| record.namespace_.target_url = kBarFallbackEntry; |
| EXPECT_TRUE(db.InsertNamespace(&record)); |
| |
| record.cache_id = 3; |
| record.origin = kBarOrigin; |
| record.namespace_.namespace_url = kBarNameSpace2; |
| record.namespace_.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()); |
| |
| ASSERT_TRUE(expecter.SawExpectedErrors()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, OnlineSafeListRecords) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| const GURL kFooNameSpace1("http://foo/namespace1"); |
| const GURL kFooNameSpace2("http://foo/namespace2"); |
| const GURL kBarNameSpace1("http://bar/namespace1"); |
| |
| const AppCacheDatabase::OnlineSafeListRecord kZeroRecord; |
| AppCacheDatabase::OnlineSafeListRecord record; |
| std::vector<AppCacheDatabase::OnlineSafeListRecord> records; |
| |
| // Behavior with an empty table |
| EXPECT_TRUE(db.FindOnlineSafeListForCache(1, &records)); |
| EXPECT_TRUE(records.empty()); |
| EXPECT_TRUE(db.DeleteOnlineSafeListForCache(1)); |
| |
| record.cache_id = 1; |
| record.namespace_url = kFooNameSpace1; |
| EXPECT_TRUE(db.InsertOnlineSafeList(&record)); |
| record.namespace_url = kFooNameSpace2; |
| EXPECT_TRUE(db.InsertOnlineSafeList(&record)); |
| records.clear(); |
| EXPECT_TRUE(db.FindOnlineSafeListForCache(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.InsertOnlineSafeList(&record)); |
| records.clear(); |
| EXPECT_TRUE(db.FindOnlineSafeListForCache(2, &records)); |
| EXPECT_EQ(1U, records.size()); |
| |
| EXPECT_TRUE(db.DeleteOnlineSafeListForCache(1)); |
| records.clear(); |
| EXPECT_TRUE(db.FindOnlineSafeListForCache(1, &records)); |
| EXPECT_TRUE(records.empty()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, DeletableResponseIds) { |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| sql::test::ScopedErrorExpecter expecter; |
| // TODO(shess): See EntryRecords test. |
| expecter.ExpectError(SQLITE_CONSTRAINT); |
| |
| std::vector<int64_t> ids; |
| |
| EXPECT_TRUE(db.GetDeletableResponseIds( |
| &ids, std::numeric_limits<int64_t>::max(), 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, std::numeric_limits<int64_t>::max(), 100)); |
| EXPECT_EQ(1U, ids.size()); |
| EXPECT_EQ(0, ids[0]); |
| |
| int64_t 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, std::numeric_limits<int64_t>::max(), 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, std::numeric_limits<int64_t>::max(), 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, std::numeric_limits<int64_t>::max(), 100)); |
| EXPECT_EQ(5U, ids.size()); |
| for (int i = 0; i < static_cast<int>(ids.size()); ++i) |
| EXPECT_EQ(i + 5, ids[i]); |
| |
| ASSERT_TRUE(expecter.SawExpectedErrors()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, OriginUsage) { |
| const GURL kManifestUrl("http://blah/manifest"); |
| const GURL kManifestUrl2("http://blah/manifest2"); |
| const url::Origin kOrigin = url::Origin::Create(kManifestUrl); |
| const GURL kOtherOriginManifestUrl("http://other/manifest"); |
| const url::Origin kOtherOrigin = url::Origin::Create(kOtherOriginManifestUrl); |
| |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| std::vector<AppCacheDatabase::CacheRecord> cache_records; |
| EXPECT_EQ(0, db.GetOriginUsage(kOrigin)); |
| |
| 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; |
| cache_record.padding_size = 1; |
| cache_record.manifest_parser_version = 1; |
| cache_record.manifest_scope = std::string("/"); |
| EXPECT_TRUE(db.InsertCache(&cache_record)); |
| |
| EXPECT_EQ(101, 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; |
| cache_record.padding_size = 1; |
| EXPECT_TRUE(db.InsertCache(&cache_record)); |
| |
| EXPECT_EQ(1102, 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; |
| cache_record.padding_size = 1; |
| EXPECT_TRUE(db.InsertCache(&cache_record)); |
| |
| EXPECT_EQ(5001, db.GetOriginUsage(kOtherOrigin)); |
| |
| std::map<url::Origin, int64_t> usage_map; |
| EXPECT_TRUE(db.GetAllOriginUsage(&usage_map)); |
| EXPECT_EQ(2U, usage_map.size()); |
| EXPECT_EQ(1102, usage_map[kOrigin]); |
| EXPECT_EQ(5001, usage_map[kOtherOrigin]); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, FindCachesForOrigin) { |
| const GURL kManifestUrl("http://blah/manifest"); |
| const GURL kManifestUrl2("http://blah/manifest2"); |
| const url::Origin kOrigin = url::Origin::Create(kManifestUrl); |
| const GURL kOtherOriginManifestUrl("http://other/manifest"); |
| const url::Origin kOtherOrigin = url::Origin::Create(kOtherOriginManifestUrl); |
| |
| const base::FilePath kEmptyPath; |
| AppCacheDatabase db(kEmptyPath); |
| EXPECT_TRUE(db.LazyOpen(true)); |
| |
| std::vector<AppCacheDatabase::CacheRecord> cache_records; |
| EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records)); |
| EXPECT_TRUE(cache_records.empty()); |
| |
| // Create 2 Groups with the same origin, and 1 Group with a different origin. |
| AppCacheDatabase::GroupRecord group_record; |
| group_record.group_id = 1; |
| group_record.manifest_url = kManifestUrl; |
| group_record.origin = kOrigin; |
| EXPECT_TRUE(db.InsertGroup(&group_record)); |
| group_record.group_id = 2; |
| group_record.manifest_url = kManifestUrl2; |
| group_record.origin = kOrigin; |
| EXPECT_TRUE(db.InsertGroup(&group_record)); |
| group_record.group_id = 3; |
| group_record.manifest_url = kOtherOriginManifestUrl; |
| group_record.origin = kOtherOrigin; |
| EXPECT_TRUE(db.InsertGroup(&group_record)); |
| |
| // Add a Cache to each of the 3 Groups. |
| AppCacheDatabase::CacheRecord cache_record; |
| for (int i = 1; i < 4; ++i) { |
| cache_record.cache_id = i; |
| cache_record.group_id = i; |
| cache_record.online_wildcard = true; |
| cache_record.update_time = kZeroTime; |
| cache_record.cache_size = 100; |
| cache_record.padding_size = 1000; |
| cache_record.manifest_parser_version = 1; |
| cache_record.manifest_scope = std::string("/"); |
| EXPECT_TRUE(db.InsertCache(&cache_record)); |
| } |
| |
| 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()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, |
| UpgradeSchemaForVersionsWithoutSupportedMigrations) { |
| // Real file on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = |
| temp_dir.GetPath().AppendASCII("deprecated.db"); |
| |
| // Create a database with a table name that does not show up in the AppCache |
| // schema. This table would not be touched by any migration, so its existence |
| // indicates that the database was not nuked. |
| { |
| sql::Database db; |
| EXPECT_TRUE(db.Open(kDbFile)); |
| |
| sql::MetaTable meta_table; |
| EXPECT_TRUE(meta_table.Init(&db, 6, 6)); |
| |
| static const char kSchemaSql[] = |
| "CREATE TABLE Unused(id INTEGER PRIMARY KEY)"; |
| EXPECT_TRUE(db.Execute(kSchemaSql)); |
| |
| EXPECT_TRUE(db.DoesColumnExist("Unused", "id")); |
| } |
| |
| // Open that database and verify that it got nuked. |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(/*create_if_needed=*/false)); |
| EXPECT_FALSE(db.db_->DoesColumnExist("Unused", "id")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "padding_size")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Entries", "padding_size")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_parser_version")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_scope")); |
| EXPECT_EQ(10, db.meta_table_->GetVersionNumber()); |
| EXPECT_EQ(10, db.meta_table_->GetCompatibleVersionNumber()); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, UpgradeSchemaFrom7) { |
| // Real file on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = |
| temp_dir.GetPath().AppendASCII("upgrade7to8.db"); |
| |
| { |
| sql::Database db; |
| EXPECT_TRUE(db.Open(kDbFile)); |
| |
| sql::MetaTable meta_table; |
| EXPECT_TRUE(meta_table.Init(&db, 7, 7)); |
| |
| // Create a database with a table name that does not show up in the AppCache |
| // schema. Its persistence across the migration indicates that the migration |
| // did not nuke the database. |
| static const char kCreateUnusedTableSql[] = |
| "CREATE TABLE Unused(id INTEGER PRIMARY KEY)"; |
| EXPECT_TRUE(db.Execute(kCreateUnusedTableSql)); |
| |
| // Include tables/columns that are needed to run the 7-to-9 backfill. |
| static const char kCreateGroupsSql[] = |
| "CREATE TABLE Groups(group_id INTEGER PRIMARY KEY, manifest_url TEXT)"; |
| static const char kCreateCachesSql[] = |
| "CREATE TABLE Caches(cache_id INTEGER PRIMARY KEY, group_id INTEGER)"; |
| static const char kCreateEntriesSql[] = |
| "CREATE TABLE Entries(cache_id INTEGER, url TEXT, response_id INTEGER)"; |
| static const char kCreateNamespacesSql[] = |
| "CREATE TABLE Namespaces(cache_id INTEGER, origin TEXT, type INTEGER," |
| "namespace_url TEXT, target_url TEXT, is_pattern INTEGER)"; |
| EXPECT_TRUE(db.Execute(kCreateGroupsSql)); |
| EXPECT_TRUE(db.Execute(kCreateCachesSql)); |
| EXPECT_TRUE(db.Execute(kCreateEntriesSql)); |
| EXPECT_TRUE(db.Execute(kCreateNamespacesSql)); |
| |
| // Insert version 7 records (with 0 padding) to test the backfill. |
| static const char kInsertGroupSql[] = |
| "INSERT INTO Groups (group_id, manifest_url) VALUES " |
| " (1, 'manifest_url')," |
| " (2, 'https://blah/manifest_url')," |
| " (3, 'https://blah/foo/manifest_url')"; |
| static const char kInsertCacheSql[] = |
| "INSERT INTO Caches (cache_id, group_id) VALUES(1, 1), (2, 2), (3, 3)"; |
| static const char kInsertEntrySql[] = |
| "INSERT INTO Entries (cache_id, url, response_id) VALUES (1, 'url', 1)"; |
| EXPECT_TRUE(db.Execute(kInsertGroupSql)); |
| EXPECT_TRUE(db.Execute(kInsertCacheSql)); |
| EXPECT_TRUE(db.Execute(kInsertEntrySql)); |
| } |
| |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(/*create_if_needed=*/false)); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Unused", "id")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "padding_size")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Entries", "padding_size")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_parser_version")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_scope")); |
| |
| static const char kFindCacheSql[] = |
| "SELECT padding_size, cache_id FROM Caches WHERE cache_id = 1"; |
| sql::Statement find_cache_statement( |
| db.db_->GetUniqueStatement(kFindCacheSql)); |
| EXPECT_TRUE(find_cache_statement.Step()); |
| int64_t cache_padding_size = find_cache_statement.ColumnInt64(0); |
| |
| static const char kFindEntrySql[] = |
| "SELECT padding_size, response_id FROM Entries WHERE response_id = 1"; |
| sql::Statement find_entry_statement( |
| db.db_->GetUniqueStatement(kFindEntrySql)); |
| EXPECT_TRUE(find_entry_statement.Step()); |
| int64_t entry_padding_size = find_entry_statement.ColumnInt64(0); |
| |
| EXPECT_GE(cache_padding_size, 0); |
| EXPECT_EQ(cache_padding_size, entry_padding_size); |
| |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 1), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 1), "/"); |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 2), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 2), "/"); |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 3), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 3), "/"); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, UpgradeSchemaFrom8) { |
| // Real file on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = |
| temp_dir.GetPath().AppendASCII("upgrade8to9.db"); |
| |
| { |
| sql::Database db; |
| EXPECT_TRUE(db.Open(kDbFile)); |
| |
| sql::MetaTable meta_table; |
| EXPECT_TRUE(meta_table.Init(&db, 8, 8)); |
| |
| // Create a database with a table name that does not show up in the AppCache |
| // schema. Its persistence across the migration indicates that the migration |
| // did not nuke the database. |
| static const char kCreateUnusedTableSql[] = |
| "CREATE TABLE Unused(id INTEGER PRIMARY KEY)"; |
| EXPECT_TRUE(db.Execute(kCreateUnusedTableSql)); |
| |
| // Include tables/columns that are needed to run the 8-to-9 backfill. |
| static const char kCreateGroupsSql[] = |
| "CREATE TABLE Groups(group_id INTEGER PRIMARY KEY, manifest_url TEXT)"; |
| static const char kCreateCachesSql[] = |
| "CREATE TABLE Caches(cache_id INTEGER PRIMARY KEY, group_id INTEGER)"; |
| static const char kCreateEntriesSql[] = |
| "CREATE TABLE Entries(cache_id INTEGER, url TEXT," |
| "flags INTEGER, response_id INTEGER, response_size INTEGER," |
| "padding_size INTEGER)"; |
| static const char kCreateNamespacesSql[] = |
| "CREATE TABLE Namespaces(cache_id INTEGER, origin TEXT, type INTEGER," |
| "namespace_url TEXT, target_url TEXT, is_pattern INTEGER)"; |
| EXPECT_TRUE(db.Execute(kCreateGroupsSql)); |
| EXPECT_TRUE(db.Execute(kCreateCachesSql)); |
| EXPECT_TRUE(db.Execute(kCreateEntriesSql)); |
| EXPECT_TRUE(db.Execute(kCreateNamespacesSql)); |
| |
| // Insert a version 8 record to test the backfill. |
| static const char kInsertGroupSql[] = |
| "INSERT INTO Groups (group_id, manifest_url) VALUES " |
| " (1, 'manifest_url')," |
| " (2, 'https://blah/manifest_url')," |
| " (3, 'https://blah/foo/manifest_url')"; |
| static const char kInsertCacheSql[] = |
| "INSERT INTO Caches (cache_id, group_id) VALUES(1, 1), (2, 2), (3, 3)"; |
| EXPECT_TRUE(db.Execute(kInsertGroupSql)); |
| EXPECT_TRUE(db.Execute(kInsertCacheSql)); |
| } |
| |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(/*create_if_needed=*/false)); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Unused", "id")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_parser_version")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "manifest_scope")); |
| |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 1), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 1), "/"); |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 2), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 2), "/"); |
| EXPECT_EQ(GetCacheManifestParserVersion(db, 3), 0); |
| EXPECT_EQ(GetCacheManifestScope(db, 3), "/"); |
| } |
| |
| TEST_F(AppCacheDatabaseTest, UpgradeSchemaFrom9) { |
| // Real file on disk for this test. |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath kDbFile = |
| temp_dir.GetPath().AppendASCII("upgrade9to10.db"); |
| |
| { |
| sql::Database db; |
| EXPECT_TRUE(db.Open(kDbFile)); |
| |
| sql::MetaTable meta_table; |
| EXPECT_TRUE(meta_table.Init(&db, 9, 9)); |
| |
| // Create a database with a table name that does not show up in the AppCache |
| // schema. Its persistence across the migration indicates that the migration |
| // did not nuke the database. |
| static const char kCreateUnusedTableSql[] = |
| "CREATE TABLE Unused(id INTEGER PRIMARY KEY)"; |
| EXPECT_TRUE(db.Execute(kCreateUnusedTableSql)); |
| |
| static const char kCreateGroupsSql[] = |
| "CREATE TABLE Groups(group_id INTEGER PRIMARY KEY, origin TEXT," |
| "manifest_url TEXT, creation_time INTEGER, last_access_time INTEGER," |
| "last_full_update_check_time INTEGER," |
| "first_evictalbe_error_time INTEGER)"; |
| static const char kCreateCachesSql[] = |
| "CREATE TABLE Caches(cache_id INTEGER PRIMARY KEY, group_id INTEGER," |
| "online_wildcard INTEGER, update_time INTEGER, cache_size INTEGER," |
| "padding_size INTEGER, manifest_parser_version INTEGER," |
| "manifest_scope TEXT)"; |
| static const char kCreateEntriesSql[] = |
| "CREATE TABLE Entries(cache_id INTEGER, url TEXT," |
| "flags INTEGER, response_id INTEGER, response_size INTEGER," |
| "padding_size INTEGER)"; |
| static const char kCreateNamespacesSql[] = |
| "CREATE TABLE Namespaces(cache_id INTEGER, origin TEXT, type INTEGER," |
| "namespace_url TEXT, target_url TEXT, is_pattern INTEGER)"; |
| EXPECT_TRUE(db.Execute(kCreateGroupsSql)); |
| EXPECT_TRUE(db.Execute(kCreateCachesSql)); |
| EXPECT_TRUE(db.Execute(kCreateEntriesSql)); |
| EXPECT_TRUE(db.Execute(kCreateNamespacesSql)); |
| |
| static const char kInsertCacheSql[] = |
| "INSERT INTO Caches(cache_id, group_id) VALUES (1, 1)"; |
| static const char kInsertGroupSql[] = |
| "INSERT INTO Groups (group_id, manifest_url) VALUES " |
| " (1, 'manifest_url')"; |
| static const char kInsertEntrySql[] = |
| "INSERT INTO Entries (cache_id, url, response_id) VALUES (1, 'url', 1)"; |
| static const char kInsertNamespaceSql[] = |
| "INSERT INTO Namespaces(cache_id, origin) VALUES (1, 'origin')"; |
| |
| EXPECT_TRUE(db.Execute(kInsertCacheSql)); |
| EXPECT_TRUE(db.Execute(kInsertGroupSql)); |
| EXPECT_TRUE(db.Execute(kInsertEntrySql)); |
| EXPECT_TRUE(db.Execute(kInsertNamespaceSql)); |
| } |
| |
| AppCacheDatabase db(kDbFile); |
| EXPECT_TRUE(db.LazyOpen(/*create_if_needed=*/false)); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Unused", "id")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Caches", "token_expires")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Groups", "token_expires")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Entries", "token_expires")); |
| EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "token_expires")); |
| |
| static const char kFindCacheSql[] = |
| "SELECT token_expires FROM Entries WHERE cache_id = 1"; |
| sql::Statement find_cache_statement( |
| db.db_->GetUniqueStatement(kFindCacheSql)); |
| EXPECT_TRUE(find_cache_statement.Step()); |
| EXPECT_EQ(0, find_cache_statement.ColumnInt64(0)); |
| |
| static const char kFindGroupSql[] = |
| "SELECT token_expires FROM Entries WHERE cache_id = 1"; |
| sql::Statement find_group_statement( |
| db.db_->GetUniqueStatement(kFindGroupSql)); |
| EXPECT_TRUE(find_group_statement.Step()); |
| EXPECT_EQ(0, find_group_statement.ColumnInt64(0)); |
| |
| static const char kFindEntrySql[] = |
| "SELECT token_expires FROM Entries WHERE cache_id = 1"; |
| sql::Statement find_entry_statement( |
| db.db_->GetUniqueStatement(kFindEntrySql)); |
| EXPECT_TRUE(find_entry_statement.Step()); |
| EXPECT_EQ(0, find_entry_statement.ColumnInt64(0)); |
| |
| static const char kFindNamespaceSql[] = |
| "SELECT token_expires FROM Namespaces WHERE cache_id = 1"; |
| sql::Statement find_namespace_statement( |
| db.db_->GetUniqueStatement(kFindNamespaceSql)); |
| EXPECT_TRUE(find_namespace_statement.Step()); |
| EXPECT_EQ(0, find_namespace_statement.ColumnInt64(0)); |
| } |
| |
| } // namespace content |