|  | // Copyright (c) 2012 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 <stddef.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/files/scoped_temp_dir.h" | 
|  | #include "base/json/json_reader.h" | 
|  | #include "base/json/json_writer.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/values.h" | 
|  | #include "build/build_config.h" | 
|  | #include "chrome/browser/extensions/api/storage/settings_sync_util.h" | 
|  | #include "chrome/browser/extensions/api/storage/sync_value_store_cache.h" | 
|  | #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" | 
|  | #include "chrome/test/base/testing_profile.h" | 
|  | #include "components/sync/model/sync_change_processor.h" | 
|  | #include "components/sync/model/sync_change_processor_wrapper_for_test.h" | 
|  | #include "components/sync/model/sync_error_factory.h" | 
|  | #include "components/sync/model/sync_error_factory_mock.h" | 
|  | #include "content/public/test/test_browser_thread_bundle.h" | 
|  | #include "content/public/test/test_utils.h" | 
|  | #include "extensions/browser/api/storage/backend_task_runner.h" | 
|  | #include "extensions/browser/api/storage/settings_test_util.h" | 
|  | #include "extensions/browser/api/storage/storage_frontend.h" | 
|  | #include "extensions/browser/event_router.h" | 
|  | #include "extensions/browser/event_router_factory.h" | 
|  | #include "extensions/browser/extension_system.h" | 
|  | #include "extensions/browser/mock_extension_system.h" | 
|  | #include "extensions/browser/value_store/test_value_store_factory.h" | 
|  | #include "extensions/browser/value_store/testing_value_store.h" | 
|  | #include "extensions/common/manifest.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using base::DictionaryValue; | 
|  | using base::ListValue; | 
|  | using base::Value; | 
|  | namespace extensions { | 
|  |  | 
|  | namespace util = settings_test_util; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // To save typing ValueStore::DEFAULTS everywhere. | 
|  | const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS; | 
|  |  | 
|  | // More saving typing. Maps extension IDs to a list of sync changes for that | 
|  | // extension. | 
|  | using SettingSyncDataMultimap = | 
|  | std::map<std::string, std::unique_ptr<SettingSyncDataList>>; | 
|  |  | 
|  | // Gets the pretty-printed JSON for a value. | 
|  | static std::string GetJson(const base::Value& value) { | 
|  | std::string json; | 
|  | base::JSONWriter::WriteWithOptions( | 
|  | value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); | 
|  | return json; | 
|  | } | 
|  |  | 
|  | // Returns whether two Values are equal. | 
|  | testing::AssertionResult ValuesEq( | 
|  | const char* _1, const char* _2, | 
|  | const base::Value* expected, | 
|  | const base::Value* actual) { | 
|  | if (expected == actual) { | 
|  | return testing::AssertionSuccess(); | 
|  | } | 
|  | if (!expected && actual) { | 
|  | return testing::AssertionFailure() << | 
|  | "Expected NULL, actual: " << GetJson(*actual); | 
|  | } | 
|  | if (expected && !actual) { | 
|  | return testing::AssertionFailure() << | 
|  | "Expected: " << GetJson(*expected) << ", actual NULL"; | 
|  | } | 
|  | if (!expected->Equals(actual)) { | 
|  | return testing::AssertionFailure() << | 
|  | "Expected: " << GetJson(*expected) << ", actual: " << GetJson(*actual); | 
|  | } | 
|  | return testing::AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | // Returns whether the result of a storage operation is an expected value. | 
|  | // Logs when different. | 
|  | testing::AssertionResult SettingsEq(const char* _1, | 
|  | const char* _2, | 
|  | const base::DictionaryValue& expected, | 
|  | ValueStore::ReadResult actual) { | 
|  | if (!actual.status().ok()) { | 
|  | return testing::AssertionFailure() | 
|  | << "Expected: " << expected | 
|  | << ", actual has error: " << actual.status().message; | 
|  | } | 
|  | return ValuesEq(_1, _2, &expected, &actual.settings()); | 
|  | } | 
|  |  | 
|  | // SyncChangeProcessor which just records the changes made, accessed after | 
|  | // being converted to the more useful SettingSyncData via changes(). | 
|  | class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { | 
|  | public: | 
|  | MockSyncChangeProcessor() : fail_all_requests_(false) {} | 
|  |  | 
|  | // syncer::SyncChangeProcessor implementation. | 
|  | syncer::SyncError ProcessSyncChanges( | 
|  | const base::Location& from_here, | 
|  | const syncer::SyncChangeList& change_list) override { | 
|  | if (fail_all_requests_) { | 
|  | return syncer::SyncError( | 
|  | FROM_HERE, | 
|  | syncer::SyncError::DATATYPE_ERROR, | 
|  | "MockSyncChangeProcessor: configured to fail", | 
|  | change_list[0].sync_data().GetDataType()); | 
|  | } | 
|  | for (syncer::SyncChangeList::const_iterator it = change_list.begin(); | 
|  | it != change_list.end(); ++it) { | 
|  | changes_.push_back(base::MakeUnique<SettingSyncData>(*it)); | 
|  | } | 
|  | return syncer::SyncError(); | 
|  | } | 
|  |  | 
|  | syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override { | 
|  | return syncer::SyncDataList(); | 
|  | } | 
|  |  | 
|  | // Mock methods. | 
|  |  | 
|  | const SettingSyncDataList& changes() { return changes_; } | 
|  |  | 
|  | void ClearChanges() { | 
|  | changes_.clear(); | 
|  | } | 
|  |  | 
|  | void set_fail_all_requests(bool fail_all_requests) { | 
|  | fail_all_requests_ = fail_all_requests; | 
|  | } | 
|  |  | 
|  | // Returns the only change for a given extension setting.  If there is not | 
|  | // exactly 1 change for that key, a test assertion will fail. | 
|  | SettingSyncData* GetOnlyChange(const std::string& extension_id, | 
|  | const std::string& key) { | 
|  | std::vector<SettingSyncData*> matching_changes; | 
|  | for (const std::unique_ptr<SettingSyncData>& change : changes_) { | 
|  | if (change->extension_id() == extension_id && change->key() == key) | 
|  | matching_changes.push_back(change.get()); | 
|  | } | 
|  | if (matching_changes.empty()) { | 
|  | ADD_FAILURE() << "No matching changes for " << extension_id << "/" << | 
|  | key << " (out of " << changes_.size() << ")"; | 
|  | return nullptr; | 
|  | } | 
|  | if (matching_changes.size() != 1u) { | 
|  | ADD_FAILURE() << matching_changes.size() << " matching changes for " << | 
|  | extension_id << "/" << key << " (out of " << changes_.size() << ")"; | 
|  | } | 
|  | return matching_changes[0]; | 
|  | } | 
|  |  | 
|  | private: | 
|  | SettingSyncDataList changes_; | 
|  | bool fail_all_requests_; | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<KeyedService> MockExtensionSystemFactoryFunction( | 
|  | content::BrowserContext* context) { | 
|  | return base::MakeUnique<MockExtensionSystem>(context); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<KeyedService> BuildEventRouter( | 
|  | content::BrowserContext* profile) { | 
|  | return base::MakeUnique<extensions::EventRouter>(profile, nullptr); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class ExtensionSettingsSyncTest : public testing::Test { | 
|  | public: | 
|  | ExtensionSettingsSyncTest() | 
|  | : storage_factory_(new TestValueStoreFactory()), | 
|  | sync_processor_(new MockSyncChangeProcessor), | 
|  | sync_processor_wrapper_(new syncer::SyncChangeProcessorWrapperForTest( | 
|  | sync_processor_.get())) {} | 
|  |  | 
|  | void SetUp() override { | 
|  | ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | 
|  | profile_.reset(new TestingProfile(temp_dir_.GetPath())); | 
|  | storage_factory_->Reset(); | 
|  | frontend_ = | 
|  | StorageFrontend::CreateForTesting(storage_factory_, profile_.get()); | 
|  |  | 
|  | ExtensionsBrowserClient::Get() | 
|  | ->GetExtensionSystemFactory() | 
|  | ->SetTestingFactoryAndUse(profile_.get(), | 
|  | &MockExtensionSystemFactoryFunction); | 
|  |  | 
|  | EventRouterFactory::GetInstance()->SetTestingFactory(profile_.get(), | 
|  | &BuildEventRouter); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | frontend_.reset(); | 
|  | profile_.reset(); | 
|  | // Execute any pending deletion tasks. | 
|  | content::RunAllTasksUntilIdle(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Adds a record of an extension or app to the extension service, then returns | 
|  | // its storage area. | 
|  | ValueStore* AddExtensionAndGetStorage( | 
|  | const std::string& id, Manifest::Type type) { | 
|  | scoped_refptr<const Extension> extension = | 
|  | util::AddExtensionWithId(profile_.get(), id, type); | 
|  | return util::GetStorage(extension, frontend_.get()); | 
|  | } | 
|  |  | 
|  | // Gets the syncer::SyncableService for the given sync type. | 
|  | syncer::SyncableService* GetSyncableService(syncer::ModelType model_type) { | 
|  | SyncValueStoreCache* sync_cache = static_cast<SyncValueStoreCache*>( | 
|  | frontend_->GetValueStoreCache(settings_namespace::SYNC)); | 
|  | return sync_cache->GetSyncableService(model_type); | 
|  | } | 
|  |  | 
|  | // Gets all the sync data from the SyncableService for a sync type as a map | 
|  | // from extension id to its sync data. | 
|  | SettingSyncDataMultimap GetAllSyncData(syncer::ModelType model_type) { | 
|  | syncer::SyncDataList as_list = | 
|  | GetSyncableService(model_type)->GetAllSyncData(model_type); | 
|  | SettingSyncDataMultimap as_map; | 
|  | for (syncer::SyncDataList::iterator it = as_list.begin(); | 
|  | it != as_list.end(); ++it) { | 
|  | std::unique_ptr<SettingSyncData> sync_data(new SettingSyncData(*it)); | 
|  | std::unique_ptr<SettingSyncDataList>& list_for_extension = | 
|  | as_map[sync_data->extension_id()]; | 
|  | if (!list_for_extension) | 
|  | list_for_extension.reset(new SettingSyncDataList()); | 
|  | list_for_extension->push_back(std::move(sync_data)); | 
|  | } | 
|  | return as_map; | 
|  | } | 
|  |  | 
|  | // This class uses it's TestingValueStore in such a way that it always mints | 
|  | // new TestingValueStore instances. | 
|  | TestingValueStore* GetExisting(const ExtensionId& extension_id) { | 
|  | return static_cast<TestingValueStore*>( | 
|  | storage_factory_->GetExisting(extension_id)); | 
|  | } | 
|  |  | 
|  | template <typename Func> | 
|  | static void RunFunc(Func func) { | 
|  | func(); | 
|  | } | 
|  |  | 
|  | template <typename Func> | 
|  | void PostOnBackendSequenceAndWait(const base::Location& from_here, | 
|  | Func func) { | 
|  | GetBackendTaskRunner()->PostTask( | 
|  | from_here, base::Bind(&ExtensionSettingsSyncTest::RunFunc<Func>, func)); | 
|  | content::RunAllTasksUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Needed so that the DCHECKs for running on FILE or UI threads pass. | 
|  | content::TestBrowserThreadBundle test_browser_thread_bundle_; | 
|  |  | 
|  | base::ScopedTempDir temp_dir_; | 
|  | std::unique_ptr<TestingProfile> profile_; | 
|  | std::unique_ptr<StorageFrontend> frontend_; | 
|  | scoped_refptr<TestValueStoreFactory> storage_factory_; | 
|  | std::unique_ptr<MockSyncChangeProcessor> sync_processor_; | 
|  | std::unique_ptr<syncer::SyncChangeProcessorWrapperForTest> | 
|  | sync_processor_wrapper_; | 
|  | }; | 
|  |  | 
|  | // Get a semblance of coverage for both EXTENSION_SETTINGS and APP_SETTINGS | 
|  | // sync by roughly alternative which one to test. | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, NoDataDoesNotInvokeSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | EXPECT_EQ(0u, GetAllSyncData(model_type).size()); | 
|  | }); | 
|  |  | 
|  | // Have one extension created before sync is set up, the other created after. | 
|  | AddExtensionAndGetStorage("s1", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | EXPECT_EQ(0u, GetAllSyncData(model_type).size()); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | }); | 
|  |  | 
|  | AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | EXPECT_EQ(0u, GetAllSyncData(model_type).size()); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  |  | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  | EXPECT_EQ(0u, GetAllSyncData(model_type).size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | storage2->Set(DEFAULTS, "bar", value2); | 
|  |  | 
|  | SettingSyncDataMultimap all_sync_data = GetAllSyncData(model_type); | 
|  | EXPECT_EQ(2u, all_sync_data.size()); | 
|  | EXPECT_EQ(1u, all_sync_data["s1"]->size()); | 
|  | EXPECT_PRED_FORMAT2(ValuesEq, &value1, &(*all_sync_data["s1"])[0]->value()); | 
|  | EXPECT_EQ(1u, all_sync_data["s2"]->size()); | 
|  | EXPECT_PRED_FORMAT2(ValuesEq, &value2, &(*all_sync_data["s2"])[0]->value()); | 
|  |  | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s1", "foo", value1, model_type)); | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s2", "bar", value2, model_type)); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | // Already in sync, so no changes. | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Regression test: not-changing the synced value shouldn't result in a sync | 
|  | // change, and changing the synced value should result in an update. | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | storage1->Set(DEFAULTS, "foo", value2); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  | SettingSyncData* change = sync_processor_->GetOnlyChange("s1", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | storage2->Set(DEFAULTS, "bar", value2); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | // All settings should have been pushed to sync. | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  | SettingSyncData* change = sync_processor_->GetOnlyChange("s1", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value1.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s2", "bar"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) { | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | // Maintain dictionaries mirrored to the expected values of the settings in | 
|  | // each storage area. | 
|  | base::DictionaryValue expected1, expected2; | 
|  |  | 
|  | // Pre-populate one of the storage areas. | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | storage1->Set(DEFAULTS, "overwriteMe", value1); | 
|  |  | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s1", "foo", value1, model_type)); | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s2", "bar", value2, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | expected1.Set("foo", value1.CreateDeepCopy()); | 
|  | expected2.Set("bar", value2.CreateDeepCopy()); | 
|  | }); | 
|  |  | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | // All changes should be local, so no sync changes. | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Sync settings should have been pushed to local settings. | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | // Make storage1 initialised from local data, storage2 initialised from sync. | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | // Maintain dictionaries mirrored to the expected values of the settings in | 
|  | // each storage area. | 
|  | base::DictionaryValue expected1, expected2; | 
|  |  | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | expected1.Set("foo", value1.CreateDeepCopy()); | 
|  |  | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s2", "bar", value2, model_type)); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | expected2.Set("bar", value2.CreateDeepCopy()); | 
|  |  | 
|  | // Make sync add some settings. | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("s1", "bar", value2, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("s2", "foo", value1, model_type)); | 
|  | GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | expected1.Set("bar", value2.CreateDeepCopy()); | 
|  | expected2.Set("foo", value1.CreateDeepCopy()); | 
|  |  | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); | 
|  |  | 
|  | // Make sync update some settings, storage1 the new setting, storage2 the | 
|  | // initial setting. | 
|  | change_list.clear(); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateUpdate("s1", "bar", value2, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateUpdate("s2", "bar", value1, model_type)); | 
|  | GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | expected1.Set("bar", value2.CreateDeepCopy()); | 
|  | expected2.Set("bar", value1.CreateDeepCopy()); | 
|  |  | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); | 
|  |  | 
|  | // Make sync remove some settings, storage1 the initial setting, storage2 | 
|  | // the new setting. | 
|  | change_list.clear(); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateDelete("s1", "foo", model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateDelete("s2", "foo", model_type)); | 
|  | GetSyncableService(model_type)->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | expected1.Remove("foo", NULL); | 
|  | expected2.Remove("foo", NULL); | 
|  |  | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, PushToSync) { | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | // Make storage1/2 initialised from local data, storage3/4 initialised from | 
|  | // sync. | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  | ValueStore* storage3 = AddExtensionAndGetStorage("s3", type); | 
|  | ValueStore* storage4 = AddExtensionAndGetStorage("s4", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | storage2->Set(DEFAULTS, "foo", value1); | 
|  |  | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s3", "bar", value2, model_type)); | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("s4", "bar", value2, model_type)); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | // Add something locally. | 
|  | storage1->Set(DEFAULTS, "bar", value2); | 
|  | storage2->Set(DEFAULTS, "bar", value2); | 
|  | storage3->Set(DEFAULTS, "foo", value1); | 
|  | storage4->Set(DEFAULTS, "foo", value1); | 
|  |  | 
|  | SettingSyncData* change = sync_processor_->GetOnlyChange("s1", "bar"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  | sync_processor_->GetOnlyChange("s2", "bar"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s3", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value1.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s4", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type()); | 
|  | EXPECT_TRUE(value1.Equals(&change->value())); | 
|  |  | 
|  | // Change something locally, storage1/3 the new setting and storage2/4 the | 
|  | // initial setting, for all combinations of local vs sync intialisation and | 
|  | // new vs initial. | 
|  | sync_processor_->ClearChanges(); | 
|  | storage1->Set(DEFAULTS, "bar", value1); | 
|  | storage2->Set(DEFAULTS, "foo", value2); | 
|  | storage3->Set(DEFAULTS, "bar", value1); | 
|  | storage4->Set(DEFAULTS, "foo", value2); | 
|  |  | 
|  | change = sync_processor_->GetOnlyChange("s1", "bar"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type()); | 
|  | EXPECT_TRUE(value1.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s2", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s3", "bar"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type()); | 
|  | EXPECT_TRUE(value1.Equals(&change->value())); | 
|  | change = sync_processor_->GetOnlyChange("s4", "foo"); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type()); | 
|  | EXPECT_TRUE(value2.Equals(&change->value())); | 
|  |  | 
|  | // Remove something locally, storage1/3 the new setting and storage2/4 the | 
|  | // initial setting, for all combinations of local vs sync intialisation and | 
|  | // new vs initial. | 
|  | sync_processor_->ClearChanges(); | 
|  | storage1->Remove("foo"); | 
|  | storage2->Remove("bar"); | 
|  | storage3->Remove("foo"); | 
|  | storage4->Remove("bar"); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s1", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s2", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s3", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s4", "bar")->change_type()); | 
|  |  | 
|  | // Remove some nonexistent settings. | 
|  | sync_processor_->ClearChanges(); | 
|  | storage1->Remove("foo"); | 
|  | storage2->Remove("bar"); | 
|  | storage3->Remove("foo"); | 
|  | storage4->Remove("bar"); | 
|  |  | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Clear the rest of the settings.  Add the removed ones back first so that | 
|  | // more than one setting is cleared. | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | storage2->Set(DEFAULTS, "bar", value2); | 
|  | storage3->Set(DEFAULTS, "foo", value1); | 
|  | storage4->Set(DEFAULTS, "bar", value2); | 
|  |  | 
|  | sync_processor_->ClearChanges(); | 
|  | storage1->Clear(); | 
|  | storage2->Clear(); | 
|  | storage3->Clear(); | 
|  | storage4->Clear(); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s1", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s1", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s2", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s2", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s3", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s3", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s4", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, | 
|  | sync_processor_->GetOnlyChange("s4", "bar")->change_type()); | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { | 
|  | base::Value value1("fooValue"); | 
|  | base::ListValue value2; | 
|  | value2.AppendString("barValue"); | 
|  |  | 
|  | // storage1 is an extension, storage2 is an app. | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage( | 
|  | "s1", Manifest::TYPE_EXTENSION); | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage( | 
|  | "s2", Manifest::TYPE_LEGACY_PACKAGED_APP); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | storage1->Set(DEFAULTS, "foo", value1); | 
|  | storage2->Set(DEFAULTS, "bar", value2); | 
|  |  | 
|  | SettingSyncDataMultimap extension_sync_data = | 
|  | GetAllSyncData(syncer::EXTENSION_SETTINGS); | 
|  | EXPECT_EQ(1u, extension_sync_data.size()); | 
|  | EXPECT_EQ(1u, extension_sync_data["s1"]->size()); | 
|  | EXPECT_PRED_FORMAT2(ValuesEq, &value1, | 
|  | &(*extension_sync_data["s1"])[0]->value()); | 
|  |  | 
|  | SettingSyncDataMultimap app_sync_data = | 
|  | GetAllSyncData(syncer::APP_SETTINGS); | 
|  | EXPECT_EQ(1u, app_sync_data.size()); | 
|  | EXPECT_EQ(1u, app_sync_data["s2"]->size()); | 
|  | EXPECT_PRED_FORMAT2(ValuesEq, &value2, &(*app_sync_data["s2"])[0]->value()); | 
|  |  | 
|  | // Stop each separately, there should be no changes either time. | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back(settings_sync_util::CreateData( | 
|  | "s1", "foo", value1, syncer::EXTENSION_SETTINGS)); | 
|  |  | 
|  | GetSyncableService(syncer::EXTENSION_SETTINGS) | 
|  | ->MergeDataAndStartSyncing( | 
|  | syncer::EXTENSION_SETTINGS, sync_data, | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | GetSyncableService(syncer::EXTENSION_SETTINGS) | 
|  | ->StopSyncing(syncer::EXTENSION_SETTINGS); | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | sync_data.clear(); | 
|  | sync_data.push_back(settings_sync_util::CreateData("s2", "bar", value2, | 
|  | syncer::APP_SETTINGS)); | 
|  |  | 
|  | std::unique_ptr<syncer::SyncChangeProcessorWrapperForTest> | 
|  | app_settings_delegate_(new syncer::SyncChangeProcessorWrapperForTest( | 
|  | sync_processor_.get())); | 
|  | GetSyncableService(syncer::APP_SETTINGS) | 
|  | ->MergeDataAndStartSyncing( | 
|  | syncer::APP_SETTINGS, sync_data, std::move(app_settings_delegate_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | GetSyncableService(syncer::APP_SETTINGS)->StopSyncing(syncer::APP_SETTINGS); | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailingStartSyncingDisablesSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | // There is a bit of a convoluted method to get storage areas that can fail; | 
|  | // hand out TestingValueStore object then toggle them failing/succeeding | 
|  | // as necessary. | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | // Make bad fail for incoming sync changes. | 
|  | GetExisting("bad")->set_status_code(ValueStore::CORRUPTION); | 
|  | { | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("good", "foo", fooValue, model_type)); | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("bad", "foo", fooValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | } | 
|  | GetExisting("bad")->set_status_code(ValueStore::OK); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Changes made to good should be sent to sync, changes from bad shouldn't. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", barValue); | 
|  | bad->Set(DEFAULTS, "bar", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Changes received from sync should go to good but not bad (even when it's | 
|  | // not failing). | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back(settings_sync_util::CreateUpdate( | 
|  | "good", "foo", barValue, model_type)); | 
|  | // (Sending UPDATE here even though it's adding, since that's what the | 
|  | // state of sync is.  In any case, it won't work.) | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateUpdate("bad", "foo", barValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Changes made to bad still shouldn't go to sync, even though it didn't | 
|  | // fail last time. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", fooValue); | 
|  | bad->Set(DEFAULTS, "bar", fooValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Failing ProcessSyncChanges shouldn't go to the storage. | 
|  | GetExisting("bad")->set_status_code(ValueStore::CORRUPTION); | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back(settings_sync_util::CreateUpdate( | 
|  | "good", "foo", fooValue, model_type)); | 
|  | // (Ditto.) | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateUpdate("bad", "foo", fooValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  | GetExisting("bad")->set_status_code(ValueStore::OK); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Restarting sync should make bad start syncing again. | 
|  | sync_processor_->ClearChanges(); | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | sync_processor_wrapper_.reset( | 
|  | new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get())); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | // Local settings will have been pushed to sync, since it's empty (in this | 
|  | // test; presumably it wouldn't be live, since we've been getting changes). | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "bar")->change_type()); | 
|  | EXPECT_EQ(3u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Live local changes now get pushed, too. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", barValue); | 
|  | bad->Set(DEFAULTS, "bar", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("bad", "bar")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  |  | 
|  | // And ProcessSyncChanges work, too. | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back(settings_sync_util::CreateUpdate( | 
|  | "good", "bar", fooValue, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateUpdate("bad", "bar", fooValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("bar", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailingProcessChangesDisablesSync) { | 
|  | // The test above tests a failing ProcessSyncChanges too, but here test with | 
|  | // an initially passing MergeDataAndStartSyncing. | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | // Unlike before, initially succeeding MergeDataAndStartSyncing. | 
|  | { | 
|  | syncer::SyncDataList sync_data; | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("good", "foo", fooValue, model_type)); | 
|  | sync_data.push_back( | 
|  | settings_sync_util::CreateData("bad", "foo", fooValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Now fail ProcessSyncChanges for bad. | 
|  | GetExisting("bad")->set_status_code(ValueStore::CORRUPTION); | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("good", "bar", barValue, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("bad", "bar", barValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  | GetExisting("bad")->set_status_code(ValueStore::OK); | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // No more changes sent to sync for bad. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "foo", barValue); | 
|  | bad->Set(DEFAULTS, "foo", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | // No more changes received from sync should go to bad. | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("good", "foo", fooValue, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("bad", "foo", fooValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailingGetAllSyncDataDoesntStopSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | good->Set(DEFAULTS, "foo", fooValue); | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  |  | 
|  | // Even though bad will fail to get all sync data, sync data should still | 
|  | // include that from good. | 
|  | GetExisting("bad")->set_status_code(ValueStore::CORRUPTION); | 
|  | { | 
|  | syncer::SyncDataList all_sync_data = | 
|  | GetSyncableService(model_type)->GetAllSyncData(model_type); | 
|  | EXPECT_EQ(1u, all_sync_data.size()); | 
|  | EXPECT_EQ("good/foo", syncer::SyncDataLocal(all_sync_data[0]).GetTag()); | 
|  | } | 
|  | GetExisting("bad")->set_status_code(ValueStore::OK); | 
|  |  | 
|  | // Sync shouldn't be disabled for good (nor bad -- but this is unimportant). | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "foo")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  |  | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", barValue); | 
|  | bad->Set(DEFAULTS, "bar", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "bar")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailureToReadChangesToPushDisablesSync) { | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | good->Set(DEFAULTS, "foo", fooValue); | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  |  | 
|  | // good will successfully push foo:fooValue to sync, but bad will fail to | 
|  | // get them so won't. | 
|  | GetExisting("bad")->set_status_code(ValueStore::CORRUPTION); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | GetExisting("bad")->set_status_code(ValueStore::OK); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | // bad should now be disabled for sync. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", barValue); | 
|  | bad->Set(DEFAULTS, "bar", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back(settings_sync_util::CreateUpdate( | 
|  | "good", "foo", barValue, model_type)); | 
|  | // (Sending ADD here even though it's updating, since that's what the | 
|  | // state of sync is.  In any case, it won't work.) | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("bad", "foo", barValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", fooValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Re-enabling sync without failing should cause the local changes from bad | 
|  | // to be pushed to sync successfully, as should future changes to bad. | 
|  | sync_processor_->ClearChanges(); | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | sync_processor_wrapper_.reset( | 
|  | new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get())); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "bar")->change_type()); | 
|  | EXPECT_EQ(4u, sync_processor_->changes().size()); | 
|  |  | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "bar", fooValue); | 
|  | bad->Set(DEFAULTS, "bar", fooValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalStateDisablesSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | // Only set bad; setting good will cause it to fail below. | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  |  | 
|  | sync_processor_->set_fail_all_requests(true); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | sync_processor_->set_fail_all_requests(false); | 
|  |  | 
|  | // Changes from good will be send to sync, changes from bad won't. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "foo", barValue); | 
|  | bad->Set(DEFAULTS, "foo", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Changes from sync will be sent to good, not to bad. | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("good", "bar", barValue, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("bad", "bar", barValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Restarting sync makes everything work again. | 
|  | sync_processor_->ClearChanges(); | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | sync_processor_wrapper_.reset( | 
|  | new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get())); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "foo")->change_type()); | 
|  | EXPECT_EQ(3u, sync_processor_->changes().size()); | 
|  |  | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "foo", fooValue); | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalChangeDisablesSync) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | base::Value fooValue("fooValue"); | 
|  | base::Value barValue("barValue"); | 
|  |  | 
|  | ValueStore* good = AddExtensionAndGetStorage("good", type); | 
|  | ValueStore* bad = AddExtensionAndGetStorage("bad", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | // bad will fail to send changes. | 
|  | good->Set(DEFAULTS, "foo", fooValue); | 
|  | sync_processor_->set_fail_all_requests(true); | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  | sync_processor_->set_fail_all_requests(false); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | // No further changes should be sent from bad. | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "foo", barValue); | 
|  | bad->Set(DEFAULTS, "foo", barValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(1u, sync_processor_->changes().size()); | 
|  |  | 
|  | // Changes from sync will be sent to good, not to bad. | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("good", "bar", barValue, model_type)); | 
|  | change_list.push_back( | 
|  | settings_sync_util::CreateAdd("bad", "bar", barValue, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | dict.Set("bar", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get()); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue dict; | 
|  | dict.Set("foo", barValue.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get()); | 
|  | } | 
|  |  | 
|  | // Restarting sync makes everything work again. | 
|  | sync_processor_->ClearChanges(); | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | sync_processor_wrapper_.reset( | 
|  | new syncer::SyncChangeProcessorWrapperForTest(sync_processor_.get())); | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("good", "bar")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, | 
|  | sync_processor_->GetOnlyChange("bad", "foo")->change_type()); | 
|  | EXPECT_EQ(3u, sync_processor_->changes().size()); | 
|  |  | 
|  | sync_processor_->ClearChanges(); | 
|  | good->Set(DEFAULTS, "foo", fooValue); | 
|  | bad->Set(DEFAULTS, "foo", fooValue); | 
|  |  | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | 
|  | sync_processor_->GetOnlyChange("good", "foo")->change_type()); | 
|  | EXPECT_EQ(2u, sync_processor_->changes().size()); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, | 
|  | LargeOutgoingChangeRejectedButIncomingAccepted) { | 
|  | syncer::ModelType model_type = syncer::APP_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_LEGACY_PACKAGED_APP; | 
|  |  | 
|  | // This value should be larger than the limit in sync_storage_backend.cc. | 
|  | std::string string_10k; | 
|  | for (size_t i = 0; i < 10000; ++i) { | 
|  | string_10k.append("a"); | 
|  | } | 
|  | base::Value large_value(string_10k); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, syncer::SyncDataList(), | 
|  | std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | }); | 
|  |  | 
|  | // Large local change rejected and doesn't get sent out. | 
|  | ValueStore* storage1 = AddExtensionAndGetStorage("s1", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | EXPECT_FALSE( | 
|  | storage1->Set(DEFAULTS, "large_value", large_value).status().ok()); | 
|  | EXPECT_EQ(0u, sync_processor_->changes().size()); | 
|  | }); | 
|  |  | 
|  | // Large incoming change should still get accepted. | 
|  | ValueStore* storage2 = AddExtensionAndGetStorage("s2", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | { | 
|  | syncer::SyncChangeList change_list; | 
|  | change_list.push_back(settings_sync_util::CreateAdd( | 
|  | "s1", "large_value", large_value, model_type)); | 
|  | change_list.push_back(settings_sync_util::CreateAdd( | 
|  | "s2", "large_value", large_value, model_type)); | 
|  | GetSyncableService(model_type) | 
|  | ->ProcessSyncChanges(FROM_HERE, change_list); | 
|  | } | 
|  | { | 
|  | base::DictionaryValue expected; | 
|  | expected.Set("large_value", large_value.CreateDeepCopy()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected, storage1->Get()); | 
|  | EXPECT_PRED_FORMAT2(SettingsEq, expected, storage2->Get()); | 
|  | } | 
|  |  | 
|  | GetSyncableService(model_type)->StopSyncing(model_type); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionSettingsSyncTest, Dots) { | 
|  | syncer::ModelType model_type = syncer::EXTENSION_SETTINGS; | 
|  | Manifest::Type type = Manifest::TYPE_EXTENSION; | 
|  |  | 
|  | ValueStore* storage = AddExtensionAndGetStorage("ext", type); | 
|  |  | 
|  | PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() { | 
|  | { | 
|  | syncer::SyncDataList sync_data_list; | 
|  | std::unique_ptr<base::Value> string_value(new base::Value("value")); | 
|  | sync_data_list.push_back(settings_sync_util::CreateData( | 
|  | "ext", "key.with.dot", *string_value, model_type)); | 
|  |  | 
|  | GetSyncableService(model_type) | 
|  | ->MergeDataAndStartSyncing( | 
|  | model_type, sync_data_list, std::move(sync_processor_wrapper_), | 
|  | base::MakeUnique<syncer::SyncErrorFactoryMock>()); | 
|  | } | 
|  |  | 
|  | // Test dots in keys that come from sync. | 
|  | { | 
|  | ValueStore::ReadResult data = storage->Get(); | 
|  | ASSERT_TRUE(data.status().ok()); | 
|  |  | 
|  | base::DictionaryValue expected_data; | 
|  | expected_data.SetWithoutPathExpansion( | 
|  | "key.with.dot", base::MakeUnique<base::Value>("value")); | 
|  | EXPECT_EQ(expected_data, data.settings()); | 
|  | } | 
|  |  | 
|  | // Test dots in keys going to sync. | 
|  | { | 
|  | std::unique_ptr<base::Value> string_value(new base::Value("spot")); | 
|  | storage->Set(DEFAULTS, "key.with.spot", *string_value); | 
|  |  | 
|  | ASSERT_EQ(1u, sync_processor_->changes().size()); | 
|  | SettingSyncData* sync_data = sync_processor_->changes()[0].get(); | 
|  | EXPECT_EQ(syncer::SyncChange::ACTION_ADD, sync_data->change_type()); | 
|  | EXPECT_EQ("ext", sync_data->extension_id()); | 
|  | EXPECT_EQ("key.with.spot", sync_data->key()); | 
|  | EXPECT_TRUE(sync_data->value().Equals(string_value.get())); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | // In other (frontend) tests, we assume that the result of GetStorage | 
|  | // is a pointer to the a Storage owned by a Frontend object, but for | 
|  | // the unlimitedStorage case, this might not be true. So, write the | 
|  | // tests in a "callback" style.  We should really rewrite all tests to | 
|  | // be asynchronous in this way. | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) { | 
|  | // Sync storage should still run out after ~100K; the unlimitedStorage | 
|  | // permission can't apply to sync. | 
|  | std::unique_ptr<base::Value> kilobyte = util::CreateKilobyte(); | 
|  | for (int i = 0; i < 100; ++i) { | 
|  | sync_storage->Set(ValueStore::DEFAULTS, base::IntToString(i), *kilobyte); | 
|  | } | 
|  |  | 
|  | EXPECT_FALSE(sync_storage->Set(ValueStore::DEFAULTS, "WillError", *kilobyte) | 
|  | .status() | 
|  | .ok()); | 
|  | } | 
|  |  | 
|  | static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) { | 
|  | // Local storage should never run out. | 
|  | std::unique_ptr<base::Value> megabyte = util::CreateMegabyte(); | 
|  | for (int i = 0; i < 7; ++i) { | 
|  | local_storage->Set(ValueStore::DEFAULTS, base::IntToString(i), *megabyte); | 
|  | } | 
|  |  | 
|  | EXPECT_TRUE(local_storage->Set(ValueStore::DEFAULTS, "WontError", *megabyte) | 
|  | .status() | 
|  | .ok()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | // See: http://crbug.com/227296 | 
|  | #define MAYBE_UnlimitedStorageForLocalButNotSync \ | 
|  | DISABLED_UnlimitedStorageForLocalButNotSync | 
|  | #else | 
|  | #define MAYBE_UnlimitedStorageForLocalButNotSync \ | 
|  | UnlimitedStorageForLocalButNotSync | 
|  | #endif | 
|  | TEST_F(ExtensionSettingsSyncTest, MAYBE_UnlimitedStorageForLocalButNotSync) { | 
|  | const std::string id = "ext"; | 
|  | std::set<std::string> permissions; | 
|  | permissions.insert("unlimitedStorage"); | 
|  | scoped_refptr<const Extension> extension = | 
|  | util::AddExtensionWithIdAndPermissions( | 
|  | profile_.get(), id, Manifest::TYPE_EXTENSION, permissions); | 
|  |  | 
|  | frontend_->RunWithStorage(extension, | 
|  | settings_namespace::SYNC, | 
|  | base::Bind(&UnlimitedSyncStorageTestCallback)); | 
|  | frontend_->RunWithStorage(extension, | 
|  | settings_namespace::LOCAL, | 
|  | base::Bind(&UnlimitedLocalStorageTestCallback)); | 
|  |  | 
|  | content::RunAllTasksUntilIdle(); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |