| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/android/webapk/webapk_sync_bridge.h" |
| |
| #include "base/containers/contains.h" |
| #include "base/logging.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/android/webapk/test/fake_data_type_store_service.h" |
| #include "chrome/browser/android/webapk/test/fake_webapk_specifics_fetcher.h" |
| #include "chrome/browser/android/webapk/webapk_helpers.h" |
| #include "chrome/browser/android/webapk/webapk_registry_update.h" |
| #include "chrome/browser/android/webapk/webapk_restore_task.h" |
| #include "components/sync/model/data_batch.h" |
| #include "components/sync/model/data_type_store_service.h" |
| #include "components/sync/model/entity_change.h" |
| #include "components/sync/protocol/entity_data.h" |
| #include "components/sync/test/mock_data_type_local_change_processor.h" |
| #include "components/webapps/browser/android/shortcut_info.h" |
| #include "components/webapps/common/web_app_id.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| using testing::_; |
| using testing::IsEmpty; |
| using testing::SizeIs; |
| |
| namespace webapk { |
| |
| bool IsBasicSyncDataEqual(const WebApkProto& expected_app, |
| const syncer::EntityData& entity_data) { |
| if (!entity_data.specifics.has_web_apk()) { |
| return false; |
| } |
| |
| const sync_pb::WebApkSpecifics& expected_app_specifics = |
| expected_app.sync_data(); |
| const sync_pb::WebApkSpecifics& entity_specifics = |
| entity_data.specifics.web_apk(); |
| |
| return expected_app_specifics.manifest_id() == |
| entity_specifics.manifest_id() && |
| expected_app_specifics.start_url() == entity_specifics.start_url() && |
| expected_app_specifics.name() == entity_specifics.name(); |
| } |
| |
| bool RegistryContainsSyncDataBatchChanges( |
| const Registry& registry, |
| std::unique_ptr<syncer::DataBatch> data_batch) { |
| if (!data_batch || !data_batch->HasNext()) { |
| return registry.empty(); |
| } |
| |
| while (data_batch->HasNext()) { |
| syncer::KeyAndData key_and_data = data_batch->Next(); |
| auto web_app_iter = registry.find(key_and_data.first); |
| if (web_app_iter == registry.end()) { |
| LOG(ERROR) << "App not found in registry: " << key_and_data.first; |
| return false; |
| } |
| |
| if (!IsBasicSyncDataEqual(*web_app_iter->second, *key_and_data.second)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| std::unique_ptr<WebApkProto> CreateWebApkProto(const std::string& url, |
| const std::string& name) { |
| std::unique_ptr<WebApkProto> web_apk = std::make_unique<WebApkProto>(); |
| |
| sync_pb::WebApkSpecifics* sync_data = web_apk->mutable_sync_data(); |
| sync_data->set_manifest_id(url); |
| sync_data->set_start_url(url); |
| sync_data->set_name(name); |
| |
| return web_apk; |
| } |
| |
| void InsertAppIntoRegistry(Registry* registry, |
| std::unique_ptr<WebApkProto> app) { |
| webapps::AppId app_id = |
| GenerateAppIdFromManifestId(GURL(app->sync_data().manifest_id())); |
| ASSERT_FALSE(base::Contains(*registry, app_id)); |
| registry->emplace(std::move(app_id), std::move(app)); |
| } |
| |
| int64_t UnixTsSecToWindowsTsMsec(double unix_ts_sec) { |
| return base::Time::FromSecondsSinceUnixEpoch(unix_ts_sec) |
| .ToDeltaSinceWindowsEpoch() |
| .InMicroseconds(); |
| } |
| |
| class WebApkSyncBridgeTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| data_type_store_service_ = std::make_unique<FakeDataTypeStoreService>(); |
| |
| ON_CALL(mock_processor_, IsTrackingMetadata()) |
| .WillByDefault(testing::Return(true)); |
| } |
| |
| void InitSyncBridge() { |
| base::RunLoop loop; |
| |
| std::unique_ptr<base::SimpleTestClock> clock = |
| std::make_unique<base::SimpleTestClock>(); |
| clock->SetNow(base::Time::FromSecondsSinceUnixEpoch( |
| 1136232245.0)); // Mon Jan 02 2006 15:04:05 GMT-0500 |
| |
| std::unique_ptr<FakeWebApkSpecificsFetcher> specifics_fetcher = |
| std::make_unique<FakeWebApkSpecificsFetcher>(); |
| specifics_fetcher_ = specifics_fetcher.get(); |
| |
| sync_bridge_ = std::make_unique<WebApkSyncBridge>( |
| data_type_store_service_.get(), loop.QuitClosure(), |
| mock_processor_.CreateForwardingProcessor(), std::move(clock), |
| std::move(specifics_fetcher)); |
| |
| loop.Run(); |
| } |
| |
| protected: |
| syncer::MockDataTypeLocalChangeProcessor& processor() { |
| return mock_processor_; |
| } |
| FakeDataTypeStoreService& data_type_store_service() { |
| return *data_type_store_service_; |
| } |
| |
| WebApkSyncBridge& sync_bridge() { return *sync_bridge_; } |
| FakeWebApkSpecificsFetcher& specifics_fetcher() { |
| return *specifics_fetcher_; |
| } |
| |
| private: |
| std::unique_ptr<FakeDataTypeStoreService> data_type_store_service_; |
| std::unique_ptr<WebApkSyncBridge> sync_bridge_; |
| raw_ptr<FakeWebApkSpecificsFetcher> |
| specifics_fetcher_; // owned by sync_bridge_; should not be accessed |
| // before InitSyncBridge() or after sync_bridge_ is |
| // destroyed |
| |
| testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_; |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| }; |
| |
| TEST_F(WebApkSyncBridgeTest, |
| ManifestIdStrToAppId_DoesNotCrashOnEmptyStringOrInvalidManifestId) { |
| EXPECT_EQ(ManifestIdStrToAppId(""), ""); |
| EXPECT_EQ(ManifestIdStrToAppId("%%"), ""); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, AppWasUsedRecently) { |
| InitSyncBridge(); |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app1 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app1->set_last_used_time_windows_epoch_micros(UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 GMT-0500 - slightly before |
| // clock_.Now() (recent enough) |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app2 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app2->set_last_used_time_windows_epoch_micros(UnixTsSecToWindowsTsMsec( |
| 1136318645.0)); // Tue Jan 03 2006 15:04:05 GMT-0500 - slightly after |
| // clock_.Now() (recent enough) |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app3 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app3->set_last_used_time_windows_epoch_micros(UnixTsSecToWindowsTsMsec( |
| 1133726645.0)); // Sun Dec 04 2005 15:04:05 GMT-0500 - 29 days before |
| // clock_.Now() (recent enough) |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app4 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app4->set_last_used_time_windows_epoch_micros(UnixTsSecToWindowsTsMsec( |
| 1133640244.0)); // Sat Dec 03 2005 15:04:04 GMT-0500 - 30 days and 1 |
| // second before clock_.Now() (not recent enough) |
| |
| EXPECT_TRUE(sync_bridge().AppWasUsedRecently(app1.get())); |
| EXPECT_TRUE(sync_bridge().AppWasUsedRecently(app2.get())); |
| EXPECT_TRUE(sync_bridge().AppWasUsedRecently(app3.get())); |
| EXPECT_FALSE(sync_bridge().AppWasUsedRecently(app4.get())); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, PrepareRegistryUpdateFromSyncApps) { |
| const std::string manifest_id_1 = "https://example.com/app1"; |
| const std::string manifest_id_2 = "https://example.com/app2"; |
| const std::string manifest_id_3 = "https://example.com/app3"; |
| const std::string manifest_id_4 = "https://example.com/app4"; |
| const std::string manifest_id_5 = "https://example.com/app5"; |
| |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> synced_app1 = |
| CreateWebApkProto(manifest_id_5, "name"); |
| InsertAppIntoRegistry(®istry, std::move(synced_app1)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| InitSyncBridge(); |
| |
| syncer::EntityData sync_data_1; |
| sync_pb::WebApkSpecifics* sync_specifics_1 = |
| sync_data_1.specifics.mutable_web_apk(); |
| sync_specifics_1->set_manifest_id(manifest_id_1); |
| sync_specifics_1->set_name("app1_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_1 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_1), |
| std::move(sync_data_1)); |
| |
| // There's no entry in the registry to delete, so we ignore it |
| std::unique_ptr<syncer::EntityChange> sync_change_2 = |
| syncer::EntityChange::CreateDelete(ManifestIdStrToAppId(manifest_id_2), |
| syncer::EntityData()); |
| |
| syncer::EntityData sync_data_3; |
| sync_pb::WebApkSpecifics* sync_specifics_3 = |
| sync_data_3.specifics.mutable_web_apk(); |
| sync_specifics_3->set_manifest_id(manifest_id_3); |
| sync_specifics_3->set_name("app3_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_3 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_3), |
| std::move(sync_data_3)); |
| |
| // There's no entry in the registry to delete, so we ignore it |
| std::unique_ptr<syncer::EntityChange> sync_change_4 = |
| syncer::EntityChange::CreateDelete(ManifestIdStrToAppId(manifest_id_4), |
| syncer::EntityData()); |
| |
| // There IS an entry in the registry to delete -> included in output |
| std::unique_ptr<syncer::EntityChange> sync_change_5 = |
| syncer::EntityChange::CreateDelete(ManifestIdStrToAppId(manifest_id_5), |
| syncer::EntityData()); |
| |
| syncer::EntityChangeList sync_changes; |
| sync_changes.push_back(std::move(sync_change_1)); |
| sync_changes.push_back(std::move(sync_change_2)); |
| sync_changes.push_back(std::move(sync_change_3)); |
| sync_changes.push_back(std::move(sync_change_4)); |
| sync_changes.push_back(std::move(sync_change_5)); |
| |
| RegistryUpdateData registry_update_from_sync; |
| sync_bridge().PrepareRegistryUpdateFromSyncApps(sync_changes, |
| ®istry_update_from_sync); |
| |
| EXPECT_EQ(2u, registry_update_from_sync.apps_to_create.size()); |
| |
| EXPECT_FALSE( |
| registry_update_from_sync.apps_to_create.at(0)->is_locally_installed()); |
| EXPECT_EQ(manifest_id_1, registry_update_from_sync.apps_to_create.at(0) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app1_sync", |
| registry_update_from_sync.apps_to_create.at(0)->sync_data().name()); |
| |
| EXPECT_FALSE( |
| registry_update_from_sync.apps_to_create.at(1)->is_locally_installed()); |
| EXPECT_EQ(manifest_id_3, registry_update_from_sync.apps_to_create.at(1) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app3_sync", |
| registry_update_from_sync.apps_to_create.at(1)->sync_data().name()); |
| |
| EXPECT_EQ(1u, registry_update_from_sync.apps_to_delete.size()); |
| EXPECT_EQ(ManifestIdStrToAppId(manifest_id_5), |
| registry_update_from_sync.apps_to_delete.at(0)); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, SyncDataContainsNewApps) { |
| InitSyncBridge(); |
| |
| const std::string manifest_id_1 = "https://example.com/app1"; |
| const std::string manifest_id_2 = "https://example.com/app2"; |
| const std::string manifest_id_3 = "https://example.com/app3"; |
| const std::string manifest_id_4 = "https://example.com/app4"; |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app1 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app1->set_manifest_id(manifest_id_1); |
| app1->set_name("app1_installed"); |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app2 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app2->set_manifest_id(manifest_id_2); |
| app2->set_name("app2_installed"); |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> app3 = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| app3->set_manifest_id(manifest_id_3); |
| app3->set_name("app3_installed"); |
| |
| std::vector<std::unique_ptr<sync_pb::WebApkSpecifics>> installed_apps; |
| installed_apps.push_back(std::move(app1)); |
| installed_apps.push_back(std::move(app2)); |
| installed_apps.push_back(std::move(app3)); |
| |
| syncer::EntityData sync_data_1; |
| sync_pb::WebApkSpecifics* sync_specifics_1 = |
| sync_data_1.specifics.mutable_web_apk(); |
| sync_specifics_1->set_manifest_id(manifest_id_1); |
| sync_specifics_1->set_name("app1_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_1 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_1), |
| std::move(sync_data_1)); |
| |
| syncer::EntityData sync_data_2; |
| sync_pb::WebApkSpecifics* sync_specifics_2 = |
| sync_data_2.specifics.mutable_web_apk(); |
| sync_specifics_2->set_manifest_id(manifest_id_2); |
| sync_specifics_2->set_name("app2_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_2 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_2), |
| std::move(sync_data_2)); |
| |
| syncer::EntityData sync_data_3; |
| sync_pb::WebApkSpecifics* sync_specifics_3 = |
| sync_data_3.specifics.mutable_web_apk(); |
| sync_specifics_3->set_manifest_id(manifest_id_3); |
| sync_specifics_3->set_name("app3_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_3 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_3), |
| std::move(sync_data_3)); |
| |
| syncer::EntityData sync_data_4; |
| sync_pb::WebApkSpecifics* sync_specifics_4 = |
| sync_data_4.specifics.mutable_web_apk(); |
| sync_specifics_4->set_manifest_id(manifest_id_4); |
| sync_specifics_4->set_name("app4_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_4 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_4), |
| std::move(sync_data_4)); |
| |
| syncer::EntityChangeList sync_changes; |
| sync_changes.push_back(std::move(sync_change_1)); |
| sync_changes.push_back(std::move(sync_change_2)); |
| |
| EXPECT_FALSE( |
| sync_bridge().SyncDataContainsNewApps(installed_apps, sync_changes)); |
| |
| syncer::EntityChangeList sync_changes_2; |
| sync_changes_2.push_back(std::move(sync_change_4)); // 4 exists in sync, but |
| // not on the device |
| sync_changes_2.push_back(std::move(sync_change_3)); |
| |
| EXPECT_TRUE( |
| sync_bridge().SyncDataContainsNewApps(installed_apps, sync_changes_2)); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, MergeFullSyncData) { |
| // inputs: |
| // sync: |
| // * App1 add oldest |
| // * App2 add new |
| // * App4 add |
| // * App5 delete |
| // |
| // registry: |
| // * App1 newest |
| // * App5 |
| // * App6 |
| // |
| // outputs: |
| // send to sync: |
| // * App1 middle (installed) |
| // * App6 (registry) |
| // |
| // final state (sync, registry, and db): |
| // * App1 middle (installed) |
| // * App2 new (sync) |
| // * App4 (sync) |
| // * App6 (registry) |
| |
| const std::string manifest_id_1 = "https://example.com/app1"; |
| const std::string manifest_id_2 = "https://example.com/app2"; |
| const std::string manifest_id_3 = "https://example.com/app3"; |
| const std::string manifest_id_4 = "https://example.com/app4"; |
| const std::string manifest_id_5 = "https://example.com/app5"; |
| const std::string manifest_id_6 = "https://example.com/app6"; |
| |
| Registry registry; |
| |
| // not sent to sync or included in final state (installed version always |
| // overrides registry version) |
| std::unique_ptr<WebApkProto> registry_app1 = |
| CreateWebApkProto(manifest_id_1, "app1_registry"); |
| registry_app1->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 GMT-0500 - newest app1 |
| // timestamp |
| InsertAppIntoRegistry(®istry, std::move(registry_app1)); |
| |
| // not sent to sync or included in final state (deleted by sync) |
| std::unique_ptr<WebApkProto> registry_app5 = |
| CreateWebApkProto(manifest_id_5, "app5_registry"); |
| InsertAppIntoRegistry(®istry, std::move(registry_app5)); |
| |
| // sent to sync and included in final state (no other version exists) |
| std::unique_ptr<WebApkProto> registry_app6 = |
| CreateWebApkProto(manifest_id_6, "app6_registry"); |
| InsertAppIntoRegistry(®istry, std::move(registry_app6)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| InitSyncBridge(); |
| |
| // not included in final state (older than installed version) |
| syncer::EntityData sync_data_1; |
| sync_pb::WebApkSpecifics* sync_specifics_1 = |
| sync_data_1.specifics.mutable_web_apk(); |
| sync_specifics_1->set_manifest_id(manifest_id_1); |
| sync_specifics_1->set_name("app1_sync"); |
| sync_specifics_1->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1134072245.0)); // Thu Dec 08 2005 15:04:05 GMT-0500 - oldest app1 |
| // timestamp |
| std::unique_ptr<syncer::EntityChange> sync_change_1 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_1), |
| std::move(sync_data_1)); |
| |
| // included in final state (newer than installed version) |
| syncer::EntityData sync_data_2; |
| sync_pb::WebApkSpecifics* sync_specifics_2 = |
| sync_data_2.specifics.mutable_web_apk(); |
| sync_specifics_2->set_manifest_id(manifest_id_2); |
| sync_specifics_2->set_name("app2_sync"); |
| sync_specifics_2->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 GMT-0500 - newest app2 |
| // timestamp |
| std::unique_ptr<syncer::EntityChange> sync_change_2 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_2), |
| std::move(sync_data_2)); |
| |
| // included in final state (no other version exists) |
| syncer::EntityData sync_data_4; |
| sync_pb::WebApkSpecifics* sync_specifics_4 = |
| sync_data_4.specifics.mutable_web_apk(); |
| sync_specifics_4->set_manifest_id(manifest_id_4); |
| sync_specifics_4->set_name("app4_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_4 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_4), |
| std::move(sync_data_4)); |
| |
| // causes app5 to be not included in final state (no installed version, only |
| // registry) |
| std::unique_ptr<syncer::EntityChange> sync_change_5 = |
| syncer::EntityChange::CreateDelete(ManifestIdStrToAppId(manifest_id_5), |
| syncer::EntityData()); |
| |
| syncer::EntityChangeList sync_changes; |
| sync_changes.push_back(std::move(sync_change_1)); |
| sync_changes.push_back(std::move(sync_change_2)); |
| sync_changes.push_back(std::move(sync_change_4)); |
| sync_changes.push_back(std::move(sync_change_5)); |
| |
| std::unique_ptr<syncer::MetadataChangeList> metadata_change_list = |
| syncer::DataTypeStore::WriteBatch::CreateMetadataChangeList(); |
| |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| std::optional<syncer::ModelError> result = sync_bridge().MergeFullSyncData( |
| std::move(metadata_change_list), std::move(sync_changes)); |
| |
| EXPECT_EQ(std::nullopt, result); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(4u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id_1, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app1_sync", final_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .name()); |
| EXPECT_FALSE(final_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_2, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app2_sync", final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .name()); |
| EXPECT_FALSE(final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_4, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app4_sync", final_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .name()); |
| EXPECT_FALSE(final_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_6, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_6)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app6_registry", |
| final_registry.at(ManifestIdStrToAppId(manifest_id_6)) |
| ->sync_data() |
| .name()); |
| EXPECT_FALSE(final_registry.at(ManifestIdStrToAppId(manifest_id_6)) |
| ->is_locally_installed()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(4u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id_1, db_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app1_sync", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_1))->sync_data().name()); |
| EXPECT_FALSE(db_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_2, db_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app2_sync", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_2))->sync_data().name()); |
| EXPECT_FALSE(db_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_4, db_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app4_sync", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_4))->sync_data().name()); |
| EXPECT_FALSE(db_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->is_locally_installed()); |
| |
| EXPECT_EQ(manifest_id_6, db_registry.at(ManifestIdStrToAppId(manifest_id_6)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app6_registry", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_6))->sync_data().name()); |
| EXPECT_FALSE(db_registry.at(ManifestIdStrToAppId(manifest_id_6)) |
| ->is_locally_installed()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, MergeFullSyncData_NoChanges) { |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| std::unique_ptr<syncer::MetadataChangeList> metadata_change_list = |
| syncer::DataTypeStore::WriteBatch::CreateMetadataChangeList(); |
| syncer::EntityChangeList sync_changes; |
| std::optional<syncer::ModelError> result = sync_bridge().MergeFullSyncData( |
| std::move(metadata_change_list), std::move(sync_changes)); |
| |
| EXPECT_EQ(std::nullopt, result); |
| EXPECT_EQ(0u, sync_bridge().GetRegistryForTesting().size()); |
| EXPECT_EQ(0u, data_type_store_service().ReadRegistry().size()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, ApplyIncrementalSyncChanges) { |
| const std::string manifest_id_1 = "https://example.com/app1"; |
| const std::string manifest_id_2 = "https://example.com/app2"; |
| const std::string manifest_id_3 = "https://example.com/app3"; |
| const std::string manifest_id_4 = "https://example.com/app4"; |
| |
| Registry registry; |
| |
| // left as-is |
| std::unique_ptr<WebApkProto> registry_app1 = |
| CreateWebApkProto(manifest_id_1, "app1_registry"); |
| InsertAppIntoRegistry(®istry, std::move(registry_app1)); |
| |
| // deleted |
| std::unique_ptr<WebApkProto> registry_app2 = |
| CreateWebApkProto(manifest_id_2, "app2_registry"); |
| InsertAppIntoRegistry(®istry, std::move(registry_app2)); |
| |
| // replaced |
| std::unique_ptr<WebApkProto> registry_app3 = |
| CreateWebApkProto(manifest_id_3, "app3_registry"); |
| InsertAppIntoRegistry(®istry, std::move(registry_app3)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| std::unique_ptr<syncer::EntityChange> sync_change_2 = |
| syncer::EntityChange::CreateDelete(ManifestIdStrToAppId(manifest_id_2), |
| syncer::EntityData()); |
| |
| syncer::EntityData sync_data_3; |
| sync_pb::WebApkSpecifics* sync_specifics_3 = |
| sync_data_3.specifics.mutable_web_apk(); |
| sync_specifics_3->set_manifest_id(manifest_id_3); |
| sync_specifics_3->set_name("app3_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_3 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_3), |
| std::move(sync_data_3)); |
| |
| syncer::EntityData sync_data_4; |
| sync_pb::WebApkSpecifics* sync_specifics_4 = |
| sync_data_4.specifics.mutable_web_apk(); |
| sync_specifics_4->set_manifest_id(manifest_id_4); |
| sync_specifics_4->set_name("app4_sync"); |
| std::unique_ptr<syncer::EntityChange> sync_change_4 = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id_4), |
| std::move(sync_data_4)); |
| |
| syncer::EntityChangeList sync_changes; |
| sync_changes.push_back(std::move(sync_change_2)); |
| sync_changes.push_back(std::move(sync_change_3)); |
| sync_changes.push_back(std::move(sync_change_4)); |
| |
| std::unique_ptr<syncer::MetadataChangeList> metadata_change_list = |
| syncer::DataTypeStore::WriteBatch::CreateMetadataChangeList(); |
| std::optional<syncer::ModelError> result = |
| sync_bridge().ApplyIncrementalSyncChanges(std::move(metadata_change_list), |
| std::move(sync_changes)); |
| |
| EXPECT_EQ(std::nullopt, result); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(3u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id_1, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app1_registry", |
| final_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .name()); |
| |
| EXPECT_EQ(manifest_id_3, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_3)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app3_sync", final_registry.at(ManifestIdStrToAppId(manifest_id_3)) |
| ->sync_data() |
| .name()); |
| |
| EXPECT_EQ(manifest_id_4, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("app4_sync", final_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .name()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(3u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id_1, db_registry.at(ManifestIdStrToAppId(manifest_id_1)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app1_registry", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_1))->sync_data().name()); |
| |
| EXPECT_EQ(manifest_id_3, db_registry.at(ManifestIdStrToAppId(manifest_id_3)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app3_sync", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_3))->sync_data().name()); |
| |
| EXPECT_EQ(manifest_id_4, db_registry.at(ManifestIdStrToAppId(manifest_id_4)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "app4_sync", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_4))->sync_data().name()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, ApplyIncrementalSyncChanges_NoChanges) { |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| std::unique_ptr<syncer::MetadataChangeList> metadata_change_list = |
| syncer::DataTypeStore::WriteBatch::CreateMetadataChangeList(); |
| syncer::EntityChangeList sync_changes; |
| std::optional<syncer::ModelError> result = |
| sync_bridge().ApplyIncrementalSyncChanges(std::move(metadata_change_list), |
| std::move(sync_changes)); |
| |
| EXPECT_EQ(std::nullopt, result); |
| EXPECT_EQ(0u, sync_bridge().GetRegistryForTesting().size()); |
| EXPECT_EQ(0u, data_type_store_service().ReadRegistry().size()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, OnWebApkUsed_ReplaceExistingSyncEntry) { |
| const std::string manifest_id = "https://example.com/app1"; |
| |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> registry_app = |
| CreateWebApkProto(manifest_id, "registry_app"); |
| registry_app->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec(1136145845.0)); // Sun Jan 01 2006 15:04:05 |
| // GMT-0500 timestamp |
| InsertAppIntoRegistry(®istry, std::move(registry_app)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| syncer::EntityData sync_data; |
| sync_pb::WebApkSpecifics* sync_specifics = |
| sync_data.specifics.mutable_web_apk(); |
| sync_specifics->set_manifest_id(manifest_id); |
| sync_specifics->set_name("sync_app1"); |
| sync_specifics->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1133726645.0)); // Sun Dec 04 2005 15:04:05 GMT-0500 - 29 days before |
| // clock_.Now() |
| std::unique_ptr<syncer::EntityChange> sync_change = |
| syncer::EntityChange::CreateAdd(ManifestIdStrToAppId(manifest_id), |
| std::move(sync_data)); |
| |
| syncer::EntityChangeList sync_changes; |
| sync_changes.push_back(std::move(sync_change)); |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> used_specifics = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| used_specifics->set_manifest_id(manifest_id); |
| used_specifics->set_name("used_app1"); |
| used_specifics->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136232245.0)); // Mon Jan 02 2006 15:04:05 GMT-0500 (now) |
| |
| base::RunLoop run_loop; |
| |
| ON_CALL(processor(), Put) |
| .WillByDefault([&](const std::string& storage_key, |
| std::unique_ptr<syncer::EntityData> entity_data, |
| syncer::MetadataChangeList* metadata) { |
| EXPECT_EQ(ManifestIdStrToAppId(manifest_id), storage_key); |
| EXPECT_EQ(manifest_id, entity_data->specifics.web_apk().manifest_id()); |
| EXPECT_EQ("used_app1", entity_data->specifics.web_apk().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| entity_data->specifics.web_apk() |
| .last_used_time_windows_epoch_micros()); |
| |
| run_loop.Quit(); |
| }); |
| |
| sync_bridge().OnWebApkUsed(std::move(used_specifics), false /* is_install */); |
| |
| run_loop.Run(); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(1u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id, final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "used_app1", |
| final_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(1u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id, db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "used_app1", |
| db_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, OnWebApkUsed_CreateNewSyncEntry) { |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| const std::string manifest_id = "https://example.com/app2"; |
| |
| std::unique_ptr<sync_pb::WebApkSpecifics> used_specifics = |
| std::make_unique<sync_pb::WebApkSpecifics>(); |
| used_specifics->set_manifest_id(manifest_id); |
| used_specifics->set_name("used_app2"); |
| used_specifics->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136232245.0)); // Mon Jan 02 2006 15:04:05 GMT-0500 (now) |
| |
| base::RunLoop run_loop; |
| |
| ON_CALL(processor(), Put) |
| .WillByDefault([&](const std::string& storage_key, |
| std::unique_ptr<syncer::EntityData> entity_data, |
| syncer::MetadataChangeList* metadata) { |
| EXPECT_EQ(ManifestIdStrToAppId(manifest_id), storage_key); |
| EXPECT_EQ(manifest_id, entity_data->specifics.web_apk().manifest_id()); |
| EXPECT_EQ("used_app2", entity_data->specifics.web_apk().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| entity_data->specifics.web_apk() |
| .last_used_time_windows_epoch_micros()); |
| |
| run_loop.Quit(); |
| }); |
| |
| sync_bridge().OnWebApkUsed(std::move(used_specifics), false /* is_install */); |
| |
| run_loop.Run(); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(1u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id, final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "used_app2", |
| final_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(1u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id, db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "used_app2", |
| db_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136232245.0), |
| db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, |
| OnWebApkUninstalled_DoesNotCrashOnEmptyStringOrInvalidManifestId) { |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| sync_bridge().OnWebApkUninstalled(""); |
| sync_bridge().OnWebApkUninstalled("%%"); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, OnWebApkUninstalled_AppTooOld) { |
| const std::string manifest_id = "https://example.com/app1"; |
| |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> registry_app = |
| CreateWebApkProto(manifest_id, "registry_app"); |
| registry_app->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1133640244.0)); // Sat Dec 03 2005 15:04:04 GMT-0500 - 30 days and 1 |
| // second before clock_.Now() (not recent enough) |
| InsertAppIntoRegistry(®istry, std::move(registry_app)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| |
| base::RunLoop run_loop; |
| |
| ON_CALL(processor(), Delete) |
| .WillByDefault([&](const std::string& storage_key, |
| const syncer::DeletionOrigin& origin, |
| syncer::MetadataChangeList* metadata_change_list) { |
| EXPECT_EQ(ManifestIdStrToAppId(manifest_id), storage_key); |
| run_loop.Quit(); |
| }); |
| |
| InitSyncBridge(); |
| |
| sync_bridge().OnWebApkUninstalled(manifest_id); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(0u, final_registry.size()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(0u, db_registry.size()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, OnWebApkUninstalled_AppDoesNotExist) { |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| sync_bridge().OnWebApkUninstalled("https://example.com/app"); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(0u, final_registry.size()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(0u, db_registry.size()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, OnWebApkUninstalled_AppNewEnough) { |
| const std::string manifest_id = "https://example.com/app1"; |
| |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> registry_app = |
| CreateWebApkProto(manifest_id, "registry_app"); |
| registry_app->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 |
| // GMT-0500 timestamp (new enough) |
| InsertAppIntoRegistry(®istry, std::move(registry_app)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| sync_bridge().OnWebApkUninstalled(manifest_id); |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(1u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id, final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "registry_app", |
| final_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136145845.0), // uninstalling did not |
| // update the timestamp |
| final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_FALSE(final_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(1u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id, db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "registry_app", |
| db_registry.at(ManifestIdStrToAppId(manifest_id))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136145845.0), // uninstalling did not |
| // update the timestamp |
| db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_FALSE(db_registry.at(ManifestIdStrToAppId(manifest_id)) |
| ->is_locally_installed()); |
| } |
| |
| // Tests that the WebApkSyncBridge correctly reports data from the |
| // WebApkDatabase. |
| TEST_F(WebApkSyncBridgeTest, GetData) { |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> synced_app1 = |
| CreateWebApkProto("https://example.com/app1/", "name1"); |
| InsertAppIntoRegistry(®istry, std::move(synced_app1)); |
| |
| std::unique_ptr<WebApkProto> synced_app2 = |
| CreateWebApkProto("https://example.com/app2/", "name2"); |
| InsertAppIntoRegistry(®istry, std::move(synced_app2)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| InitSyncBridge(); |
| |
| { |
| WebApkSyncBridge::StorageKeyList storage_keys; |
| // Add an unknown key to test this is handled gracefully. |
| storage_keys.push_back("unknown"); |
| for (const Registry::value_type& id_and_web_app : registry) { |
| storage_keys.push_back(id_and_web_app.first); |
| } |
| |
| EXPECT_TRUE(RegistryContainsSyncDataBatchChanges( |
| registry, sync_bridge().GetDataForCommit(std::move(storage_keys)))); |
| } |
| |
| EXPECT_TRUE(RegistryContainsSyncDataBatchChanges( |
| registry, sync_bridge().GetAllDataForDebugging())); |
| } |
| |
| // Tests that the client & storage tags are correct for entity data. |
| TEST_F(WebApkSyncBridgeTest, Identities) { |
| // Should be kept up to date with |
| // chrome/browser/web_applications/web_app_sync_bridge_unittest.cc's |
| // WebAppSyncBridgeTest.Identities test. |
| InitSyncBridge(); |
| |
| std::unique_ptr<WebApkProto> app = |
| CreateWebApkProto("https://example.com/", "name"); |
| std::unique_ptr<syncer::EntityData> entity_data = CreateSyncEntityData(*app); |
| |
| EXPECT_EQ("ocjeedicdelkkoefdcgeopgiagdjbcng", |
| sync_bridge().GetClientTag(*entity_data)); |
| EXPECT_EQ("ocjeedicdelkkoefdcgeopgiagdjbcng", |
| sync_bridge().GetStorageKey(*entity_data)); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, ApplyDisableSyncChanges) { |
| Registry registry; |
| |
| InsertAppIntoRegistry( |
| ®istry, CreateWebApkProto("https://example.com/app1", "registry_app")); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| ASSERT_THAT(sync_bridge().GetRegistryForTesting(), SizeIs(1)); |
| ASSERT_THAT(data_type_store_service().ReadRegistry(), SizeIs(1)); |
| |
| sync_bridge().ApplyDisableSyncChanges( |
| sync_bridge().CreateMetadataChangeList()); |
| |
| EXPECT_THAT(sync_bridge().GetRegistryForTesting(), IsEmpty()); |
| EXPECT_THAT(data_type_store_service().ReadRegistry(), IsEmpty()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, RemoveOldWebAPKsFromSync) { |
| const std::string manifest_id_1 = "https://example.com/app1"; |
| const std::string manifest_id_2 = "https://example.com/app2"; |
| |
| Registry registry; |
| |
| std::unique_ptr<WebApkProto> registry_app_1 = |
| CreateWebApkProto(manifest_id_1, "registry_app_1"); |
| registry_app_1->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec(1136145845.0)); // Sun Jan 01 2006 15:04:05 |
| // GMT-0500 timestamp (too old - |
| // will be deleted from sync) |
| registry_app_1->set_is_locally_installed(true); |
| |
| std::unique_ptr<WebApkProto> registry_app_2 = |
| CreateWebApkProto(manifest_id_2, "registry_app_2"); |
| registry_app_2->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec(1136946167.0)); // Tue Jan 10 2006 21:22:47 |
| // GMT-0500 timestamp (new |
| // enough - will not be deleted) |
| registry_app_2->set_is_locally_installed(true); |
| |
| InsertAppIntoRegistry(®istry, std::move(registry_app_1)); |
| InsertAppIntoRegistry(®istry, std::move(registry_app_2)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| |
| base::RunLoop run_loop; |
| |
| ON_CALL(processor(), Delete) |
| .WillByDefault([&](const std::string& storage_key, |
| const syncer::DeletionOrigin& origin, |
| syncer::MetadataChangeList* metadata_change_list) { |
| EXPECT_EQ(ManifestIdStrToAppId(manifest_id_1), storage_key); |
| run_loop.Quit(); |
| }); |
| |
| InitSyncBridge(); |
| |
| sync_bridge().RemoveOldWebAPKsFromSync( |
| 1138933367000); // Thu Feb 02 2006 21:22:47 GMT-0500 timestamp |
| |
| const Registry& final_registry = sync_bridge().GetRegistryForTesting(); |
| EXPECT_EQ(1u, final_registry.size()); |
| |
| EXPECT_EQ(manifest_id_2, |
| final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ("registry_app_2", |
| final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136946167.0), |
| final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(final_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->is_locally_installed()); |
| |
| const Registry db_registry = data_type_store_service().ReadRegistry(); |
| EXPECT_EQ(1u, db_registry.size()); |
| |
| EXPECT_EQ(manifest_id_2, db_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .manifest_id()); |
| EXPECT_EQ( |
| "registry_app_2", |
| db_registry.at(ManifestIdStrToAppId(manifest_id_2))->sync_data().name()); |
| EXPECT_EQ(UnixTsSecToWindowsTsMsec(1136946167.0), |
| db_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->sync_data() |
| .last_used_time_windows_epoch_micros()); |
| EXPECT_TRUE(db_registry.at(ManifestIdStrToAppId(manifest_id_2)) |
| ->is_locally_installed()); |
| } |
| |
| TEST_F(WebApkSyncBridgeTest, GetRestorableAppsInfo) { |
| InitSyncBridge(); |
| |
| const GURL manifest_id_1("https://example.com/app1"); |
| const GURL manifest_id_2("https://example.com/app2"); |
| const GURL manifest_id_3("https://example.com/app3"); |
| const GURL manifest_id_4("https://example.com/app4"); |
| const GURL icon_url("https://example.com/app2/icon"); |
| |
| std::unique_ptr<WebApkProto> registry_app_1 = |
| CreateWebApkProto(manifest_id_1.spec(), "registry_app_1"); |
| registry_app_1->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 GMT-0500 - slightly |
| // before clock_.Now() (recent enough) |
| registry_app_1->set_is_locally_installed(true); |
| |
| std::unique_ptr<WebApkProto> registry_app_2 = |
| CreateWebApkProto(manifest_id_2.spec(), "registry_app_2"); |
| auto* icon = registry_app_2->mutable_sync_data()->add_icon_infos(); |
| icon->set_url(icon_url.spec()); |
| registry_app_2->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1136145845.0)); // Sun Jan 01 2006 15:04:05 GMT-0500 - slightly |
| // before clock_.Now() (recent enough) |
| registry_app_2->set_is_locally_installed(false); |
| |
| std::unique_ptr<WebApkProto> registry_app_3 = |
| CreateWebApkProto(manifest_id_3.spec(), "registry_app_3"); |
| registry_app_3->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1133726645.0)); // Sun Dec 04 2005 15:04:05 GMT-0500 - 29 days before |
| // clock_.Now() (recent enough) |
| registry_app_3->set_is_locally_installed(false); |
| |
| std::unique_ptr<WebApkProto> registry_app_4 = |
| CreateWebApkProto(manifest_id_4.spec(), "registry_app_4"); |
| registry_app_4->mutable_sync_data()->set_last_used_time_windows_epoch_micros( |
| UnixTsSecToWindowsTsMsec( |
| 1133640244.0)); // Sat Dec 03 2005 15:04:04 GMT-0500 - 30 days and 1 |
| // second before clock_.Now() (not recent enough) |
| registry_app_4->set_is_locally_installed(false); |
| |
| Registry registry; |
| |
| InsertAppIntoRegistry(®istry, std::move(registry_app_1)); |
| InsertAppIntoRegistry(®istry, std::move(registry_app_2)); |
| InsertAppIntoRegistry(®istry, std::move(registry_app_3)); |
| InsertAppIntoRegistry(®istry, std::move(registry_app_4)); |
| |
| data_type_store_service().WriteRegistry(registry); |
| |
| EXPECT_CALL(processor(), ModelReadyToSync).Times(1); |
| EXPECT_CALL(processor(), Put).Times(0); |
| EXPECT_CALL(processor(), Delete).Times(0); |
| |
| InitSyncBridge(); |
| |
| auto result = sync_bridge().GetRestorableAppsShortcutInfo(); |
| EXPECT_EQ(result.size(), 2u); |
| |
| EXPECT_EQ(result[0].app_id, ManifestIdStrToAppId(manifest_id_2.spec())); |
| EXPECT_EQ(result[0].shortcut_info->manifest_id, manifest_id_2); |
| EXPECT_EQ(result[0].shortcut_info->name, u"registry_app_2"); |
| EXPECT_EQ(result[0].shortcut_info->best_primary_icon_url, icon_url); |
| |
| EXPECT_EQ(result[1].app_id, ManifestIdStrToAppId(manifest_id_3.spec())); |
| EXPECT_EQ(result[1].shortcut_info->manifest_id, manifest_id_3); |
| EXPECT_EQ(result[1].shortcut_info->name, u"registry_app_3"); |
| // No icon url specified for apps 3, fallback to use start url as place |
| // holder. |
| EXPECT_EQ(result[1].shortcut_info->best_primary_icon_url, manifest_id_3); |
| } |
| |
| } // namespace webapk |