| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync/driver/glue/sync_engine_impl.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/invalidation/impl/invalidation_logger.h" |
| #include "components/invalidation/public/invalidation_service.h" |
| #include "components/invalidation/public/invalidation_util.h" |
| #include "components/invalidation/public/invalidator_state.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/sync/base/features.h" |
| #include "components/sync/base/invalidation_helper.h" |
| #include "components/sync/base/model_type.h" |
| #include "components/sync/driver/active_devices_provider.h" |
| #include "components/sync/driver/glue/sync_transport_data_prefs.h" |
| #include "components/sync/engine/net/http_bridge.h" |
| #include "components/sync/engine/sync_engine_host.h" |
| #include "components/sync/engine/sync_manager_factory.h" |
| #include "components/sync/protocol/sync_invalidations_payload.pb.h" |
| #include "components/sync/test/fake_sync_manager.h" |
| #include "components/sync/test/mock_sync_invalidations_service.h" |
| #include "services/network/test/test_network_connection_tracker.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| using testing::ByMove; |
| using testing::NiceMock; |
| using testing::Return; |
| |
| namespace syncer { |
| |
| namespace { |
| |
| static const base::FilePath::CharType kTestSyncDir[] = |
| FILE_PATH_LITERAL("sync-test"); |
| constexpr char kTestGaiaId[] = "test_gaia_id"; |
| |
| class MockSyncEngineHost : public SyncEngineHost { |
| public: |
| MOCK_METHOD(void, |
| OnEngineInitialized, |
| (bool success, bool is_first_time_sync_configure), |
| (override)); |
| MOCK_METHOD(void, |
| OnSyncCycleCompleted, |
| (const SyncCycleSnapshot& snapshot), |
| (override)); |
| MOCK_METHOD(void, OnProtocolEvent, (const ProtocolEvent& event), (override)); |
| MOCK_METHOD(void, |
| OnConnectionStatusChange, |
| (ConnectionStatus status), |
| (override)); |
| MOCK_METHOD(void, |
| OnMigrationNeededForTypes, |
| (ModelTypeSet types), |
| (override)); |
| MOCK_METHOD(void, |
| OnActionableProtocolError, |
| (const SyncProtocolError& error), |
| (override)); |
| MOCK_METHOD(void, OnBackedOffTypesChanged, (), (override)); |
| MOCK_METHOD(void, OnInvalidationStatusChanged, (), (override)); |
| }; |
| |
| class FakeSyncManagerFactory : public SyncManagerFactory { |
| public: |
| explicit FakeSyncManagerFactory( |
| FakeSyncManager** fake_manager, |
| network::NetworkConnectionTracker* network_connection_tracker) |
| : SyncManagerFactory(network_connection_tracker), |
| fake_manager_(fake_manager) { |
| *fake_manager_ = nullptr; |
| } |
| ~FakeSyncManagerFactory() override = default; |
| |
| // SyncManagerFactory implementation. Called on the sync thread. |
| std::unique_ptr<SyncManager> CreateSyncManager( |
| const std::string& /* name */) override { |
| *fake_manager_ = |
| new FakeSyncManager(initial_sync_ended_types_, progress_marker_types_, |
| configure_fail_types_); |
| return std::unique_ptr<SyncManager>(*fake_manager_); |
| } |
| |
| void set_initial_sync_ended_types(ModelTypeSet types) { |
| initial_sync_ended_types_ = types; |
| } |
| |
| void set_progress_marker_types(ModelTypeSet types) { |
| progress_marker_types_ = types; |
| } |
| |
| void set_configure_fail_types(ModelTypeSet types) { |
| configure_fail_types_ = types; |
| } |
| |
| private: |
| ModelTypeSet initial_sync_ended_types_; |
| ModelTypeSet progress_marker_types_; |
| ModelTypeSet configure_fail_types_; |
| raw_ptr<FakeSyncManager*> fake_manager_; |
| }; |
| |
| class MockInvalidationService : public invalidation::InvalidationService { |
| public: |
| MockInvalidationService() = default; |
| ~MockInvalidationService() override = default; |
| MOCK_METHOD(void, |
| RegisterInvalidationHandler, |
| (invalidation::InvalidationHandler * handler), |
| (override)); |
| MOCK_METHOD(bool, |
| UpdateInterestedTopics, |
| (invalidation::InvalidationHandler * handler, |
| const invalidation::TopicSet& topics), |
| (override)); |
| MOCK_METHOD(void, |
| UnsubscribeFromUnregisteredTopics, |
| (invalidation::InvalidationHandler * handler), |
| (override)); |
| MOCK_METHOD(void, |
| UnregisterInvalidationHandler, |
| (invalidation::InvalidationHandler * handler), |
| (override)); |
| MOCK_METHOD(invalidation::InvalidatorState, |
| GetInvalidatorState, |
| (), |
| (const override)); |
| MOCK_METHOD(std::string, GetInvalidatorClientId, (), (const override)); |
| MOCK_METHOD(invalidation::InvalidationLogger*, |
| GetInvalidationLogger, |
| (), |
| (override)); |
| MOCK_METHOD(void, |
| RequestDetailedStatus, |
| (base::RepeatingCallback<void(base::Value::Dict)> post_caller), |
| (const override)); |
| }; |
| |
| class MockActiveDevicesProvider : public ActiveDevicesProvider { |
| public: |
| MockActiveDevicesProvider() = default; |
| ~MockActiveDevicesProvider() override = default; |
| |
| MOCK_METHOD(void, |
| SetActiveDevicesChangedCallback, |
| (ActiveDevicesProvider::ActiveDevicesChangedCallback), |
| (override)); |
| MOCK_METHOD(ActiveDevicesInvalidationInfo, |
| CalculateInvalidationInfo, |
| (const std::string&), |
| (const override)); |
| }; |
| |
| std::unique_ptr<HttpPostProviderFactory> CreateHttpBridgeFactory() { |
| return std::make_unique<HttpBridgeFactory>( |
| /*user_agent=*/"", |
| /*pending_url_loader_factory=*/nullptr); |
| } |
| |
| class SyncEngineImplTest : public testing::Test { |
| protected: |
| SyncEngineImplTest() = default; |
| ~SyncEngineImplTest() override = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| SyncTransportDataPrefs::RegisterProfilePrefs(pref_service_.registry()); |
| |
| ON_CALL(invalidator_, UpdateInterestedTopics) |
| .WillByDefault(testing::Return(true)); |
| scoped_refptr<base::SequencedTaskRunner> sync_task_runner = |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
| base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); |
| auto mock_active_devices_provider = |
| std::make_unique<NiceMock<MockActiveDevicesProvider>>(); |
| ON_CALL(*mock_active_devices_provider.get(), CalculateInvalidationInfo) |
| .WillByDefault(Return( |
| ByMove(ActiveDevicesInvalidationInfo::CreateUninitialized()))); |
| backend_ = std::make_unique<SyncEngineImpl>( |
| "dummyDebugName", &invalidator_, &mock_sync_invalidations_service_, |
| std::move(mock_active_devices_provider), |
| std::make_unique<SyncTransportDataPrefs>(&pref_service_), |
| temp_dir_.GetPath().Append(base::FilePath(kTestSyncDir)), |
| std::move(sync_task_runner), sync_transport_data_cleared_cb_.Get()); |
| |
| fake_manager_factory_ = std::make_unique<FakeSyncManagerFactory>( |
| &fake_manager_, network::TestNetworkConnectionTracker::GetInstance()); |
| |
| // These types are always implicitly enabled. |
| enabled_types_.PutAll(ControlTypes()); |
| |
| // NOTE: We can't include Passwords or Typed URLs due to the Sync Backend |
| // Registrar removing them if it can't find their model workers. |
| enabled_types_.Put(BOOKMARKS); |
| enabled_types_.Put(PREFERENCES); |
| enabled_types_.Put(SESSIONS); |
| enabled_types_.Put(SEARCH_ENGINES); |
| enabled_types_.Put(AUTOFILL); |
| } |
| |
| void TearDown() override { |
| if (backend_) { |
| ShutdownBackend(ShutdownReason::BROWSER_SHUTDOWN_AND_KEEP_DATA); |
| } |
| // Pump messages posted by the sync thread. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Synchronously initializes the backend. |
| void InitializeBackend(bool expect_success = true, |
| const std::string& gaia_id = kTestGaiaId) { |
| SyncEngine::InitParams params; |
| params.host = &mock_host_; |
| params.http_factory_getter = base::BindOnce(&CreateHttpBridgeFactory); |
| params.authenticated_account_info.gaia = gaia_id; |
| params.authenticated_account_info.account_id = |
| CoreAccountId::FromGaiaId("gaia_id"); |
| params.sync_manager_factory = std::move(fake_manager_factory_); |
| |
| EXPECT_CALL(mock_host_, OnEngineInitialized(expect_success, _)) |
| .WillOnce( |
| testing::InvokeWithoutArgs(this, &SyncEngineImplTest::QuitRunLoop)); |
| backend_->Initialize(std::move(params)); |
| PumpSyncThread(); |
| // |fake_manager_| is set on the sync thread, but we can rely on the message |
| // loop barriers to guarantee that we see the updated value. |
| DCHECK(fake_manager_); |
| |
| if (expect_success) { |
| EXPECT_TRUE(engine_types_.Empty()); |
| engine_types_ = fake_manager_->GetConnectedTypes(); |
| } |
| } |
| |
| void ShutdownBackend(ShutdownReason reason) { |
| DCHECK(backend_); |
| backend_->StopSyncingForShutdown(); |
| backend_->Shutdown(reason); |
| backend_.reset(); |
| } |
| |
| // Synchronously configures the backend's datatypes. |
| ModelTypeSet ConfigureDataTypes() { |
| return ConfigureDataTypesWithUnready(ModelTypeSet()); |
| } |
| |
| ModelTypeSet ConfigureDataTypesWithUnready(ModelTypeSet unready_types) { |
| ModelTypeConfigurer::ConfigureParams params; |
| params.reason = CONFIGURE_REASON_RECONFIGURATION; |
| ModelTypeSet enabled_types = Difference(enabled_types_, unready_types); |
| params.to_download = Difference(enabled_types, engine_types_); |
| if (!params.to_download.Empty()) { |
| params.to_download.Put(NIGORI); |
| } |
| params.to_purge = Difference(engine_types_, enabled_types_); |
| params.ready_task = base::BindOnce(&SyncEngineImplTest::DownloadReady, |
| base::Unretained(this)); |
| |
| ModelTypeSet ready_types = Difference(enabled_types, params.to_download); |
| backend_->ConfigureDataTypes(std::move(params)); |
| PumpSyncThread(); |
| |
| return ready_types; |
| } |
| |
| protected: |
| void DownloadReady(ModelTypeSet succeeded_types, ModelTypeSet failed_types) { |
| engine_types_.PutAll(succeeded_types); |
| |
| backend_->StartSyncingWithServer(); |
| QuitRunLoop(); |
| } |
| |
| void QuitRunLoop() { std::move(quit_loop_).Run(); } |
| |
| void PumpSyncThread() { |
| base::RunLoop run_loop; |
| quit_loop_ = run_loop.QuitClosure(); |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&SyncEngineImplTest::QuitRunLoop, |
| weak_ptr_factory_.GetWeakPtr()), |
| TestTimeouts::action_timeout()); |
| run_loop.Run(); |
| } |
| |
| base::test::TaskEnvironment task_environment_; |
| base::ScopedTempDir temp_dir_; |
| TestingPrefServiceSimple pref_service_; |
| NiceMock<MockSyncEngineHost> mock_host_; |
| NiceMock<base::MockCallback<base::RepeatingClosure>> |
| sync_transport_data_cleared_cb_; |
| std::unique_ptr<SyncEngineImpl> backend_; |
| std::unique_ptr<FakeSyncManagerFactory> fake_manager_factory_; |
| // This field is not a raw_ptr<> because it was filtered by the rewriter for: |
| // #addr-of |
| RAW_PTR_EXCLUSION FakeSyncManager* fake_manager_ = nullptr; |
| ModelTypeSet engine_types_; |
| ModelTypeSet enabled_types_; |
| base::OnceClosure quit_loop_; |
| NiceMock<MockInvalidationService> invalidator_; |
| NiceMock<MockSyncInvalidationsService> mock_sync_invalidations_service_; |
| |
| base::WeakPtrFactory<SyncEngineImplTest> weak_ptr_factory_{this}; |
| }; |
| |
| // TODO(crbug.com/1404927): remove the test once feature toogles are cleaned up. |
| class SyncEngineImplWithSyncInvalidationsTest : public SyncEngineImplTest { |
| public: |
| SyncEngineImplWithSyncInvalidationsTest() { |
| override_features_.InitWithFeatures( |
| /*enabled_features=*/{kUseSyncInvalidations}, |
| /*disabled_features=*/{kUseSyncInvalidationsForWalletAndOffer}); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList override_features_; |
| }; |
| |
| class SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest |
| : public SyncEngineImplTest { |
| public: |
| SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest() { |
| override_features_.InitWithFeatures( |
| /*enabled_features=*/{kUseSyncInvalidations, |
| kUseSyncInvalidationsForWalletAndOffer}, |
| /*disabled_features=*/{}); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList override_features_; |
| }; |
| |
| // Test basic initialization with no initial types (first time initialization). |
| // Only the nigori should be configured. |
| TEST_F(SyncEngineImplTest, InitShutdownWithStopSync) { |
| InitializeBackend(); |
| EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes()); |
| |
| EXPECT_CALL(sync_transport_data_cleared_cb_, Run()).Times(0); |
| ShutdownBackend(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| } |
| |
| TEST_F(SyncEngineImplTest, InitShutdownWithDisableSync) { |
| InitializeBackend(); |
| EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes()); |
| |
| EXPECT_CALL(sync_transport_data_cleared_cb_, Run()); |
| ShutdownBackend(ShutdownReason::DISABLE_SYNC_AND_CLEAR_DATA); |
| } |
| |
| // Test first time sync scenario. All types should be properly configured. |
| |
| TEST_F(SyncEngineImplTest, FirstTimeSync) { |
| InitializeBackend(); |
| EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes()); |
| |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| // Nigori is always downloaded so won't be ready. |
| EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types); |
| EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().HasAll( |
| Difference(enabled_types_, ControlTypes()))); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| } |
| |
| // Test the restart after setting up sync scenario. No enabled types should be |
| // downloaded. |
| TEST_F(SyncEngineImplTest, Restart) { |
| fake_manager_factory_->set_progress_marker_types(enabled_types_); |
| fake_manager_factory_->set_initial_sync_ended_types(enabled_types_); |
| InitializeBackend(); |
| EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| EXPECT_EQ(enabled_types_, ready_types); |
| EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| } |
| |
| TEST_F(SyncEngineImplTest, DisableTypes) { |
| // Simulate first time sync. |
| InitializeBackend(); |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| // Nigori is always downloaded so won't be ready. |
| EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types); |
| EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| |
| // Then disable two datatypes. |
| ModelTypeSet disabled_types(BOOKMARKS, SEARCH_ENGINES); |
| enabled_types_.RemoveAll(disabled_types); |
| ready_types = ConfigureDataTypes(); |
| |
| // Only those datatypes disabled should be cleaned. Nothing should be |
| // downloaded. |
| EXPECT_EQ(enabled_types_, ready_types); |
| EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty()); |
| } |
| |
| TEST_F(SyncEngineImplTest, AddTypes) { |
| // Simulate first time sync. |
| InitializeBackend(); |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| // Nigori is always downloaded so won't be ready. |
| EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types); |
| EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| |
| // Then add two datatypes. |
| ModelTypeSet new_types(EXTENSIONS, APPS); |
| enabled_types_.PutAll(new_types); |
| ready_types = ConfigureDataTypes(); |
| |
| // Only those datatypes added should be downloaded (plus nigori). Nothing |
| // should be cleaned aside from the disabled types. |
| new_types.Put(NIGORI); |
| EXPECT_EQ(Difference(enabled_types_, new_types), ready_types); |
| EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| } |
| |
| // And and disable in the same configuration. |
| TEST_F(SyncEngineImplTest, AddDisableTypes) { |
| // Simulate first time sync. |
| InitializeBackend(); |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| // Nigori is always downloaded so won't be ready. |
| EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types); |
| EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| |
| // Then add two datatypes. |
| ModelTypeSet disabled_types(BOOKMARKS, SEARCH_ENGINES); |
| ModelTypeSet new_types(EXTENSIONS, APPS); |
| enabled_types_.PutAll(new_types); |
| enabled_types_.RemoveAll(disabled_types); |
| ready_types = ConfigureDataTypes(); |
| |
| // Only those datatypes added should be downloaded (plus nigori). Nothing |
| // should be cleaned aside from the disabled types. |
| new_types.Put(NIGORI); |
| EXPECT_EQ(Difference(enabled_types_, new_types), ready_types); |
| EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes()); |
| } |
| |
| // Test restarting the browser to newly supported datatypes. The new datatypes |
| // should be downloaded on the configuration after backend initialization. |
| TEST_F(SyncEngineImplTest, NewlySupportedTypes) { |
| // Set sync manager behavior before passing it down. All types have progress |
| // markers and initial sync ended except the new types. |
| ModelTypeSet old_types = enabled_types_; |
| fake_manager_factory_->set_progress_marker_types(old_types); |
| fake_manager_factory_->set_initial_sync_ended_types(old_types); |
| ModelTypeSet new_types(APP_SETTINGS, EXTENSION_SETTINGS); |
| enabled_types_.PutAll(new_types); |
| |
| // Does nothing. |
| InitializeBackend(); |
| EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty()); |
| EXPECT_EQ(old_types, fake_manager_->InitialSyncEndedTypes()); |
| |
| // Downloads and applies the new types (plus nigori). |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| |
| new_types.Put(NIGORI); |
| EXPECT_EQ(Difference(old_types, ModelTypeSet(NIGORI)), ready_types); |
| EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| } |
| |
| // Verify that downloading control types only downloads those types that do |
| // not have initial sync ended set. |
| TEST_F(SyncEngineImplTest, DownloadControlTypes) { |
| // Set sync manager behavior before passing it down. Experiments and device |
| // info are new types without progress markers or initial sync ended, while |
| // all other types have been fully downloaded and applied. |
| ModelTypeSet new_types(NIGORI); |
| ModelTypeSet old_types = Difference(enabled_types_, new_types); |
| fake_manager_factory_->set_progress_marker_types(old_types); |
| fake_manager_factory_->set_initial_sync_ended_types(old_types); |
| |
| // Bringing up the backend should download the new types without downloading |
| // any old types. |
| InitializeBackend(); |
| EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes()); |
| EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes()); |
| } |
| |
| // Fail to download control types. It's believed that there is a server bug |
| // which can allow this to happen (crbug.com/164288). The sync engine should |
| // detect this condition and fail to initialize the backend. |
| // |
| // The failure is "silent" in the sense that the GetUpdates request appears to |
| // be successful, but it returned no results. This means that the usual |
| // download retry logic will not be invoked. |
| TEST_F(SyncEngineImplTest, SilentlyFailToDownloadControlTypes) { |
| fake_manager_factory_->set_configure_fail_types(ModelTypeSet::All()); |
| InitializeBackend(/*expect_success=*/false); |
| } |
| |
| // Test that local refresh requests are delivered to sync. |
| TEST_F(SyncEngineImplTest, ForwardLocalRefreshRequest) { |
| InitializeBackend(); |
| |
| ModelTypeSet set1 = ModelTypeSet::All(); |
| backend_->TriggerRefresh(set1); |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_EQ(set1, fake_manager_->GetLastRefreshRequestTypes()); |
| |
| ModelTypeSet set2 = ModelTypeSet(SESSIONS); |
| backend_->TriggerRefresh(set2); |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_EQ(set2, fake_manager_->GetLastRefreshRequestTypes()); |
| } |
| |
| // Test that configuration on signin sends the proper GU source. |
| TEST_F(SyncEngineImplTest, DownloadControlTypesNewClient) { |
| InitializeBackend(); |
| EXPECT_EQ(CONFIGURE_REASON_NEW_CLIENT, |
| fake_manager_->GetAndResetConfigureReason()); |
| } |
| |
| // Test that configuration on restart sends the proper GU source. |
| TEST_F(SyncEngineImplTest, DownloadControlTypesRestart) { |
| fake_manager_factory_->set_progress_marker_types(enabled_types_); |
| fake_manager_factory_->set_initial_sync_ended_types(enabled_types_); |
| InitializeBackend(); |
| EXPECT_EQ(CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, |
| fake_manager_->GetAndResetConfigureReason()); |
| } |
| |
| // If bookmarks encounter an error that results in disabling without purging |
| // (such as when the type is unready), and then is explicitly disabled, the |
| // SyncEngine needs to tell the manager to purge the type, even though |
| // it's already disabled (crbug.com/386778). |
| TEST_F(SyncEngineImplTest, DisableThenPurgeType) { |
| ModelTypeSet error_types(BOOKMARKS); |
| |
| InitializeBackend(); |
| |
| // First enable the types. |
| ModelTypeSet ready_types = ConfigureDataTypes(); |
| |
| // Nigori is always downloaded so won't be ready. |
| EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types); |
| |
| // Then mark the error types as unready (disables without purging). |
| ready_types = ConfigureDataTypesWithUnready(error_types); |
| EXPECT_EQ(Difference(enabled_types_, error_types), ready_types); |
| |
| // Lastly explicitly disable the error types, which should result in a purge. |
| enabled_types_.RemoveAll(error_types); |
| ready_types = ConfigureDataTypes(); |
| EXPECT_EQ(Difference(enabled_types_, error_types), ready_types); |
| } |
| |
| // Tests that SyncEngineImpl retains ModelTypeConnector after call to |
| // StopSyncingForShutdown. This is needed for datatype deactivation during |
| // DataTypeManager shutdown. |
| TEST_F(SyncEngineImplTest, ModelTypeConnectorValidDuringShutdown) { |
| InitializeBackend(); |
| backend_->StopSyncingForShutdown(); |
| // Verify that call to DisconnectDataType doesn't assert. |
| backend_->DisconnectDataType(AUTOFILL); |
| backend_->Shutdown(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| backend_.reset(); |
| } |
| |
| // TODO(crbug.com/1404927): remove the test once old invalidations are not used |
| // in sync anymore. |
| TEST_F(SyncEngineImplTest, |
| NoisyDataTypesInvalidationAreDiscardedByDefaultOnAndroid) { |
| base::test::ScopedFeatureList feature_overrides; |
| feature_overrides.InitAndDisableFeature(syncer::kUseSyncInvalidations); |
| |
| // Making sure that the noisy types we're interested in are in the |
| // |enabled_types_|. |
| enabled_types_.Put(SESSIONS); |
| |
| ModelTypeSet invalidation_enabled_types( |
| Difference(enabled_types_, CommitOnlyTypes())); |
| |
| #if BUILDFLAG(IS_ANDROID) |
| // SESSIONS is a noisy data type whose invalidations aren't enabled by default |
| // on Android. |
| invalidation_enabled_types.Remove(SESSIONS); |
| #endif |
| |
| InitializeBackend(); |
| EXPECT_CALL( |
| invalidator_, |
| UpdateInterestedTopics( |
| backend_.get(), ModelTypeSetToTopicSet(invalidation_enabled_types))); |
| ConfigureDataTypes(); |
| |
| // When Sync is stopped, we clear the registered invalidation ids. |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), invalidation::TopicSet())); |
| ShutdownBackend(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| } |
| |
| // TODO(crbug.com/1404927): remove the test once old invalidations are not used |
| // in sync anymore. |
| TEST_F(SyncEngineImplTest, WhenEnabledTypesStayDisabled) { |
| base::test::ScopedFeatureList feature_overrides; |
| feature_overrides.InitAndDisableFeature(syncer::kUseSyncInvalidations); |
| |
| // Tests that noisy types aren't used for registration if they're disabled, |
| // hence removing noisy datatypes from |enabled_types_|. |
| enabled_types_.Remove(SESSIONS); |
| |
| InitializeBackend(); |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), |
| ModelTypeSetToTopicSet(Difference( |
| enabled_types_, CommitOnlyTypes())))); |
| ConfigureDataTypes(); |
| |
| // When Sync is stopped, we clear the registered invalidation ids. |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), invalidation::TopicSet())); |
| ShutdownBackend(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| } |
| |
| // TODO(crbug.com/1404927): remove the test once old invalidations are not used |
| // in sync anymore. |
| TEST_F(SyncEngineImplTest, |
| EnabledTypesChangesWhenSetInvalidationsForSessionsCalled) { |
| base::test::ScopedFeatureList feature_overrides; |
| feature_overrides.InitAndDisableFeature(syncer::kUseSyncInvalidations); |
| |
| // Making sure that the noisy types we're interested in are in the |
| // |enabled_types_|. |
| enabled_types_.Put(SESSIONS); |
| |
| InitializeBackend(); |
| ConfigureDataTypes(); |
| |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), |
| ModelTypeSetToTopicSet(Difference( |
| enabled_types_, CommitOnlyTypes())))); |
| backend_->SetInvalidationsForSessionsEnabled(true); |
| |
| ModelTypeSet enabled_types(enabled_types_); |
| enabled_types.Remove(SESSIONS); |
| |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), |
| ModelTypeSetToTopicSet(Difference( |
| enabled_types, CommitOnlyTypes())))); |
| backend_->SetInvalidationsForSessionsEnabled(false); |
| |
| // When Sync is stopped, we clear the registered invalidation ids. |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), invalidation::TopicSet())); |
| ShutdownBackend(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| ShouldInvalidateDataTypesOnIncomingInvalidation) { |
| enabled_types_.PutAll({syncer::BOOKMARKS, syncer::PREFERENCES}); |
| |
| InitializeBackend(/*expect_success=*/true); |
| ConfigureDataTypes(); |
| |
| sync_pb::SyncInvalidationsPayload payload; |
| sync_pb::SyncInvalidationsPayload::DataTypeInvalidation* |
| bookmarks_invalidation = payload.add_data_type_invalidations(); |
| bookmarks_invalidation->set_data_type_id( |
| GetSpecificsFieldNumberFromModelType(ModelType::BOOKMARKS)); |
| sync_pb::SyncInvalidationsPayload::DataTypeInvalidation* |
| preferences_invalidation = payload.add_data_type_invalidations(); |
| preferences_invalidation->set_data_type_id( |
| GetSpecificsFieldNumberFromModelType(ModelType::PREFERENCES)); |
| |
| EXPECT_CALL(mock_sync_invalidations_service_, GetInterestedDataTypes()) |
| .WillOnce(Return(enabled_types_)); |
| backend_->OnInvalidationReceived(payload.SerializeAsString()); |
| |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_EQ(1, fake_manager_->GetInvalidationCount(ModelType::BOOKMARKS)); |
| EXPECT_EQ(1, fake_manager_->GetInvalidationCount(ModelType::PREFERENCES)); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| ShouldInvalidateOnlyEnabledDataTypes) { |
| enabled_types_.Remove(syncer::BOOKMARKS); |
| enabled_types_.Put(syncer::PREFERENCES); |
| |
| InitializeBackend(/*expect_success=*/true); |
| ConfigureDataTypes(); |
| |
| sync_pb::SyncInvalidationsPayload payload; |
| sync_pb::SyncInvalidationsPayload::DataTypeInvalidation* |
| bookmarks_invalidation = payload.add_data_type_invalidations(); |
| bookmarks_invalidation->set_data_type_id( |
| GetSpecificsFieldNumberFromModelType(ModelType::BOOKMARKS)); |
| sync_pb::SyncInvalidationsPayload::DataTypeInvalidation* |
| preferences_invalidation = payload.add_data_type_invalidations(); |
| preferences_invalidation->set_data_type_id( |
| GetSpecificsFieldNumberFromModelType(ModelType::PREFERENCES)); |
| |
| EXPECT_CALL(mock_sync_invalidations_service_, GetInterestedDataTypes()) |
| .WillOnce(Return(enabled_types_)); |
| backend_->OnInvalidationReceived(payload.SerializeAsString()); |
| |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_EQ(0, fake_manager_->GetInvalidationCount(ModelType::BOOKMARKS)); |
| EXPECT_EQ(1, fake_manager_->GetInvalidationCount(ModelType::PREFERENCES)); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| ShouldStartHandlingInvalidations) { |
| ON_CALL(mock_sync_invalidations_service_, GetInterestedDataTypes()) |
| .WillByDefault(Return(enabled_types_)); |
| EXPECT_CALL(mock_sync_invalidations_service_, AddListener(backend_.get())); |
| backend_->StartHandlingInvalidations(); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsTest, |
| UseOldInvalidationsOnlyForWalletAndOffer) { |
| enabled_types_.PutAll({AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_OFFER}); |
| |
| EXPECT_CALL(mock_sync_invalidations_service_, GetInterestedDataTypes()) |
| .WillRepeatedly(Return(enabled_types_)); |
| InitializeBackend(/*expect_success=*/true); |
| EXPECT_CALL( |
| invalidator_, |
| UpdateInterestedTopics( |
| backend_.get(), ModelTypeSetToTopicSet( |
| {AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_OFFER}))); |
| ConfigureDataTypes(); |
| |
| // When Sync is stopped, we clear the registered invalidation ids. |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(backend_.get(), invalidation::TopicSet())); |
| ShutdownBackend(ShutdownReason::STOP_SYNC_AND_KEEP_DATA); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| DoNotUseOldInvalidationsAtAll) { |
| enabled_types_.PutAll({AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_OFFER}); |
| |
| // Since the old invalidations system is not being used anymore (based on the |
| // enabled feature flags), SyncEngine should call the (old) invalidator with |
| // an empty TopicSet upon initialization. |
| EXPECT_CALL(invalidator_, |
| UpdateInterestedTopics(_, invalidation::TopicSet())); |
| EXPECT_CALL(invalidator_, UnsubscribeFromUnregisteredTopics); |
| EXPECT_CALL(mock_sync_invalidations_service_, GetInterestedDataTypes()) |
| .WillRepeatedly(Return(enabled_types_)); |
| InitializeBackend(/*expect_success=*/true); |
| |
| EXPECT_CALL(invalidator_, UpdateInterestedTopics).Times(0); |
| ConfigureDataTypes(); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| ShouldEnableInvalidationsWhenInitialized) { |
| EXPECT_CALL(mock_sync_invalidations_service_, GetFCMRegistrationToken) |
| .WillRepeatedly(Return("fcm_token")); |
| InitializeBackend(/*expect_success=*/true); |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_TRUE(fake_manager_->IsInvalidatorEnabled()); |
| } |
| |
| TEST_F(SyncEngineImplWithSyncInvalidationsForWalletAndOfferTest, |
| ShouldEnableInvalidationsOnTokenUpdate) { |
| EXPECT_CALL(mock_sync_invalidations_service_, GetFCMRegistrationToken) |
| .WillRepeatedly(Return(absl::nullopt)); |
| InitializeBackend(/*expect_success=*/true); |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_FALSE(fake_manager_->IsInvalidatorEnabled()); |
| |
| EXPECT_CALL(mock_sync_invalidations_service_, GetFCMRegistrationToken) |
| .WillRepeatedly(Return("fcm_token")); |
| backend_->OnFCMRegistrationTokenChanged(); |
| fake_manager_->WaitForSyncThread(); |
| EXPECT_TRUE(fake_manager_->IsInvalidatorEnabled()); |
| } |
| |
| TEST_F(SyncEngineImplTest, GenerateCacheGUID) { |
| const std::string guid1 = SyncEngineImpl::GenerateCacheGUIDForTest(); |
| const std::string guid2 = SyncEngineImpl::GenerateCacheGUIDForTest(); |
| EXPECT_EQ(24U, guid1.size()); |
| EXPECT_EQ(24U, guid2.size()); |
| EXPECT_NE(guid1, guid2); |
| } |
| |
| TEST_F(SyncEngineImplTest, ShouldPopulateAccountIdCachedInPrefs) { |
| const std::string kTestCacheGuid = "test_cache_guid"; |
| const std::string kTestBirthday = "test_birthday"; |
| |
| SyncTransportDataPrefs transport_data_prefs(&pref_service_); |
| transport_data_prefs.SetCacheGuid(kTestCacheGuid); |
| transport_data_prefs.SetBirthday(kTestBirthday); |
| |
| InitializeBackend(); |
| |
| ASSERT_EQ(kTestCacheGuid, transport_data_prefs.GetCacheGuid()); |
| EXPECT_EQ(kTestGaiaId, transport_data_prefs.GetGaiaId()); |
| } |
| |
| TEST_F(SyncEngineImplTest, |
| ShouldNotPopulateAccountIdCachedInPrefsWithLocalSync) { |
| const std::string kTestCacheGuid = "test_cache_guid"; |
| const std::string kTestBirthday = "test_birthday"; |
| |
| SyncTransportDataPrefs transport_data_prefs(&pref_service_); |
| transport_data_prefs.SetCacheGuid(kTestCacheGuid); |
| transport_data_prefs.SetBirthday(kTestBirthday); |
| |
| InitializeBackend(/*expect_success=*/true, /*gaia_id=*/std::string()); |
| |
| ASSERT_EQ(kTestCacheGuid, transport_data_prefs.GetCacheGuid()); |
| EXPECT_TRUE(transport_data_prefs.GetGaiaId().empty()); |
| } |
| |
| TEST_F(SyncEngineImplTest, ShouldLoadSyncDataUponInitialization) { |
| const std::string kTestCacheGuid = "test_cache_guid"; |
| const std::string kTestBirthday = "test_birthday"; |
| |
| SyncTransportDataPrefs transport_data_prefs(&pref_service_); |
| transport_data_prefs.SetCacheGuid(kTestCacheGuid); |
| transport_data_prefs.SetBirthday(kTestBirthday); |
| transport_data_prefs.SetGaiaId(kTestGaiaId); |
| |
| EXPECT_CALL(sync_transport_data_cleared_cb_, Run()).Times(0); |
| InitializeBackend(); |
| |
| EXPECT_EQ(kTestGaiaId, transport_data_prefs.GetGaiaId()); |
| EXPECT_EQ(kTestCacheGuid, transport_data_prefs.GetCacheGuid()); |
| EXPECT_EQ(kTestBirthday, transport_data_prefs.GetBirthday()); |
| } |
| |
| // Verifies that local sync transport data is thrown away if there is a mismatch |
| // between the account ID cached in SyncPrefs and the actual one. |
| TEST_F(SyncEngineImplTest, |
| ShouldClearLocalSyncTransportDataDueToAccountIdMismatch) { |
| const std::string kTestCacheGuid = "test_cache_guid"; |
| const std::string kTestBirthday = "test_birthday"; |
| |
| SyncTransportDataPrefs transport_data_prefs(&pref_service_); |
| transport_data_prefs.SetCacheGuid(kTestCacheGuid); |
| transport_data_prefs.SetBirthday(kTestBirthday); |
| transport_data_prefs.SetGaiaId("corrupt_gaia_id"); |
| |
| EXPECT_CALL(sync_transport_data_cleared_cb_, Run()); |
| InitializeBackend(); |
| |
| EXPECT_EQ(kTestGaiaId, transport_data_prefs.GetGaiaId()); |
| EXPECT_NE(kTestCacheGuid, transport_data_prefs.GetCacheGuid()); |
| EXPECT_NE(kTestBirthday, transport_data_prefs.GetBirthday()); |
| } |
| |
| } // namespace |
| |
| } // namespace syncer |