blob: 3b61d2c5bfb151da6b9081185eb9e787c7d051d3 [file] [log] [blame]
// Copyright 2013 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 "components/sync/driver/glue/sync_backend_host_impl.h"
#include <cstddef>
#include <utility>
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/invalidation/impl/invalidation_logger.h"
#include "components/invalidation/impl/invalidation_switches.h"
#include "components/invalidation/impl/invalidator_storage.h"
#include "components/invalidation/impl/profile_invalidation_provider.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/invalidation/public/object_id_invalidation_map.h"
#include "components/sync/base/experiments.h"
#include "components/sync/base/invalidation_helper.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/base/test_unrecoverable_error_handler.h"
#include "components/sync/device_info/device_info.h"
#include "components/sync/driver/fake_sync_client.h"
#include "components/sync/engine/cycle/commit_counters.h"
#include "components/sync/engine/cycle/status_counters.h"
#include "components/sync/engine/cycle/update_counters.h"
#include "components/sync/engine/fake_sync_manager.h"
#include "components/sync/engine/model_safe_worker.h"
#include "components/sync/engine/net/http_bridge_network_resources.h"
#include "components/sync/engine/net/network_resources.h"
#include "components/sync/engine/passive_model_worker.h"
#include "components/sync/engine/sync_engine_host_stub.h"
#include "components/sync/engine/sync_manager_factory.h"
#include "components/sync/test/callback_counter.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "google/cacheinvalidation/include/types.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using ::testing::InvokeWithoutArgs;
using ::testing::StrictMock;
using ::testing::_;
namespace syncer {
namespace {
static const base::FilePath::CharType kTestSyncDir[] =
FILE_PATH_LITERAL("sync-test");
class TestSyncEngineHost : public SyncEngineHostStub {
public:
explicit TestSyncEngineHost(
base::Callback<void(ModelTypeSet)> set_engine_types)
: set_engine_types_(set_engine_types) {}
void OnEngineInitialized(ModelTypeSet initial_types,
const WeakHandle<JsBackend>&,
const WeakHandle<DataTypeDebugInfoListener>&,
const std::string&,
const std::string&,
bool success) override {
EXPECT_EQ(expect_success_, success);
set_engine_types_.Run(initial_types);
std::move(quit_closure_).Run();
}
void SetExpectSuccess(bool expect_success) {
expect_success_ = expect_success;
}
void set_quit_closure(base::OnceClosure quit_closure) {
quit_closure_ = std::move(quit_closure);
}
private:
base::Callback<void(ModelTypeSet)> set_engine_types_;
bool expect_success_ = false;
base::OnceClosure quit_closure_;
};
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 {}
// 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_;
FakeSyncManager** fake_manager_;
};
class BackendSyncClient : public FakeSyncClient {
public:
scoped_refptr<ModelSafeWorker> CreateModelWorkerForGroup(
ModelSafeGroup group) override {
switch (group) {
case GROUP_PASSIVE:
return new PassiveModelWorker();
default:
return nullptr;
}
}
};
class NullEncryptionObserver : public SyncEncryptionHandler::Observer {
public:
void OnPassphraseRequired(
PassphraseRequiredReason reason,
const KeyDerivationParams& key_derivation_params,
const sync_pb::EncryptedData& pending_keys) override {}
void OnPassphraseAccepted() override {}
void OnBootstrapTokenUpdated(const std::string& bootstrap_token,
BootstrapTokenType type) override {}
void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
bool encrypt_everything) override {}
void OnEncryptionComplete() override {}
void OnCryptographerStateChanged(Cryptographer* cryptographer) override {}
void OnPassphraseTypeChanged(PassphraseType type,
base::Time passphrase_time) override {}
void OnLocalSetPassphraseEncryption(
const SyncEncryptionHandler::NigoriState& nigori_state) override {}
};
class MockInvalidationService : public invalidation::InvalidationService {
public:
MockInvalidationService() = default;
~MockInvalidationService() override = default;
MOCK_METHOD1(RegisterInvalidationHandler,
void(syncer::InvalidationHandler* handler));
MOCK_METHOD2(UpdateRegisteredInvalidationIds,
bool(syncer::InvalidationHandler* handler,
const syncer::ObjectIdSet& ids));
MOCK_METHOD1(UnregisterInvalidationHandler,
void(syncer::InvalidationHandler* handler));
MOCK_METHOD0(GetInvalidatorStat, syncer::InvalidatorState());
MOCK_CONST_METHOD0(GetInvalidatorState, syncer::InvalidatorState());
MOCK_CONST_METHOD0(GetInvalidatorClientId, std::string());
MOCK_METHOD0(GetInvalidationLogger, invalidation::InvalidationLogger*());
MOCK_CONST_METHOD1(RequestDetailedStatus,
void(base::RepeatingCallback<
void(const base::DictionaryValue&)> post_caller));
};
class SyncEngineTest : public testing::Test {
protected:
SyncEngineTest()
: sync_thread_("SyncThreadForTest"),
host_(base::Bind(&SyncEngineTest::SetEngineTypes,
base::Unretained(this))),
fake_manager_(nullptr) {}
~SyncEngineTest() override {}
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
sync_prefs_ = std::make_unique<SyncPrefs>(&pref_service_);
sync_thread_.StartAndWaitForTesting();
ON_CALL(invalidator_,
UpdateRegisteredInvalidationIds(testing::_, testing::_))
.WillByDefault(testing::Return(true));
backend_ = std::make_unique<SyncBackendHostImpl>(
"dummyDebugName", &sync_client_, &invalidator_,
sync_prefs_->AsWeakPtr(),
temp_dir_.GetPath().Append(base::FilePath(kTestSyncDir)));
credentials_.account_id = "user@example.com";
credentials_.email = "user@example.com";
credentials_.sync_token = "sync_token";
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);
network_resources_ = std::make_unique<HttpBridgeNetworkResources>();
}
void TearDown() override {
if (backend_) {
backend_->StopSyncingForShutdown();
backend_->Shutdown(STOP_SYNC);
}
backend_.reset();
sync_prefs_.reset();
// Pump messages posted by the sync thread.
base::RunLoop().RunUntilIdle();
}
// Synchronously initializes the backend.
void InitializeBackend(bool expect_success) {
host_.SetExpectSuccess(expect_success);
SyncEngine::HttpPostProviderFactoryGetter
http_post_provider_factory_getter =
base::BindOnce(&NetworkResources::GetHttpPostProviderFactory,
base::Unretained(network_resources_.get()), nullptr,
base::DoNothing());
SyncEngine::InitParams params;
params.sync_task_runner = sync_thread_.task_runner();
params.host = &host_;
params.registrar = std::make_unique<SyncBackendRegistrar>(
std::string(), base::Bind(&SyncClient::CreateModelWorkerForGroup,
base::Unretained(&sync_client_)));
params.encryption_observer_proxies.push_back(
std::make_unique<NullEncryptionObserver>());
params.http_factory_getter = std::move(http_post_provider_factory_getter);
params.credentials = credentials_;
params.sync_manager_factory = std::move(fake_manager_factory_);
params.delete_sync_data_folder = true;
params.unrecoverable_error_handler =
MakeWeakHandle(test_unrecoverable_error_handler_.GetWeakPtr()),
params.saved_nigori_state = std::move(saved_nigori_state_);
sync_prefs_->GetInvalidationVersions(&params.invalidation_versions);
backend_->Initialize(std::move(params));
PumpSyncThread();
// |fake_manager_factory_|'s 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_);
}
// Synchronously configures the backend's datatypes.
ModelTypeSet ConfigureDataTypes() {
return ConfigureDataTypesWithUnready(ModelTypeSet());
}
ModelTypeSet ConfigureDataTypesWithUnready(ModelTypeSet unready_types) {
ModelTypeSet disabled_types =
Difference(ModelTypeSet::All(), enabled_types_);
ModelTypeConfigurer::ConfigureParams params;
params.reason = CONFIGURE_REASON_RECONFIGURATION;
params.enabled_types = Difference(enabled_types_, unready_types);
params.disabled_types = Union(disabled_types, unready_types);
params.to_download = Difference(params.enabled_types, engine_types_);
if (!params.to_download.Empty()) {
params.to_download.Put(NIGORI);
}
params.to_purge = Intersection(engine_types_, disabled_types);
params.ready_task =
base::Bind(&SyncEngineTest::DownloadReady, base::Unretained(this));
ModelTypeSet ready_types =
Difference(params.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);
std::move(quit_loop_).Run();
}
void SetEngineTypes(ModelTypeSet engine_types) {
EXPECT_TRUE(engine_types_.Empty());
engine_types_ = engine_types;
}
void PumpSyncThread() {
base::RunLoop run_loop;
quit_loop_ = run_loop.QuitClosure();
host_.set_quit_closure(run_loop.QuitClosure());
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
run_loop.Run();
}
base::test::ScopedTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
sync_preferences::TestingPrefServiceSyncable pref_service_;
base::Thread sync_thread_;
TestSyncEngineHost host_;
SyncCredentials credentials_;
BackendSyncClient sync_client_;
TestUnrecoverableErrorHandler test_unrecoverable_error_handler_;
std::unique_ptr<SyncPrefs> sync_prefs_;
std::unique_ptr<SyncBackendHostImpl> backend_;
std::unique_ptr<FakeSyncManagerFactory> fake_manager_factory_;
FakeSyncManager* fake_manager_;
ModelTypeSet engine_types_;
ModelTypeSet enabled_types_;
std::unique_ptr<NetworkResources> network_resources_;
std::unique_ptr<SyncEncryptionHandler::NigoriState> saved_nigori_state_;
base::OnceClosure quit_loop_;
testing::NiceMock<MockInvalidationService> invalidator_;
};
// Test basic initialization with no initial types (first time initialization).
// Only the nigori should be configured.
TEST_F(SyncEngineTest, InitShutdown) {
InitializeBackend(true);
EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes());
EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(ControlTypes())
.Empty());
}
// Test first time sync scenario. All types should be properly configured.
TEST_F(SyncEngineTest, FirstTimeSync) {
InitializeBackend(true);
EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes());
EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(ControlTypes())
.Empty());
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());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// Test the restart after setting up sync scenario. No enabled types should be
// downloaded or cleaned.
TEST_F(SyncEngineTest, Restart) {
sync_prefs_->SetFirstSetupComplete();
fake_manager_factory_->set_progress_marker_types(enabled_types_);
fake_manager_factory_->set_initial_sync_ended_types(enabled_types_);
InitializeBackend(true);
EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
EXPECT_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
ModelTypeSet ready_types = ConfigureDataTypes();
EXPECT_EQ(enabled_types_, ready_types);
EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
EXPECT_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// Test a sync restart scenario where some types had never finished configuring.
// The partial types should be purged, then reconfigured properly.
TEST_F(SyncEngineTest, PartialTypes) {
sync_prefs_->SetFirstSetupComplete();
// Set sync manager behavior before passing it down. All types have progress
// markers, but nigori and bookmarks are missing initial sync ended.
ModelTypeSet partial_types(NIGORI, BOOKMARKS);
ModelTypeSet full_types = Difference(enabled_types_, partial_types);
fake_manager_factory_->set_progress_marker_types(enabled_types_);
fake_manager_factory_->set_initial_sync_ended_types(full_types);
// Bringing up the backend should purge all partial types, then proceed to
// download the Nigori.
InitializeBackend(true);
EXPECT_EQ(ModelTypeSet(NIGORI), fake_manager_->GetAndResetDownloadedTypes());
EXPECT_TRUE(fake_manager_->GetAndResetPurgedTypes().HasAll(partial_types));
EXPECT_EQ(Union(full_types, ModelTypeSet(NIGORI)),
fake_manager_->InitialSyncEndedTypes());
EXPECT_EQ(
Difference(partial_types, ModelTypeSet(NIGORI)),
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
// Now do the actual configuration, which should download and apply bookmarks.
ModelTypeSet ready_types = ConfigureDataTypes();
EXPECT_EQ(full_types, ready_types);
EXPECT_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(partial_types, fake_manager_->GetAndResetDownloadedTypes());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// Test the behavior when we lose the sync db. Although we already have types
// enabled, we should re-download all of them because we lost their data.
TEST_F(SyncEngineTest, LostDB) {
sync_prefs_->SetFirstSetupComplete();
// Initialization should fetch the Nigori node. Everything else should be
// left untouched.
InitializeBackend(true);
EXPECT_EQ(ModelTypeSet(ControlTypes()),
fake_manager_->GetAndResetDownloadedTypes());
EXPECT_EQ(ModelTypeSet(ControlTypes()),
fake_manager_->InitialSyncEndedTypes());
EXPECT_EQ(
Difference(enabled_types_, ControlTypes()),
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
// The database was empty, so any cleaning is entirely optional. We want to
// reset this value before running the next part of the test, though.
fake_manager_->GetAndResetPurgedTypes();
// The actual configuration should redownload and apply all the enabled types.
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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
TEST_F(SyncEngineTest, DisableTypes) {
// Simulate first time sync.
InitializeBackend(true);
fake_manager_->GetAndResetPurgedTypes();
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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
// Then disable two datatypes.
ModelTypeSet disabled_types(BOOKMARKS, SEARCH_ENGINES);
ModelTypeSet old_types = enabled_types_;
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());
EXPECT_EQ(disabled_types,
Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types));
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
TEST_F(SyncEngineTest, AddTypes) {
// Simulate first time sync.
InitializeBackend(true);
fake_manager_->GetAndResetPurgedTypes();
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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
// 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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// And and disable in the same configuration.
TEST_F(SyncEngineTest, AddDisableTypes) {
// Simulate first time sync.
InitializeBackend(true);
fake_manager_->GetAndResetPurgedTypes();
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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
// Then add two datatypes.
ModelTypeSet old_types = enabled_types_;
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());
EXPECT_EQ(disabled_types,
Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types));
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_EQ(disabled_types,
fake_manager_->GetTypesWithEmptyProgressMarkerToken(old_types));
}
// Test restarting the browser to newly supported datatypes. The new datatypes
// should be downloaded on the configuration after backend initialization.
TEST_F(SyncEngineTest, NewlySupportedTypes) {
sync_prefs_->SetFirstSetupComplete();
// 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(true);
EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
EXPECT_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types).Empty());
EXPECT_EQ(old_types, fake_manager_->InitialSyncEndedTypes());
EXPECT_EQ(new_types, fake_manager_->GetTypesWithEmptyProgressMarkerToken(
enabled_types_));
// 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_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// Test the newly supported types scenario, but with the presence of partial
// types as well. Both partial and newly supported types should be downloaded
// the configuration.
TEST_F(SyncEngineTest, NewlySupportedTypesWithPartialTypes) {
sync_prefs_->SetFirstSetupComplete();
// 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_;
ModelTypeSet partial_types(NIGORI, BOOKMARKS);
ModelTypeSet full_types = Difference(enabled_types_, partial_types);
fake_manager_factory_->set_progress_marker_types(old_types);
fake_manager_factory_->set_initial_sync_ended_types(full_types);
ModelTypeSet new_types(APP_SETTINGS, EXTENSION_SETTINGS);
enabled_types_.PutAll(new_types);
// Purge the partial types. The nigori will be among the purged types, but
// the syncer will re-download it by the time the initialization is complete.
InitializeBackend(true);
EXPECT_EQ(ModelTypeSet(NIGORI), fake_manager_->GetAndResetDownloadedTypes());
EXPECT_TRUE(fake_manager_->GetAndResetPurgedTypes().HasAll(partial_types));
EXPECT_EQ(Union(full_types, ModelTypeSet(NIGORI)),
fake_manager_->InitialSyncEndedTypes());
EXPECT_EQ(
Union(new_types, Difference(partial_types, ModelTypeSet(NIGORI))),
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
// Downloads and applies the new types and partial types (which includes
// nigori anyways).
ModelTypeSet ready_types = ConfigureDataTypes();
EXPECT_EQ(full_types, ready_types);
EXPECT_EQ(Union(new_types, partial_types),
fake_manager_->GetAndResetDownloadedTypes());
EXPECT_TRUE(
Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
.Empty());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// Verify that downloading control types only downloads those types that do
// not have initial sync ended set.
TEST_F(SyncEngineTest, DownloadControlTypes) {
sync_prefs_->SetFirstSetupComplete();
// 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(EXPERIMENTS, 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(true);
EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes());
EXPECT_EQ(Difference(ModelTypeSet::All(), enabled_types_),
fake_manager_->GetAndResetPurgedTypes());
EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
.Empty());
}
// 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 backend host
// 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(SyncEngineTest, SilentlyFailToDownloadControlTypes) {
fake_manager_factory_->set_configure_fail_types(ModelTypeSet::All());
InitializeBackend(false);
}
// Test that local refresh requests are delivered to sync.
TEST_F(SyncEngineTest, ForwardLocalRefreshRequest) {
InitializeBackend(true);
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(SyncEngineTest, DownloadControlTypesNewClient) {
InitializeBackend(true);
EXPECT_EQ(CONFIGURE_REASON_NEW_CLIENT,
fake_manager_->GetAndResetConfigureReason());
}
// Test that configuration on restart sends the proper GU source.
TEST_F(SyncEngineTest, DownloadControlTypesRestart) {
sync_prefs_->SetFirstSetupComplete();
fake_manager_factory_->set_progress_marker_types(enabled_types_);
fake_manager_factory_->set_initial_sync_ended_types(enabled_types_);
InitializeBackend(true);
EXPECT_EQ(CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE,
fake_manager_->GetAndResetConfigureReason());
}
// It is SyncBackendHostCore responsibility to cleanup Sync Data folder if sync
// setup hasn't been completed. This test ensures that cleanup happens.
TEST_F(SyncEngineTest, TestStartupWithOldSyncData) {
const char* nonsense = "slon";
base::FilePath temp_directory =
temp_dir_.GetPath().Append(base::FilePath(kTestSyncDir));
base::FilePath sync_file = temp_directory.AppendASCII("SyncData.sqlite3");
ASSERT_TRUE(base::CreateDirectory(temp_directory));
ASSERT_NE(-1, base::WriteFile(sync_file, nonsense, strlen(nonsense)));
InitializeBackend(true);
EXPECT_FALSE(base::PathExists(sync_file));
}
// 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(SyncEngineTest, DisableThenPurgeType) {
ModelTypeSet error_types(BOOKMARKS);
InitializeBackend(true);
// 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);
EXPECT_TRUE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(error_types).Empty());
// 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);
EXPECT_FALSE(
fake_manager_->GetTypesWithEmptyProgressMarkerToken(error_types).Empty());
}
// Test that a call to ClearServerData is forwarded to the underlying
// SyncManager.
TEST_F(SyncEngineTest, ClearServerDataCallsAreForwarded) {
InitializeBackend(true);
CallbackCounter callback_counter;
backend_->ClearServerData(base::Bind(&CallbackCounter::Callback,
base::Unretained(&callback_counter)));
fake_manager_->WaitForSyncThread();
EXPECT_EQ(1, callback_counter.times_called());
}
// Ensure that redundant invalidations are ignored and that the most recent
// set of invalidation version is persisted across restarts.
TEST_F(SyncEngineTest, IgnoreOldInvalidations) {
// Set up some old persisted invalidations.
std::map<ModelType, int64_t> invalidation_versions;
invalidation_versions[BOOKMARKS] = 20;
sync_prefs_->UpdateInvalidationVersions(invalidation_versions);
InitializeBackend(true);
EXPECT_EQ(0, fake_manager_->GetInvalidationCount());
// Receiving an invalidation with an old version should do nothing.
ObjectIdInvalidationMap invalidation_map;
std::string notification_type;
RealModelTypeToNotificationType(BOOKMARKS, &notification_type);
invalidation_map.Insert(Invalidation::Init(
invalidation::ObjectId(0, notification_type), 10, "payload"));
backend_->OnIncomingInvalidation(invalidation_map);
fake_manager_->WaitForSyncThread();
EXPECT_EQ(0, fake_manager_->GetInvalidationCount());
// Invalidations with new versions should be acted upon.
invalidation_map.Insert(Invalidation::Init(
invalidation::ObjectId(0, notification_type), 30, "payload"));
backend_->OnIncomingInvalidation(invalidation_map);
fake_manager_->WaitForSyncThread();
EXPECT_EQ(1, fake_manager_->GetInvalidationCount());
// Invalidation for new data types should be acted on.
RealModelTypeToNotificationType(SESSIONS, &notification_type);
invalidation_map.Insert(Invalidation::Init(
invalidation::ObjectId(0, notification_type), 10, "payload"));
backend_->OnIncomingInvalidation(invalidation_map);
fake_manager_->WaitForSyncThread();
EXPECT_EQ(2, fake_manager_->GetInvalidationCount());
// But redelivering that same invalidation should be ignored.
backend_->OnIncomingInvalidation(invalidation_map);
fake_manager_->WaitForSyncThread();
EXPECT_EQ(2, fake_manager_->GetInvalidationCount());
// If an invalidation with an unknown version is received, it should be
// acted on, but should not affect the persisted versions.
invalidation_map.Insert(Invalidation::InitUnknownVersion(
invalidation::ObjectId(0, notification_type)));
backend_->OnIncomingInvalidation(invalidation_map);
fake_manager_->WaitForSyncThread();
EXPECT_EQ(3, fake_manager_->GetInvalidationCount());
// Verify that the invalidation versions were updated in the prefs.
invalidation_versions[BOOKMARKS] = 30;
invalidation_versions[SESSIONS] = 10;
std::map<ModelType, int64_t> persisted_invalidation_versions;
sync_prefs_->GetInvalidationVersions(&persisted_invalidation_versions);
EXPECT_EQ(invalidation_versions.size(),
persisted_invalidation_versions.size());
for (auto iter : persisted_invalidation_versions) {
EXPECT_EQ(invalidation_versions[iter.first], iter.second);
}
}
// Tests that SyncBackendHostImpl retains ModelTypeConnector after call to
// StopSyncingForShutdown. This is needed for datatype deactivation during
// DataTypeManager shutdown.
TEST_F(SyncEngineTest, ModelTypeConnectorValidDuringShutdown) {
InitializeBackend(true);
backend_->StopSyncingForShutdown();
// Verify that call to DeactivateNonBlockingDataType doesn't assert.
backend_->DeactivateNonBlockingDataType(AUTOFILL);
backend_->Shutdown(STOP_SYNC);
backend_.reset();
}
TEST_F(SyncEngineTest, EnabledTypesStayUnchangedWhenFCMIsDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
invalidation::switches::kFCMInvalidations);
// Making sure that the noisy types we're interested in are in the
// |enabled_types_|.
enabled_types_.Put(SESSIONS);
enabled_types_.Put(FAVICON_IMAGES);
enabled_types_.Put(FAVICON_TRACKING);
InitializeBackend(true);
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(
backend_.get(), ModelTypeSetToObjectIdSet(enabled_types_)));
ConfigureDataTypes();
// At shutdown, we clear the registered invalidation ids.
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(backend_.get(), ObjectIdSet()));
}
TEST_F(
SyncEngineTest,
NoisyDataTypesInvalidationAreDiscardedByDefaultOnAndroidWhenFCMIsEnabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
invalidation::switches::kFCMInvalidations);
// Making sure that the noisy types we're interested in are in the
// |enabled_types_|.
enabled_types_.Put(SESSIONS);
enabled_types_.Put(FAVICON_IMAGES);
enabled_types_.Put(FAVICON_TRACKING);
ModelTypeSet invalidation_enabled_types(enabled_types_);
#if defined(OS_ANDROID)
// SESSIONS, FAVICON_IMAGES, FAVICON_TRACKING are noisy data types whose
// invalidations aren't enabled by default on Android.
invalidation_enabled_types.Remove(SESSIONS);
invalidation_enabled_types.Remove(FAVICON_IMAGES);
invalidation_enabled_types.Remove(FAVICON_TRACKING);
#endif
InitializeBackend(true);
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(
backend_.get(),
ModelTypeSetToObjectIdSet(invalidation_enabled_types)));
ConfigureDataTypes();
// At shutdown, we clear the registered invalidation ids.
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(backend_.get(), ObjectIdSet()));
}
TEST_F(SyncEngineTest, WhenEnabledTypesStayDisabledFCMIsEnabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
invalidation::switches::kFCMInvalidations);
// Testing that noisy types doesn't used for registration, when
// they're disabled in Sync, hence removing noisy datatypes from
// |enabled_types_|.
enabled_types_.Remove(SESSIONS);
enabled_types_.Remove(FAVICON_IMAGES);
enabled_types_.Remove(FAVICON_TRACKING);
InitializeBackend(true);
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(
backend_.get(), ModelTypeSetToObjectIdSet(enabled_types_)));
ConfigureDataTypes();
// At shutdown, we clear the registered invalidation ids.
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(backend_.get(), ObjectIdSet()));
}
TEST_F(SyncEngineTest,
EnabledTypesChangesWhenSetInvalidationsForSessionsCalled) {
base::test::ScopedFeatureList scoped_feature_list;
// Making sure that the noisy types we're interested in are in the
// |enabled_types_|.
enabled_types_.Put(SESSIONS);
enabled_types_.Put(FAVICON_IMAGES);
enabled_types_.Put(FAVICON_TRACKING);
InitializeBackend(true);
ConfigureDataTypes();
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(
backend_.get(), ModelTypeSetToObjectIdSet(enabled_types_)));
backend_->SetInvalidationsForSessionsEnabled(true);
ModelTypeSet enabled_types(enabled_types_);
enabled_types.Remove(SESSIONS);
enabled_types.Remove(FAVICON_IMAGES);
enabled_types.Remove(FAVICON_TRACKING);
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(
backend_.get(), ModelTypeSetToObjectIdSet(enabled_types)));
backend_->SetInvalidationsForSessionsEnabled(false);
// At shutdown, we clear the registered invalidation ids.
EXPECT_CALL(invalidator_,
UpdateRegisteredInvalidationIds(backend_.get(), ObjectIdSet()));
}
} // namespace
} // namespace syncer