| // 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 "content/browser/service_worker/service_worker_database.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <string> |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "content/browser/service_worker/service_worker_database.pb.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" |
| #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| using ::testing::ElementsAreArray; |
| using ::testing::IsEmpty; |
| |
| typedef ServiceWorkerDatabase::RegistrationData RegistrationData; |
| typedef ServiceWorkerDatabase::ResourceRecord Resource; |
| |
| struct AvailableIds { |
| int64_t reg_id; |
| int64_t res_id; |
| int64_t ver_id; |
| |
| AvailableIds() : reg_id(-1), res_id(-1), ver_id(-1) {} |
| ~AvailableIds() {} |
| }; |
| |
| GURL URL(const GURL& origin, const std::string& path) { |
| EXPECT_TRUE(origin.is_valid()); |
| EXPECT_EQ(origin, origin.GetOrigin()); |
| GURL out(origin.spec() + path); |
| EXPECT_TRUE(out.is_valid()); |
| return out; |
| } |
| |
| Resource CreateResource(int64_t resource_id, |
| const GURL& url, |
| uint64_t size_bytes) { |
| EXPECT_TRUE(url.is_valid()); |
| return Resource(resource_id, url, size_bytes); |
| } |
| |
| ServiceWorkerDatabase* CreateDatabase(const base::FilePath& path) { |
| return new ServiceWorkerDatabase(path); |
| } |
| |
| ServiceWorkerDatabase* CreateDatabaseInMemory() { |
| return new ServiceWorkerDatabase(base::FilePath()); |
| } |
| |
| void VerifyRegistrationData(const RegistrationData& expected, |
| const RegistrationData& actual) { |
| EXPECT_EQ(expected.registration_id, actual.registration_id); |
| EXPECT_EQ(expected.scope, actual.scope); |
| EXPECT_EQ(expected.script, actual.script); |
| EXPECT_EQ(expected.script_type, actual.script_type); |
| EXPECT_EQ(expected.update_via_cache, actual.update_via_cache); |
| EXPECT_EQ(expected.version_id, actual.version_id); |
| EXPECT_EQ(expected.is_active, actual.is_active); |
| EXPECT_EQ(expected.has_fetch_handler, actual.has_fetch_handler); |
| EXPECT_EQ(expected.last_update_check, actual.last_update_check); |
| EXPECT_EQ(expected.used_features, actual.used_features); |
| EXPECT_EQ(expected.resources_total_size_bytes, |
| actual.resources_total_size_bytes); |
| } |
| |
| void VerifyResourceRecords(const std::vector<Resource>& expected, |
| const std::vector<Resource>& actual) { |
| ASSERT_EQ(expected.size(), actual.size()); |
| for (size_t i = 0; i < expected.size(); ++i) { |
| EXPECT_EQ(expected[i].resource_id, actual[i].resource_id); |
| EXPECT_EQ(expected[i].url, actual[i].url); |
| EXPECT_EQ(expected[i].size_bytes, actual[i].size_bytes); |
| } |
| } |
| |
| } // namespace |
| |
| TEST(ServiceWorkerDatabaseTest, OpenDatabase) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| std::unique_ptr<ServiceWorkerDatabase> database( |
| CreateDatabase(database_dir.GetPath())); |
| |
| // Should be false because the database does not exist at the path. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->LazyOpen(false)); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| |
| database.reset(CreateDatabase(database_dir.GetPath())); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(false)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, OpenDatabase_InMemory) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| // Should be false because the database does not exist in memory. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->LazyOpen(false)); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| database.reset(CreateDatabaseInMemory()); |
| |
| // Should be false because the database is not persistent. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->LazyOpen(false)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DatabaseVersion_ValidSchemaVersion) { |
| GURL origin("https://example.com"); |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| |
| // Opening a new database does not write anything, so its schema version |
| // should be 0. |
| int64_t db_version = -1; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadDatabaseVersion(&db_version)); |
| EXPECT_EQ(0u, db_version); |
| |
| // First writing triggers database initialization and bumps the schema |
| // version. |
| std::vector<ServiceWorkerDatabase::ResourceRecord> resources; |
| resources.push_back(CreateResource(1, URL(origin, "/resource"), 10)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ServiceWorkerDatabase::RegistrationData data; |
| data.resources_total_size_bytes = 10; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data, resources, &deleted_version, &newly_purgeable_resources)); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadDatabaseVersion(&db_version)); |
| EXPECT_LT(0, db_version); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DatabaseVersion_ObsoleteSchemaVersion) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| std::unique_ptr<ServiceWorkerDatabase> database( |
| CreateDatabase(database_dir.GetPath())); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| |
| // First writing triggers database initialization and bumps the schema |
| // version. |
| GURL origin("https://example.com"); |
| std::vector<ServiceWorkerDatabase::ResourceRecord> resources; |
| resources.push_back(CreateResource(1, URL(origin, "/resource"), 10)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ServiceWorkerDatabase::RegistrationData data; |
| data.resources_total_size_bytes = 10; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| int64_t db_version = -1; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadDatabaseVersion(&db_version)); |
| ASSERT_LT(0, db_version); |
| |
| // Emulate an obsolete schema version. |
| int64_t old_db_version = 1; |
| leveldb::WriteBatch batch; |
| batch.Put("INITDATA_DB_VERSION", base::Int64ToString(old_db_version)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->WriteBatch(&batch)); |
| db_version = -1; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadDatabaseVersion(&db_version)); |
| ASSERT_EQ(old_db_version, db_version); |
| |
| // Opening the database whose schema version is obsolete should fail. |
| database.reset(CreateDatabase(database_dir.GetPath())); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_FAILED, |
| database->LazyOpen(true)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DatabaseVersion_CorruptedSchemaVersion) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| std::unique_ptr<ServiceWorkerDatabase> database( |
| CreateDatabase(database_dir.GetPath())); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| |
| // First writing triggers database initialization and bumps the schema |
| // version. |
| GURL origin("https://example.com"); |
| std::vector<ServiceWorkerDatabase::ResourceRecord> resources; |
| resources.push_back(CreateResource(1, URL(origin, "/resource"), 10)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ServiceWorkerDatabase::RegistrationData data; |
| data.resources_total_size_bytes = 10; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| int64_t db_version = -1; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadDatabaseVersion(&db_version)); |
| ASSERT_LT(0, db_version); |
| |
| // Emulate a corrupted schema version. |
| int64_t corrupted_db_version = -10; |
| leveldb::WriteBatch batch; |
| batch.Put("INITDATA_DB_VERSION", base::Int64ToString(corrupted_db_version)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->WriteBatch(&batch)); |
| db_version = -1; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED, |
| database->ReadDatabaseVersion(&db_version)); |
| |
| // Opening the database whose schema version is corrupted should fail. |
| database.reset(CreateDatabase(database_dir.GetPath())); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED, |
| database->LazyOpen(true)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, GetNextAvailableIds) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| std::unique_ptr<ServiceWorkerDatabase> database( |
| CreateDatabase(database_dir.GetPath())); |
| |
| GURL origin("https://example.com"); |
| |
| // The database has never been used, so returns initial values. |
| AvailableIds ids; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds( |
| &ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(0, ids.reg_id); |
| EXPECT_EQ(0, ids.ver_id); |
| EXPECT_EQ(0, ids.res_id); |
| |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds( |
| &ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(0, ids.reg_id); |
| EXPECT_EQ(0, ids.ver_id); |
| EXPECT_EQ(0, ids.res_id); |
| |
| // Writing uncommitted resources bumps the next available resource id. |
| const int64_t kUncommittedIds[] = {0, 1, 3, 5, 6, 10}; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds(std::set<int64_t>( |
| kUncommittedIds, kUncommittedIds + base::size(kUncommittedIds)))); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->GetNextAvailableIds(&ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(0, ids.reg_id); |
| EXPECT_EQ(0, ids.ver_id); |
| EXPECT_EQ(11, ids.res_id); |
| |
| // Writing purgeable resources bumps the next available id. |
| const int64_t kPurgeableIds[] = {4, 12, 16, 17, 20}; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds(std::set<int64_t>( |
| kPurgeableIds, kPurgeableIds + base::size(kPurgeableIds)))); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->GetNextAvailableIds(&ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(0, ids.reg_id); |
| EXPECT_EQ(0, ids.ver_id); |
| EXPECT_EQ(21, ids.res_id); |
| |
| // Writing a registration bumps the next available registration and version |
| // ids. |
| std::vector<Resource> resources1; |
| RegistrationData data1; |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| data1.registration_id = 100; |
| data1.scope = URL(origin, "/foo"); |
| data1.script = URL(origin, "/script1.js"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 300; |
| resources1.push_back(CreateResource(1, data1.script, 300)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->GetNextAvailableIds(&ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(101, ids.reg_id); |
| EXPECT_EQ(201, ids.ver_id); |
| EXPECT_EQ(21, ids.res_id); |
| |
| // Writing a registration whose ids are lower than the stored ones should not |
| // bump the next available ids. |
| RegistrationData data2; |
| data2.registration_id = 10; |
| data2.scope = URL(origin, "/bar"); |
| data2.script = URL(origin, "/script2.js"); |
| data2.version_id = 20; |
| data2.resources_total_size_bytes = 400; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 400)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Same with resources. |
| int64_t kLowResourceId = 15; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds( |
| std::set<int64_t>(&kLowResourceId, &kLowResourceId + 1))); |
| |
| // Close and reopen the database to verify the stored values. |
| database.reset(CreateDatabase(database_dir.GetPath())); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->GetNextAvailableIds( |
| &ids.reg_id, &ids.ver_id, &ids.res_id)); |
| EXPECT_EQ(101, ids.reg_id); |
| EXPECT_EQ(201, ids.ver_id); |
| EXPECT_EQ(21, ids.res_id); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, GetOriginsWithRegistrations) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| std::set<GURL> origins; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetOriginsWithRegistrations(&origins)); |
| EXPECT_TRUE(origins.empty()); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| GURL origin1("https://example.com"); |
| RegistrationData data1; |
| data1.registration_id = 123; |
| data1.scope = URL(origin1, "/foo"); |
| data1.script = URL(origin1, "/script1.js"); |
| data1.version_id = 456; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| GURL origin2("https://www.example.com"); |
| RegistrationData data2; |
| data2.registration_id = 234; |
| data2.scope = URL(origin2, "/bar"); |
| data2.script = URL(origin2, "/script2.js"); |
| data2.version_id = 567; |
| data2.resources_total_size_bytes = 200; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| GURL origin3("https://example.org"); |
| RegistrationData data3; |
| data3.registration_id = 345; |
| data3.scope = URL(origin3, "/hoge"); |
| data3.script = URL(origin3, "/script3.js"); |
| data3.version_id = 678; |
| data3.resources_total_size_bytes = 300; |
| std::vector<Resource> resources3; |
| resources3.push_back(CreateResource(3, data3.script, 300)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data3, resources3, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // |origin3| has two registrations. |
| RegistrationData data4; |
| data4.registration_id = 456; |
| data4.scope = URL(origin3, "/fuga"); |
| data4.script = URL(origin3, "/script4.js"); |
| data4.version_id = 789; |
| data4.resources_total_size_bytes = 400; |
| std::vector<Resource> resources4; |
| resources4.push_back(CreateResource(4, data4.script, 400)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data4, resources4, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| origins.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetOriginsWithRegistrations(&origins)); |
| EXPECT_EQ(3U, origins.size()); |
| EXPECT_TRUE(base::ContainsKey(origins, origin1)); |
| EXPECT_TRUE(base::ContainsKey(origins, origin2)); |
| EXPECT_TRUE(base::ContainsKey(origins, origin3)); |
| |
| // |origin3| has another registration, so should not remove it from the |
| // unique origin list. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data4.registration_id, |
| origin3, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data4.registration_id, deleted_version.registration_id); |
| |
| origins.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetOriginsWithRegistrations(&origins)); |
| EXPECT_EQ(3U, origins.size()); |
| EXPECT_TRUE(base::ContainsKey(origins, origin1)); |
| EXPECT_TRUE(base::ContainsKey(origins, origin2)); |
| EXPECT_TRUE(base::ContainsKey(origins, origin3)); |
| |
| // |origin3| should be removed from the unique origin list. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data3.registration_id, |
| origin3, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data3.registration_id, deleted_version.registration_id); |
| |
| origins.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetOriginsWithRegistrations(&origins)); |
| EXPECT_EQ(2U, origins.size()); |
| EXPECT_TRUE(base::ContainsKey(origins, origin1)); |
| EXPECT_TRUE(base::ContainsKey(origins, origin2)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, GetRegistrationsForOrigin) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| GURL origin1("https://example.com"); |
| GURL origin2("https://www.example.com"); |
| GURL origin3("https://example.org"); |
| |
| std::vector<RegistrationData> registrations; |
| std::vector<std::vector<Resource>> resources_list; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin1, ®istrations, |
| &resources_list)); |
| EXPECT_TRUE(registrations.empty()); |
| EXPECT_TRUE(resources_list.empty()); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(origin1, "/foo"); |
| data1.script = URL(origin1, "/script1.js"); |
| data1.version_id = 1000; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| registrations.clear(); |
| resources_list.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin1, ®istrations, |
| &resources_list)); |
| EXPECT_EQ(1U, registrations.size()); |
| VerifyRegistrationData(data1, registrations[0]); |
| EXPECT_EQ(1U, resources_list.size()); |
| VerifyResourceRecords(resources1, resources_list[0]); |
| |
| RegistrationData data2; |
| data2.registration_id = 200; |
| data2.scope = URL(origin2, "/bar"); |
| data2.script = URL(origin2, "/script2.js"); |
| data2.version_id = 2000; |
| data2.resources_total_size_bytes = 200; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| registrations.clear(); |
| resources_list.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin2, ®istrations, |
| &resources_list)); |
| EXPECT_EQ(1U, registrations.size()); |
| VerifyRegistrationData(data2, registrations[0]); |
| EXPECT_EQ(1U, resources_list.size()); |
| VerifyResourceRecords(resources2, resources_list[0]); |
| |
| RegistrationData data3; |
| data3.registration_id = 300; |
| data3.scope = URL(origin3, "/hoge"); |
| data3.script = URL(origin3, "/script3.js"); |
| data3.version_id = 3000; |
| data3.resources_total_size_bytes = 300; |
| std::vector<Resource> resources3; |
| resources3.push_back(CreateResource(3, data3.script, 300)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data3, resources3, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // |origin3| has two registrations. |
| RegistrationData data4; |
| data4.registration_id = 400; |
| data4.scope = URL(origin3, "/fuga"); |
| data4.script = URL(origin3, "/script4.js"); |
| data4.version_id = 4000; |
| data4.resources_total_size_bytes = 400; |
| std::vector<Resource> resources4; |
| resources4.push_back(CreateResource(4, data4.script, 400)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data4, resources4, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| registrations.clear(); |
| resources_list.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin3, ®istrations, |
| &resources_list)); |
| EXPECT_EQ(2U, registrations.size()); |
| VerifyRegistrationData(data3, registrations[0]); |
| VerifyRegistrationData(data4, registrations[1]); |
| EXPECT_EQ(2U, resources_list.size()); |
| VerifyResourceRecords(resources3, resources_list[0]); |
| VerifyResourceRecords(resources4, resources_list[1]); |
| |
| // The third parameter |opt_resources_list| to GetRegistrationsForOrigin() |
| // is optional. So, nullptr should be acceptable. |
| registrations.clear(); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin1, ®istrations, nullptr)); |
| EXPECT_EQ(1U, registrations.size()); |
| VerifyRegistrationData(data1, registrations[0]); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, GetAllRegistrations) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| std::vector<RegistrationData> registrations; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetAllRegistrations(®istrations)); |
| EXPECT_TRUE(registrations.empty()); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| GURL origin1("https://www1.example.com"); |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(origin1, "/foo"); |
| data1.script = URL(origin1, "/script1.js"); |
| data1.version_id = 1000; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| GURL origin2("https://www2.example.com"); |
| RegistrationData data2; |
| data2.registration_id = 200; |
| data2.scope = URL(origin2, "/bar"); |
| data2.script = URL(origin2, "/script2.js"); |
| data2.version_id = 2000; |
| data2.resources_total_size_bytes = 200; |
| data2.update_via_cache = blink::mojom::ServiceWorkerUpdateViaCache::kNone; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| GURL origin3("https://www3.example.com"); |
| RegistrationData data3; |
| data3.registration_id = 300; |
| data3.scope = URL(origin3, "/hoge"); |
| data3.script = URL(origin3, "/script3.js"); |
| data3.version_id = 3000; |
| data3.resources_total_size_bytes = 300; |
| std::vector<Resource> resources3; |
| resources3.push_back(CreateResource(3, data3.script, 300)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data3, resources3, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // |origin3| has two registrations. |
| RegistrationData data4; |
| data4.registration_id = 400; |
| data4.scope = URL(origin3, "/fuga"); |
| data4.script = URL(origin3, "/script4.js"); |
| data4.version_id = 4000; |
| data4.resources_total_size_bytes = 400; |
| std::vector<Resource> resources4; |
| resources4.push_back(CreateResource(4, data4.script, 400)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data4, resources4, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| registrations.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetAllRegistrations(®istrations)); |
| EXPECT_EQ(4U, registrations.size()); |
| |
| VerifyRegistrationData(data1, registrations[0]); |
| VerifyRegistrationData(data2, registrations[1]); |
| VerifyRegistrationData(data3, registrations[2]); |
| VerifyRegistrationData(data4, registrations[3]); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Registration_Basic) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| GURL origin("https://example.com"); |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/resource1"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 10939 + 200; |
| data.used_features = {124, 901, 1019}; |
| |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, URL(origin, "/resource1"), 10939)); |
| resources.push_back(CreateResource(2, URL(origin, "/resource2"), 200)); |
| |
| // Write a resource to the uncommitted list to make sure that writing |
| // registration removes resource ids associated with the registration from |
| // the uncommitted list. |
| std::set<int64_t> uncommitted_ids; |
| uncommitted_ids.insert(resources[0].resource_id); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds(uncommitted_ids)); |
| std::set<int64_t> uncommitted_ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetUncommittedResourceIds(&uncommitted_ids_out)); |
| EXPECT_EQ(uncommitted_ids, uncommitted_ids_out); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| deleted_version.version_id = 222; // Dummy initial value |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data, resources, &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| |
| // Make sure that the registration and resource records are stored. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data, data_out); |
| VerifyResourceRecords(resources, resources_out); |
| GURL origin_out; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistrationOrigin(data.registration_id, &origin_out)); |
| EXPECT_EQ(origin, origin_out); |
| |
| // Make sure that the resource is removed from the uncommitted list. |
| uncommitted_ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetUncommittedResourceIds(&uncommitted_ids_out)); |
| EXPECT_TRUE(uncommitted_ids_out.empty()); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data.registration_id, |
| origin, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data.version_id, deleted_version.version_id); |
| ASSERT_EQ(resources.size(), newly_purgeable_resources.size()); |
| for (size_t i = 0; i < resources.size(); ++i) |
| EXPECT_EQ(newly_purgeable_resources[i], resources[i].resource_id); |
| |
| // Make sure that the registration and resource records are gone. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| EXPECT_TRUE(resources_out.empty()); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistrationOrigin(data.registration_id, &origin_out)); |
| |
| // Resources should be purgeable because these are no longer referred. |
| std::set<int64_t> purgeable_ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&purgeable_ids_out)); |
| EXPECT_EQ(2u, purgeable_ids_out.size()); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources[0].resource_id)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources[1].resource_id)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DeleteNonExistentRegistration) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| GURL origin("https://example.com"); |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/resource1"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 19 + 29129; |
| |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, URL(origin, "/resource1"), 19)); |
| resources.push_back(CreateResource(2, URL(origin, "/resource2"), 29129)); |
| |
| const int64_t kNonExistentRegistrationId = 999; |
| const int64_t kArbitraryVersionId = 222; // Used as a dummy initial value |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| deleted_version.version_id = kArbitraryVersionId; |
| std::vector<int64_t> newly_purgeable_resources; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data, resources, &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| |
| // Delete from an origin that has a registration. |
| deleted_version.version_id = kArbitraryVersionId; |
| newly_purgeable_resources.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(kNonExistentRegistrationId, |
| origin, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| |
| // Delete from an origin that has no registration. |
| deleted_version.version_id = kArbitraryVersionId; |
| newly_purgeable_resources.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration( |
| kNonExistentRegistrationId, GURL("https://example.net"), |
| &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Registration_Overwrite) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| GURL origin("https://example.com"); |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/resource1"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 10 + 11; |
| data.used_features = {124, 901, 1019}; |
| |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, URL(origin, "/resource1"), 10)); |
| resources1.push_back(CreateResource(2, URL(origin, "/resource2"), 11)); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| deleted_version.version_id = 222; // Dummy inital value |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data, resources1, &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| |
| // Make sure that the registration and resource records are stored. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data, data_out); |
| VerifyResourceRecords(resources1, resources_out); |
| |
| // Update the registration. |
| RegistrationData updated_data = data; |
| updated_data.script = URL(origin, "/resource3"); |
| updated_data.version_id = data.version_id + 1; |
| updated_data.resources_total_size_bytes = 12 + 13; |
| updated_data.used_features = {109, 421, 9101}; |
| updated_data.script_type = blink::mojom::ScriptType::kModule; |
| updated_data.update_via_cache = |
| blink::mojom::ServiceWorkerUpdateViaCache::kAll; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(3, URL(origin, "/resource3"), 12)); |
| resources2.push_back(CreateResource(4, URL(origin, "/resource4"), 13)); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(updated_data, |
| resources2, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data.version_id, deleted_version.version_id); |
| ASSERT_EQ(resources1.size(), newly_purgeable_resources.size()); |
| for (size_t i = 0; i < resources1.size(); ++i) |
| EXPECT_EQ(newly_purgeable_resources[i], resources1[i].resource_id); |
| |
| // Make sure that |updated_data| is stored and resources referred from |data| |
| // is moved to the purgeable list. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| updated_data.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(updated_data, data_out); |
| VerifyResourceRecords(resources2, resources_out); |
| |
| std::set<int64_t> purgeable_ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&purgeable_ids_out)); |
| EXPECT_EQ(2u, purgeable_ids_out.size()); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources1[0].resource_id)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources1[1].resource_id)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Registration_Multiple) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| GURL origin("https://example.com"); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| // Add registration1. |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(origin, "/foo"); |
| data1.script = URL(origin, "/resource1"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 1451 + 15234; |
| |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, URL(origin, "/resource1"), 1451)); |
| resources1.push_back(CreateResource(2, URL(origin, "/resource2"), 15234)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data1, resources1, &deleted_version, &newly_purgeable_resources)); |
| |
| // Add registration2. |
| RegistrationData data2; |
| data2.registration_id = 101; |
| data2.scope = URL(origin, "/bar"); |
| data2.script = URL(origin, "/resource3"); |
| data2.version_id = 201; |
| data2.resources_total_size_bytes = 5 + 6; |
| |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(3, URL(origin, "/resource3"), 5)); |
| resources2.push_back(CreateResource(4, URL(origin, "/resource4"), 6)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data2, resources2, &deleted_version, &newly_purgeable_resources)); |
| |
| // Make sure that registration1 is stored. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| data1.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data1, data_out); |
| VerifyResourceRecords(resources1, resources_out); |
| GURL origin_out; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistrationOrigin(data1.registration_id, &origin_out)); |
| EXPECT_EQ(origin, origin_out); |
| |
| // Make sure that registration2 is also stored. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| data2.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data2, data_out); |
| VerifyResourceRecords(resources2, resources_out); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistrationOrigin(data2.registration_id, &origin_out)); |
| EXPECT_EQ(origin, origin_out); |
| |
| std::set<int64_t> purgeable_ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&purgeable_ids_out)); |
| EXPECT_TRUE(purgeable_ids_out.empty()); |
| |
| // Delete registration1. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data1.registration_id, |
| origin, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data1.registration_id, deleted_version.registration_id); |
| |
| // Make sure that registration1 is gone. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistration( |
| data1.registration_id, origin, &data_out, &resources_out)); |
| EXPECT_TRUE(resources_out.empty()); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistrationOrigin(data1.registration_id, &origin_out)); |
| |
| purgeable_ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&purgeable_ids_out)); |
| EXPECT_EQ(2u, purgeable_ids_out.size()); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources1[0].resource_id)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, resources1[1].resource_id)); |
| |
| // Make sure that registration2 is still alive. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| data2.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data2, data_out); |
| VerifyResourceRecords(resources2, resources_out); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistrationOrigin(data2.registration_id, &origin_out)); |
| EXPECT_EQ(origin, origin_out); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Registration_UninitializedDatabase) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL origin("https://example.com"); |
| |
| // Should be failed because the database does not exist. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistration( |
| 100, origin, &data_out, &resources_out)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerRegistrationId, |
| data_out.registration_id); |
| EXPECT_TRUE(resources_out.empty()); |
| GURL origin_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistrationOrigin(100, &origin_out)); |
| |
| // Deleting non-existent registration should succeed. |
| RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration( |
| 100, origin, &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| |
| // Actually create a new database, but not initialized yet. |
| database->LazyOpen(true); |
| |
| // Should be failed because the database is not initialized. |
| ASSERT_EQ(ServiceWorkerDatabase::DATABASE_STATE_UNINITIALIZED, |
| database->state_); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistration( |
| 100, origin, &data_out, &resources_out)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerRegistrationId, |
| data_out.registration_id); |
| EXPECT_TRUE(resources_out.empty()); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistrationOrigin(100, &origin_out)); |
| |
| // Deleting non-existent registration should succeed. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration( |
| 100, origin, &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId, |
| deleted_version.version_id); |
| EXPECT_TRUE(newly_purgeable_resources.empty()); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Registration_ScriptType) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| // Default script type. |
| GURL origin1("https://www1.example.com"); |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(origin1, "/foo"); |
| data1.script = URL(origin1, "/resource1"); |
| data1.version_id = 100; |
| data1.resources_total_size_bytes = 10 + 10000; |
| EXPECT_EQ(blink::mojom::ScriptType::kClassic, data1.script_type); |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, URL(origin1, "/resource1"), 10)); |
| resources1.push_back(CreateResource(2, URL(origin1, "/resource2"), 10000)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Classic script type. |
| GURL origin2("https://www2.example.com"); |
| RegistrationData data2; |
| data2.registration_id = 200; |
| data2.scope = URL(origin2, "/bar"); |
| data2.script = URL(origin2, "/resource3"); |
| data2.version_id = 200; |
| data2.resources_total_size_bytes = 20 + 20000; |
| data2.script_type = blink::mojom::ScriptType::kClassic; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(3, URL(origin2, "/resource3"), 20)); |
| resources2.push_back(CreateResource(4, URL(origin2, "/resource4"), 20000)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Module script type. |
| GURL origin3("https://www3.example.com"); |
| RegistrationData data3; |
| data3.registration_id = 300; |
| data3.scope = URL(origin3, "/baz"); |
| data3.script = URL(origin3, "/resource5"); |
| data3.version_id = 300; |
| data3.resources_total_size_bytes = 30 + 30000; |
| data3.script_type = blink::mojom::ScriptType::kModule; |
| std::vector<Resource> resources3; |
| resources3.push_back(CreateResource(5, URL(origin3, "/resource5"), 30)); |
| resources3.push_back(CreateResource(6, URL(origin3, "/resource6"), 30000)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data3, resources3, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| RegistrationData data; |
| std::vector<Resource> resources; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration(data1.registration_id, origin1, &data, |
| &resources)); |
| VerifyRegistrationData(data1, data); |
| VerifyResourceRecords(resources1, resources); |
| EXPECT_EQ(2U, resources.size()); |
| resources.clear(); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration(data2.registration_id, origin2, &data, |
| &resources)); |
| VerifyRegistrationData(data2, data); |
| VerifyResourceRecords(resources2, resources); |
| EXPECT_EQ(2U, resources.size()); |
| resources.clear(); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration(data3.registration_id, origin3, &data, |
| &resources)); |
| VerifyRegistrationData(data3, data); |
| VerifyResourceRecords(resources3, resources); |
| EXPECT_EQ(2U, resources.size()); |
| resources.clear(); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UserData_Basic) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add a registration. |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(kOrigin, "/foo"); |
| data.script = URL(kOrigin, "/script.js"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 100; |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, data.script, 100)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data, resources, &deleted_version, &newly_purgeable_resources)); |
| |
| // Write user data associated with the stored registration. |
| std::vector<std::string> user_data_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data.registration_id, kOrigin, |
| {{"key1", "data"}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data", user_data_out[0]); |
| |
| // Writing user data not associated with the stored registration should be |
| // failed. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->WriteUserData(300, kOrigin, {{"key1", "data"}})); |
| |
| // Write empty user data for a different key. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data.registration_id, kOrigin, |
| {{"key2", std::string()}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key2"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ(std::string(), user_data_out[0]); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data", user_data_out[0]); |
| |
| // Overwrite the existing user data. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data.registration_id, kOrigin, |
| {{"key1", "overwrite"}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("overwrite", user_data_out[0]); |
| |
| // Delete the user data. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserData(data.registration_id, {"key1"})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| EXPECT_TRUE(user_data_out.empty()); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key2"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ(std::string(), user_data_out[0]); |
| |
| // Write/overwrite multiple user data keys. |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData( |
| data.registration_id, kOrigin, |
| {{"key2", "overwrite2"}, {"key3", "data3"}, {"key4", "data4"}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, |
| {"key2", "key3", "key4"}, &user_data_out)); |
| ASSERT_EQ(3u, user_data_out.size()); |
| EXPECT_EQ("overwrite2", user_data_out[0]); |
| EXPECT_EQ("data3", user_data_out[1]); |
| EXPECT_EQ("data4", user_data_out[2]); |
| // Multiple reads fail if one is not found. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key2", "key1"}, |
| &user_data_out)); |
| EXPECT_TRUE(user_data_out.empty()); |
| |
| // Delete multiple user data keys, even if some are not found. |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserData(data.registration_id, {"key1", "key2", "key3"})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key1"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key2"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data.registration_id, {"key3"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data.registration_id, {"key4"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data4", user_data_out[0]); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, |
| UserData_ReadUserDataForAllRegistrationsByKeyPrefix) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add registration 1. |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(kOrigin, "/foo"); |
| data1.script = URL(kOrigin, "/script1.js"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| |
| // Add registration 2. |
| RegistrationData data2; |
| data2.registration_id = 101; |
| data2.scope = URL(kOrigin, "/bar"); |
| data2.script = URL(kOrigin, "/script2.js"); |
| data2.version_id = 201; |
| data2.resources_total_size_bytes = 200; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with the registration1. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key_prefix:key1", "value1"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key_prefix:key2", "value2"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key_prefix:key3", "value3"}})); |
| |
| // Write user data associated with the registration2. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"key_prefix:key1", "value1"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"key_prefix:key2", "value2"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"another_key_prefix:key1", "value1"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"another_key_prefix:key2", "value2"}})); |
| |
| // Get all registrations with user data by key prefix. |
| std::vector<std::pair<int64_t, std::string>> user_data_list; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrationsByKeyPrefix( |
| "key_prefix:", &user_data_list)); |
| ASSERT_EQ(5u, user_data_list.size()); |
| |
| EXPECT_EQ(data1.registration_id, user_data_list[0].first); |
| EXPECT_EQ("value1", user_data_list[0].second); |
| EXPECT_EQ(data2.registration_id, user_data_list[1].first); |
| EXPECT_EQ("value1", user_data_list[1].second); |
| EXPECT_EQ(data1.registration_id, user_data_list[2].first); |
| EXPECT_EQ("value2", user_data_list[2].second); |
| EXPECT_EQ(data2.registration_id, user_data_list[3].first); |
| EXPECT_EQ("value2", user_data_list[3].second); |
| EXPECT_EQ(data1.registration_id, user_data_list[4].first); |
| EXPECT_EQ("value3", user_data_list[4].second); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, ReadUserDataByKeyPrefix) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add a registration. |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(kOrigin, "/foo"); |
| data.script = URL(kOrigin, "/script.js"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 100; |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, data.script, 100)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with the registration. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data.registration_id, kOrigin, |
| {{"key_prefix:key1", "value_c1"}, |
| {"key_prefix:key2", "value_c2"}, |
| {"other_key_prefix:k1", "value_d1"}, |
| {"other_key_prefix:k2", "value_d2"}})); |
| |
| std::vector<std::string> user_data; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataByKeyPrefix(data.registration_id, |
| "bogus_prefix:", &user_data)); |
| EXPECT_THAT(user_data, IsEmpty()); |
| |
| user_data.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataByKeyPrefix(data.registration_id, |
| "key_prefix:", &user_data)); |
| EXPECT_THAT(user_data, ElementsAreArray({"value_c1", "value_c2"})); |
| |
| user_data.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataByKeyPrefix(data.registration_id, |
| "other_key_prefix:", &user_data)); |
| EXPECT_THAT(user_data, ElementsAreArray({"value_d1", "value_d2"})); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, ReadUserKeysAndDataByKeyPrefix) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add a registration. |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(kOrigin, "/foo"); |
| data.script = URL(kOrigin, "/script.js"); |
| data.version_id = 200; |
| data.resources_total_size_bytes = 100; |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, data.script, 100)); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with the registration. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data.registration_id, kOrigin, |
| {{"key_prefix:key1", "value_c1"}, |
| {"key_prefix:key2", "value_c2"}, |
| {"other_key_prefix:k1", "value_d1"}, |
| {"other_key_prefix:k2", "value_d2"}})); |
| |
| base::flat_map<std::string, std::string> user_data_map; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserKeysAndDataByKeyPrefix( |
| data.registration_id, "bogus_prefix:", &user_data_map)); |
| EXPECT_THAT(user_data_map, IsEmpty()); |
| |
| user_data_map.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserKeysAndDataByKeyPrefix( |
| data.registration_id, "key_prefix:", &user_data_map)); |
| EXPECT_THAT(user_data_map, |
| ElementsAreArray(std::vector<std::pair<std::string, std::string>>{ |
| {"key1", "value_c1"}, {"key2", "value_c2"}})); |
| |
| user_data_map.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserKeysAndDataByKeyPrefix( |
| data.registration_id, "other_key_prefix:", &user_data_map)); |
| EXPECT_THAT(user_data_map, |
| ElementsAreArray(std::vector<std::pair<std::string, std::string>>{ |
| {"k1", "value_d1"}, {"k2", "value_d2"}})); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UserData_DeleteUserDataByKeyPrefixes) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add registration 1. |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(kOrigin, "/foo"); |
| data1.script = URL(kOrigin, "/script1.js"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| |
| // Add registration 2. |
| RegistrationData data2; |
| data2.registration_id = 101; |
| data2.scope = URL(kOrigin, "/bar"); |
| data2.script = URL(kOrigin, "/script2.js"); |
| data2.version_id = 201; |
| data2.resources_total_size_bytes = 200; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with registration 1. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key_prefix:key1", "value_a1"}, |
| {"key_prefix:key2", "value_a2"}, |
| {"key_prefix:key3", "value_a3"}, |
| {"kept_key_prefix:key1", "value_b1"}})); |
| |
| // Write user data associated with registration 2. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"key_prefix:key1", "value_c1"}, |
| {"key_prefix:key2", "value_c2"}, |
| {"other_key_prefix:key1", "value_d1"}, |
| {"other_key_prefix:key2", "value_d2"}, |
| {"kept_key_prefix:key1", "value_e1"}, |
| {"kept_key_prefix:key2", "value_e2"}})); |
| |
| // Deleting user data by key prefixes should return STATUS_OK (rather than |
| // STATUS_ERROR_NOT_FOUND) even if no keys match the prefixes and so nothing |
| // is deleted. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserDataByKeyPrefixes( |
| data2.registration_id, |
| {"not_found_key_prefix1:", "not_found_key_prefix2:"})); |
| |
| // Actually delete user data by key prefixes for registration 2. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserDataByKeyPrefixes( |
| data2.registration_id, |
| {"key_prefix:", "other_key_prefix:", "not_found_key_prefix:"})); |
| |
| // User data with deleted "key_prefix:" should only remain for registration 1. |
| std::vector<std::pair<int64_t, std::string>> user_data_list; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrationsByKeyPrefix( |
| "key_prefix:", &user_data_list)); |
| ASSERT_EQ(3u, user_data_list.size()); |
| EXPECT_EQ(data1.registration_id, user_data_list[0].first); |
| EXPECT_EQ("value_a1", user_data_list[0].second); |
| EXPECT_EQ(data1.registration_id, user_data_list[1].first); |
| EXPECT_EQ("value_a2", user_data_list[1].second); |
| EXPECT_EQ(data1.registration_id, user_data_list[2].first); |
| EXPECT_EQ("value_a3", user_data_list[2].second); |
| |
| // User data for second deleted key prefix should also have been deleted. |
| user_data_list.clear(); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrationsByKeyPrefix( |
| "other_key_prefix:", &user_data_list)); |
| ASSERT_EQ(0u, user_data_list.size()); |
| |
| // User data with "kept_key_prefix:" that was not deleted should remain on |
| // both registrations. |
| user_data_list.clear(); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrationsByKeyPrefix( |
| "kept_key_prefix:", &user_data_list)); |
| ASSERT_EQ(3u, user_data_list.size()); |
| EXPECT_EQ(data1.registration_id, user_data_list[0].first); |
| EXPECT_EQ("value_b1", user_data_list[0].second); |
| EXPECT_EQ(data2.registration_id, user_data_list[1].first); |
| EXPECT_EQ("value_e1", user_data_list[1].second); |
| EXPECT_EQ(data2.registration_id, user_data_list[2].first); |
| EXPECT_EQ("value_e2", user_data_list[2].second); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UserData_DataIsolation) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add registration 1. |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(kOrigin, "/foo"); |
| data1.script = URL(kOrigin, "/script1.js"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| |
| // Add registration 2. |
| RegistrationData data2; |
| data2.registration_id = 101; |
| data2.scope = URL(kOrigin, "/bar"); |
| data2.script = URL(kOrigin, "/script2.js"); |
| data2.version_id = 201; |
| data2.resources_total_size_bytes = 200; |
| data2.update_via_cache = blink::mojom::ServiceWorkerUpdateViaCache::kImports; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with the registration1. |
| std::vector<std::string> user_data_out; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key", "data1"}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data1.registration_id, {"key"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data1", user_data_out[0]); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data2.registration_id, {"key"}, &user_data_out)); |
| |
| // Write user data associated with the registration2. This shouldn't overwrite |
| // the data associated with registration1. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"key", "data2"}})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data1.registration_id, {"key"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data1", user_data_out[0]); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data2.registration_id, {"key"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data2", user_data_out[0]); |
| |
| // Get all registrations with user data. |
| std::vector<std::pair<int64_t, std::string>> user_data_list; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrations("key", &user_data_list)); |
| EXPECT_EQ(2u, user_data_list.size()); |
| EXPECT_EQ(data1.registration_id, user_data_list[0].first); |
| EXPECT_EQ("data1", user_data_list[0].second); |
| EXPECT_EQ(data2.registration_id, user_data_list[1].first); |
| EXPECT_EQ("data2", user_data_list[1].second); |
| |
| // Delete the data associated with the registration2. This shouldn't delete |
| // the data associated with registration1. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserData(data2.registration_id, {"key"})); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data1.registration_id, {"key"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data1", user_data_out[0]); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data2.registration_id, {"key"}, &user_data_out)); |
| |
| // And again get all registrations with user data. |
| user_data_list.clear(); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserDataForAllRegistrations("key", &user_data_list)); |
| EXPECT_EQ(1u, user_data_list.size()); |
| EXPECT_EQ(data1.registration_id, user_data_list[0].first); |
| EXPECT_EQ("data1", user_data_list[0].second); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UserData_DeleteRegistration) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Add registration 1. |
| RegistrationData data1; |
| data1.registration_id = 100; |
| data1.scope = URL(kOrigin, "/foo"); |
| data1.script = URL(kOrigin, "/script1.js"); |
| data1.version_id = 200; |
| data1.resources_total_size_bytes = 100; |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, data1.script, 100)); |
| |
| // Add registration 2. |
| RegistrationData data2; |
| data2.registration_id = 101; |
| data2.scope = URL(kOrigin, "/bar"); |
| data2.script = URL(kOrigin, "/script2.js"); |
| data2.version_id = 201; |
| data2.resources_total_size_bytes = 200; |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(2, data2.script, 200)); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources1, &deleted_version, |
| &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources2, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write user data associated with the registration1. |
| std::vector<std::string> user_data_out; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key1", "data1"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, kOrigin, |
| {{"key2", "data2"}})); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data1.registration_id, {"key1"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| ASSERT_EQ("data1", user_data_out[0]); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data1.registration_id, {"key2"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| ASSERT_EQ("data2", user_data_out[0]); |
| |
| // Write user data associated with the registration2. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, kOrigin, |
| {{"key3", "data3"}})); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data2.registration_id, {"key3"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| ASSERT_EQ("data3", user_data_out[0]); |
| |
| // Delete all data associated with the registration1. This shouldn't delete |
| // the data associated with registration2. |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration( |
| data1.registration_id, kOrigin, |
| &deleted_version, &newly_purgeable_resources)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data1.registration_id, {"key1"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data1.registration_id, {"key2"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data2.registration_id, {"key3"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data3", user_data_out[0]); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UserData_UninitializedDatabase) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| const GURL kOrigin("https://example.com"); |
| |
| // Should be failed because the database does not exist. |
| std::vector<std::string> user_data_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(100, {"key"}, &user_data_out)); |
| |
| // Should be failed because the associated registration does not exist. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->WriteUserData(100, kOrigin, {{"key", "data"}})); |
| |
| // Deleting non-existent entry should succeed. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserData(100, {"key"})); |
| |
| // Actually create a new database, but not initialized yet. |
| database->LazyOpen(true); |
| |
| // Should be failed because the database is not initialized. |
| ASSERT_EQ(ServiceWorkerDatabase::DATABASE_STATE_UNINITIALIZED, |
| database->state_); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(100, {"key"}, &user_data_out)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->WriteUserData(100, kOrigin, {{"key", "data"}})); |
| |
| // Deleting non-existent entry should succeed. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteUserData(100, {"key"})); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UpdateVersionToActive) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| GURL origin("https://example.com"); |
| |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| // Should be false because a registration does not exist. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->UpdateVersionToActive(0, origin)); |
| |
| // Add a registration. |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/script.js"); |
| data.version_id = 200; |
| data.is_active = false; |
| data.resources_total_size_bytes = 100; |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, data.script, 100)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Make sure that the registration is stored. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data, data_out); |
| EXPECT_EQ(1u, resources_out.size()); |
| |
| // Activate the registration. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->UpdateVersionToActive(data.registration_id, origin)); |
| |
| // Make sure that the registration is activated. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| RegistrationData expected_data = data; |
| expected_data.is_active = true; |
| VerifyRegistrationData(expected_data, data_out); |
| EXPECT_EQ(1u, resources_out.size()); |
| |
| // Delete the registration. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data.registration_id, |
| origin, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data.registration_id, deleted_version.registration_id); |
| |
| // Should be false because the registration is gone. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->UpdateVersionToActive(data.registration_id, origin)); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UpdateLastCheckTime) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| GURL origin("https://example.com"); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| // Should be false because a registration does not exist. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->UpdateLastCheckTime(0, origin, base::Time::Now())); |
| |
| // Add a registration. |
| RegistrationData data; |
| data.registration_id = 100; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/script.js"); |
| data.version_id = 200; |
| data.last_update_check = base::Time::Now(); |
| data.resources_total_size_bytes = 100; |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(1, data.script, 100)); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Make sure that the registration is stored. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| VerifyRegistrationData(data, data_out); |
| EXPECT_EQ(1u, resources_out.size()); |
| |
| // Update the last check time. |
| base::Time updated_time = base::Time::Now(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->UpdateLastCheckTime( |
| data.registration_id, origin, updated_time)); |
| |
| // Make sure that the registration is updated. |
| resources_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistration( |
| data.registration_id, origin, &data_out, &resources_out)); |
| RegistrationData expected_data = data; |
| expected_data.last_update_check = updated_time; |
| VerifyRegistrationData(expected_data, data_out); |
| EXPECT_EQ(1u, resources_out.size()); |
| |
| // Delete the registration. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteRegistration(data.registration_id, |
| origin, |
| &deleted_version, |
| &newly_purgeable_resources)); |
| EXPECT_EQ(data.registration_id, deleted_version.registration_id); |
| |
| // Should be false because the registration is gone. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->UpdateLastCheckTime( |
| data.registration_id, origin, base::Time::Now())); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, UncommittedAndPurgeableResourceIds) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| |
| // Write {1, 2, 3} into the uncommitted list. |
| std::set<int64_t> ids1; |
| ids1.insert(1); |
| ids1.insert(2); |
| ids1.insert(3); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds(ids1)); |
| |
| std::set<int64_t> ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetUncommittedResourceIds(&ids_out)); |
| EXPECT_EQ(ids1, ids_out); |
| |
| // Write {2, 4} into the uncommitted list. |
| std::set<int64_t> ids2; |
| ids2.insert(2); |
| ids2.insert(4); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUncommittedResourceIds(ids2)); |
| |
| ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetUncommittedResourceIds(&ids_out)); |
| std::set<int64_t> expected = base::STLSetUnion<std::set<int64_t>>(ids1, ids2); |
| EXPECT_EQ(expected, ids_out); |
| |
| // Move {2, 4} from the uncommitted list to the purgeable list. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->PurgeUncommittedResourceIds(ids2)); |
| ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&ids_out)); |
| EXPECT_EQ(ids2, ids_out); |
| |
| // Delete {2, 4} from the purgeable list. |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->ClearPurgeableResourceIds(ids2)); |
| ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&ids_out)); |
| EXPECT_TRUE(ids_out.empty()); |
| |
| // {1, 3} should be still in the uncommitted list. |
| ids_out.clear(); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetUncommittedResourceIds(&ids_out)); |
| expected = base::STLSetDifference<std::set<int64_t>>(ids1, ids2); |
| EXPECT_EQ(expected, ids_out); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DeleteAllDataForOrigin) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| // Data associated with |origin1| will be removed. |
| GURL origin1("https://example.com"); |
| GURL origin2("https://example.org"); |
| |
| // |origin1| has two registrations (registration1 and registration2). |
| RegistrationData data1; |
| data1.registration_id = 10; |
| data1.scope = URL(origin1, "/foo"); |
| data1.script = URL(origin1, "/resource1"); |
| data1.version_id = 100; |
| data1.resources_total_size_bytes = 2013 + 512; |
| |
| std::vector<Resource> resources1; |
| resources1.push_back(CreateResource(1, URL(origin1, "/resource1"), 2013)); |
| resources1.push_back(CreateResource(2, URL(origin1, "/resource2"), 512)); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data1, resources1, &deleted_version, &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, origin1, |
| {{"key1", "data1"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data1.registration_id, origin1, |
| {{"key2", "data2"}})); |
| |
| RegistrationData data2; |
| data2.registration_id = 11; |
| data2.scope = URL(origin1, "/bar"); |
| data2.script = URL(origin1, "/resource3"); |
| data2.version_id = 101; |
| data2.resources_total_size_bytes = 4 + 5; |
| |
| std::vector<Resource> resources2; |
| resources2.push_back(CreateResource(3, URL(origin1, "/resource3"), 4)); |
| resources2.push_back(CreateResource(4, URL(origin1, "/resource4"), 5)); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data2, resources2, &deleted_version, &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, origin1, |
| {{"key3", "data3"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data2.registration_id, origin1, |
| {{"key4", "data4"}})); |
| |
| // |origin2| has one registration (registration3). |
| RegistrationData data3; |
| data3.registration_id = 12; |
| data3.scope = URL(origin2, "/hoge"); |
| data3.script = URL(origin2, "/resource5"); |
| data3.version_id = 102; |
| data3.resources_total_size_bytes = 6 + 7; |
| |
| std::vector<Resource> resources3; |
| resources3.push_back(CreateResource(5, URL(origin2, "/resource5"), 6)); |
| resources3.push_back(CreateResource(6, URL(origin2, "/resource6"), 7)); |
| ASSERT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration( |
| data3, resources3, &deleted_version, &newly_purgeable_resources)); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data3.registration_id, origin2, |
| {{"key5", "data5"}})); |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteUserData(data3.registration_id, origin2, |
| {{"key6", "data6"}})); |
| |
| std::set<GURL> origins_to_delete; |
| origins_to_delete.insert(origin1); |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->DeleteAllDataForOrigins(origins_to_delete, |
| &newly_purgeable_resources)); |
| |
| // |origin1| should be removed from the unique origin list. |
| std::set<GURL> unique_origins; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetOriginsWithRegistrations(&unique_origins)); |
| EXPECT_EQ(1u, unique_origins.size()); |
| EXPECT_TRUE(base::ContainsKey(unique_origins, origin2)); |
| |
| // The registrations for |origin1| should be removed. |
| std::vector<RegistrationData> registrations; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->GetRegistrationsForOrigin(origin1, ®istrations, nullptr)); |
| EXPECT_TRUE(registrations.empty()); |
| GURL origin_out; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadRegistrationOrigin(data1.registration_id, &origin_out)); |
| |
| // The registration for |origin2| should not be removed. |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->ReadRegistration( |
| data3.registration_id, origin2, &data_out, &resources_out)); |
| VerifyRegistrationData(data3, data_out); |
| VerifyResourceRecords(resources3, resources_out); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadRegistrationOrigin(data3.registration_id, &origin_out)); |
| EXPECT_EQ(origin2, origin_out); |
| |
| // The resources associated with |origin1| should be purgeable. |
| std::set<int64_t> purgeable_ids_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->GetPurgeableResourceIds(&purgeable_ids_out)); |
| EXPECT_EQ(4u, purgeable_ids_out.size()); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, 1)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, 2)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, 3)); |
| EXPECT_TRUE(base::ContainsKey(purgeable_ids_out, 4)); |
| |
| // The user data associated with |origin1| should be removed. |
| std::vector<std::string> user_data_out; |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data1.registration_id, {"key1"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data1.registration_id, {"key2"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data2.registration_id, {"key3"}, &user_data_out)); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadUserData(data2.registration_id, {"key4"}, &user_data_out)); |
| |
| // The user data associated with |origin2| should not be removed. |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data3.registration_id, {"key5"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data5", user_data_out[0]); |
| EXPECT_EQ( |
| ServiceWorkerDatabase::STATUS_OK, |
| database->ReadUserData(data3.registration_id, {"key6"}, &user_data_out)); |
| ASSERT_EQ(1u, user_data_out.size()); |
| EXPECT_EQ("data6", user_data_out[0]); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, DestroyDatabase) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| std::unique_ptr<ServiceWorkerDatabase> database( |
| CreateDatabase(database_dir.GetPath())); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->LazyOpen(true)); |
| ASSERT_TRUE(base::DirectoryExists(database_dir.GetPath())); |
| |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK, database->DestroyDatabase()); |
| ASSERT_FALSE(base::DirectoryExists(database_dir.GetPath())); |
| } |
| |
| TEST(ServiceWorkerDatabaseTest, Corruption_NoMainResource) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| |
| GURL origin("https://example.com"); |
| |
| RegistrationData data; |
| data.registration_id = 10; |
| data.scope = URL(origin, "/foo"); |
| data.script = URL(origin, "/resource1"); |
| data.version_id = 100; |
| data.resources_total_size_bytes = 2016; |
| |
| // Simulate that "/resource1" wasn't correctly written in the database by not |
| // adding it. |
| std::vector<Resource> resources; |
| resources.push_back(CreateResource(2, URL(origin, "/resource2"), 2016)); |
| |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // The database should detect lack of the main resource (i.e. "/resource1"). |
| RegistrationData data_out; |
| std::vector<Resource> resources_out; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED, |
| database->ReadRegistration(data.registration_id, origin, &data_out, |
| &resources_out)); |
| EXPECT_TRUE(resources_out.empty()); |
| } |
| |
| // Tests that GetRegistrationsForOrigin() detects corruption without crashing. |
| // It must delete the database after freeing the iterator it uses to read all |
| // registrations. Regression test for https://crbug.com/909024. |
| TEST(ServiceWorkerDatabaseTest, Corruption_GetRegistrationsForOrigin) { |
| std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory()); |
| ServiceWorkerDatabase::RegistrationData deleted_version; |
| std::vector<int64_t> newly_purgeable_resources; |
| std::vector<Resource> resources; |
| GURL origin("https://example.com"); |
| |
| // Write a normal registration. |
| RegistrationData data1; |
| data1.registration_id = 1; |
| data1.scope = URL(origin, "/foo"); |
| data1.script = URL(origin, "/resource1"); |
| data1.version_id = 1; |
| data1.resources_total_size_bytes = 2016; |
| resources = {CreateResource(1, URL(origin, "/resource1"), 2016)}; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data1, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Write a corrupt registration. |
| RegistrationData data2; |
| data2.registration_id = 2; |
| data2.scope = URL(origin, "/foo"); |
| data2.script = URL(origin, "/resource2"); |
| data2.version_id = 2; |
| data2.resources_total_size_bytes = 2016; |
| // Simulate that "/resource2" wasn't correctly written in the database by |
| // not adding it. |
| resources = {CreateResource(3, URL(origin, "/resource3"), 2016)}; |
| ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, |
| database->WriteRegistration(data2, resources, &deleted_version, |
| &newly_purgeable_resources)); |
| |
| // Call GetRegistrationsForOrigin(). It should detect corruption, and not |
| // crash. |
| base::HistogramTester histogram_tester; |
| std::vector<RegistrationData> registrations; |
| std::vector<std::vector<ServiceWorkerDatabase::ResourceRecord>> |
| resources_list; |
| EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED, |
| database->GetRegistrationsForOrigin(origin, ®istrations, |
| &resources_list)); |
| EXPECT_TRUE(registrations.empty()); |
| EXPECT_TRUE(resources_list.empty()); |
| |
| // There should be three "read" operations logged: |
| // 1. Reading all registration data. |
| // 2. Reading the resources of the first registration. |
| // 3. Reading the resources of the second registration. This one fails. |
| histogram_tester.ExpectTotalCount("ServiceWorker.Database.ReadResult", 3); |
| histogram_tester.ExpectBucketCount("ServiceWorker.Database.ReadResult", |
| ServiceWorkerDatabase::STATUS_OK, 2); |
| histogram_tester.ExpectBucketCount( |
| "ServiceWorker.Database.ReadResult", |
| ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED, 1); |
| } |
| |
| } // namespace content |