// Copyright 2012 The Chromium Authors
// 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/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.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_storage_backend.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/test/sync_change_processor_wrapper_for_test.h"
#include "components/value_store/test_value_store_factory.h"
#include "components/value_store/testing_value_store.h"
#include "content/public/test/browser_task_environment.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/api/storage/value_store_util.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/buildflags/buildflags.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest.h"
#include "testing/gtest/include/gtest/gtest.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

using value_store::ValueStore;

namespace extensions {

namespace {

// To save typing ValueStore::DEFAULTS everywhere.
const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS;

// Valid extension IDs for use as test data.
constexpr char kId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
constexpr char kId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
constexpr char kId3[] = "cccccccccccccccccccccccccccccccc";
constexpr char kId4[] = "dddddddddddddddddddddddddddddddd";
constexpr char kId5[] = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";

// More saving typing. Maps extension IDs to a list of sync changes for that
// extension.
using SettingSyncDataMultimap =
    std::map<ExtensionId, 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 != *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::DictValue& expected,
                                    ValueStore::ReadResult actual) {
  if (!actual.status().ok()) {
    return testing::AssertionFailure()
           << "Expected: " << expected
           << ", actual has error: " << actual.status().message;
  }
  base::Value settings(actual.PassSettings());
  base::Value expected_value(expected.Clone());
  return ValuesEq(_1, _2, &expected_value, &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.
  std::optional<syncer::ModelError> ProcessSyncChanges(
      const base::Location& from_here,
      const syncer::SyncChangeList& change_list) override {
    if (fail_all_requests_) {
      return syncer::ModelError(FROM_HERE,
                                syncer::ModelError::Type::kGenericTestError);
    }
    for (const auto& sync_change : change_list) {
      changes_.push_back(std::make_unique<SettingSyncData>(sync_change));
    }
    return std::nullopt;
  }

  // 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 ExtensionId& 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 std::make_unique<MockExtensionSystem>(context);
}

std::unique_ptr<KeyedService> BuildEventRouter(
    content::BrowserContext* profile) {
  return std::make_unique<extensions::EventRouter>(profile, nullptr);
}

}  // namespace

class ExtensionSettingsSyncTest : public testing::Test {
 public:
  ExtensionSettingsSyncTest()
      : storage_factory_(new value_store::TestValueStoreFactory()),
        sync_processor_(new MockSyncChangeProcessor),
        sync_processor_wrapper_(new syncer::SyncChangeProcessorWrapperForTest(
            sync_processor_.get())) {}

  void SetUp() override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    TestingProfile::Builder profile_builder;
    profile_builder.AddTestingFactory(
        ExtensionsBrowserClient::Get()->GetExtensionSystemFactory(),
        base::BindRepeating(&MockExtensionSystemFactoryFunction));
    profile_builder.AddTestingFactory(EventRouterFactory::GetInstance(),
                                      base::BindRepeating(&BuildEventRouter));

    profile_builder.SetPath(temp_dir_.GetPath());
    profile_ = profile_builder.Build();

    content::RunAllTasksUntilIdle();

    storage_factory_->Reset();
    frontend_ =
        StorageFrontend::CreateForTesting(storage_factory_, profile_.get());

    // Hold a pointer to SyncValueStoreCache in the main thread, such that
    // GetSyncableService() can be called from the backend sequence.
    sync_cache_ = static_cast<SyncValueStoreCache*>(
        frontend_->GetValueStoreCache(settings_namespace::SYNC));
    ASSERT_NE(sync_cache_, nullptr);
  }

  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 ExtensionId& id,
                                        Manifest::Type type) {
    scoped_refptr<const Extension> extension =
        settings_test_util::AddExtensionWithId(profile_.get(), id, type);
    return settings_test_util::GetStorage(extension, frontend_.get());
  }

  // Gets the syncer::SyncableService for the given sync type.
  SyncStorageBackend* GetSyncableService(syncer::DataType data_type) {
    // SyncValueStoreCache::GetSyncableService internally enforces |data_type|
    // to be APP_SETTINGS or EXTENSION_SETTINGS, and the dynamic type of the
    // returned service is always SyncStorageBackend, so it can be downcast.
    DCHECK(data_type == syncer::APP_SETTINGS ||
           data_type == syncer::EXTENSION_SETTINGS);
    return static_cast<SyncStorageBackend*>(
        sync_cache_->GetSyncableService(data_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::DataType data_type) {
    syncer::SyncDataList as_list =
        GetSyncableService(data_type)->GetAllSyncDataForTesting(data_type);
    SettingSyncDataMultimap as_map;
    for (auto& data : as_list) {
      std::unique_ptr<SettingSyncData> sync_data(new SettingSyncData(data));
      std::unique_ptr<SettingSyncDataList>& list_for_extension =
          as_map[sync_data->extension_id()];
      if (!list_for_extension) {
        list_for_extension = std::make_unique<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.
  value_store::TestingValueStore* GetExisting(const ExtensionId& extension_id,
                                              syncer::DataType type) {
    base::FilePath value_store_dir;
    value_store_util::ModelType data_type;
    switch (type) {
      case syncer::APP_SETTINGS:
        data_type = value_store_util::ModelType::APP;
        break;
      case syncer::EXTENSION_SETTINGS:
        data_type = value_store_util::ModelType::EXTENSION;
        break;
      default:
        NOTREACHED();
    }
    value_store_dir = value_store_util::GetValueStoreDir(
        settings_namespace::SYNC, data_type, extension_id);
    return static_cast<value_store::TestingValueStore*>(
        storage_factory_->GetExisting(value_store_dir));
  }

  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::BindOnce(&ExtensionSettingsSyncTest::RunFunc<Func>, func));
    content::RunAllTasksUntilIdle();
  }

  // Needed so that the DCHECKs for running on FILE or UI threads pass.
  content::BrowserTaskEnvironment task_environment_;

  base::ScopedTempDir temp_dir_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<StorageFrontend> frontend_;
  scoped_refptr<value_store::TestValueStoreFactory> storage_factory_;
  std::unique_ptr<MockSyncChangeProcessor> sync_processor_;
  std::unique_ptr<syncer::SyncChangeProcessorWrapperForTest>
      sync_processor_wrapper_;
  raw_ptr<SyncValueStoreCache, DanglingUntriaged> sync_cache_;
};

// 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::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    EXPECT_EQ(0u, GetAllSyncData(data_type).size());
  });

  // Have one extension created before sync is set up, the other created after.
  AddExtensionAndGetStorage(kId1, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    EXPECT_EQ(0u, GetAllSyncData(data_type).size());

    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));
  });

  AddExtensionAndGetStorage(kId2, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    EXPECT_EQ(0u, GetAllSyncData(data_type).size());

    GetSyncableService(data_type)->StopSyncing(data_type);

    EXPECT_EQ(0u, sync_processor_->changes().size());
    EXPECT_EQ(0u, GetAllSyncData(data_type).size());
  });
}

TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) {
  syncer::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, type);
  ValueStore* storage2 = AddExtensionAndGetStorage(kId2, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    storage1->Set(DEFAULTS, "foo", value1);
    storage2->Set(DEFAULTS, "bar", value2);

    SettingSyncDataMultimap all_sync_data = GetAllSyncData(data_type);
    EXPECT_EQ(2u, all_sync_data.size());
    EXPECT_EQ(1u, all_sync_data[kId1]->size());
    EXPECT_PRED_FORMAT2(ValuesEq, &value1, &(*all_sync_data[kId1])[0]->value());
    EXPECT_EQ(1u, all_sync_data[kId2]->size());
    EXPECT_PRED_FORMAT2(ValuesEq, &value2, &(*all_sync_data[kId2])[0]->value());

    syncer::SyncDataList sync_data;
    sync_data.push_back(
        settings_sync_util::CreateData(kId1, "foo", value1, data_type));
    sync_data.push_back(
        settings_sync_util::CreateData(kId2, "bar", value2, data_type));

    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, sync_data, std::move(sync_processor_wrapper_));

    // 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(kId1, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type());
    EXPECT_EQ(value2, change->value());

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, type);
  ValueStore* storage2 = AddExtensionAndGetStorage(kId2, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    storage1->Set(DEFAULTS, "foo", value1);
    storage2->Set(DEFAULTS, "bar", value2);

    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    // All settings should have been pushed to sync.
    EXPECT_EQ(2u, sync_processor_->changes().size());
    SettingSyncData* change = sync_processor_->GetOnlyChange(kId1, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value1, change->value());
    change = sync_processor_->GetOnlyChange(kId2, "bar");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value2, change->value());

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) {
  syncer::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  // Maintain dictionaries mirrored to the expected values of the settings in
  // each storage area.
  base::DictValue expected1, expected2;

  // Pre-populate one of the storage areas.
  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    storage1->Set(DEFAULTS, "overwriteMe", value1);

    syncer::SyncDataList sync_data;
    sync_data.push_back(
        settings_sync_util::CreateData(kId1, "foo", value1, data_type));
    sync_data.push_back(
        settings_sync_util::CreateData(kId2, "bar", value2, data_type));
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, sync_data, std::move(sync_processor_wrapper_));
    expected1.Set("foo", value1.Clone());
    expected2.Set("bar", value2.Clone());
  });

  ValueStore* storage2 = AddExtensionAndGetStorage(kId2, 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(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  // Make storage1 initialised from local data, storage2 initialised from sync.
  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, type);
  ValueStore* storage2 = AddExtensionAndGetStorage(kId2, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    // Maintain dictionaries mirrored to the expected values of the settings in
    // each storage area.
    base::DictValue expected1, expected2;

    storage1->Set(DEFAULTS, "foo", value1);
    expected1.Set("foo", value1.Clone());

    syncer::SyncDataList sync_data;
    sync_data.push_back(
        settings_sync_util::CreateData(kId2, "bar", value2, data_type));

    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, sync_data, std::move(sync_processor_wrapper_));
    expected2.Set("bar", value2.Clone());

    // Make sync add some settings.
    syncer::SyncChangeList change_list;
    change_list.push_back(
        settings_sync_util::CreateAdd(kId1, "bar", value2, data_type));
    change_list.push_back(
        settings_sync_util::CreateAdd(kId2, "foo", value1, data_type));
    GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    expected1.Set("bar", value2.Clone());
    expected2.Set("foo", value1.Clone());

    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(kId1, "bar", value2, data_type));
    change_list.push_back(
        settings_sync_util::CreateUpdate(kId2, "bar", value1, data_type));
    GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    expected1.Set("bar", value2.Clone());
    expected2.Set("bar", value1.Clone());

    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(kId1, "foo", data_type));
    change_list.push_back(
        settings_sync_util::CreateDelete(kId2, "foo", data_type));
    GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    expected1.Remove("foo");
    expected2.Remove("foo");

    EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get());
    EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get());

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, PushToSync) {
  syncer::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  // Make storage1/2 initialised from local data, storage3/4 initialised from
  // sync.
  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, type);
  ValueStore* storage2 = AddExtensionAndGetStorage(kId2, type);
  ValueStore* storage3 = AddExtensionAndGetStorage(kId3, type);
  ValueStore* storage4 = AddExtensionAndGetStorage(kId4, 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(kId3, "bar", value2, data_type));
    sync_data.push_back(
        settings_sync_util::CreateData(kId4, "bar", value2, data_type));

    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, sync_data, std::move(sync_processor_wrapper_));

    // 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(kId1, "bar");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value2, change->value());
    sync_processor_->GetOnlyChange(kId2, "bar");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value2, change->value());
    change = sync_processor_->GetOnlyChange(kId3, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value1, change->value());
    change = sync_processor_->GetOnlyChange(kId4, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_ADD, change->change_type());
    EXPECT_EQ(value1, 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(kId1, "bar");
    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type());
    EXPECT_EQ(value1, change->value());
    change = sync_processor_->GetOnlyChange(kId2, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type());
    EXPECT_EQ(value2, change->value());
    change = sync_processor_->GetOnlyChange(kId3, "bar");
    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type());
    EXPECT_EQ(value1, change->value());
    change = sync_processor_->GetOnlyChange(kId4, "foo");
    EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change->change_type());
    EXPECT_EQ(value2, 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(kId1, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId2, "bar")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId3, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId4, "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(kId1, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId1, "bar")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId2, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId2, "bar")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId3, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId3, "bar")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId4, "foo")->change_type());
    EXPECT_EQ(syncer::SyncChange::ACTION_DELETE,
              sync_processor_->GetOnlyChange(kId4, "bar")->change_type());

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) {
  base::Value value1("fooValue");
  base::Value value2(base::Value::Type::LIST);
  value2.GetList().Append("barValue");

  // storage1 is an extension, storage2 is an app.
  ValueStore* storage1 =
      AddExtensionAndGetStorage(kId1, Manifest::Type::kExtension);
  ValueStore* storage2 =
      AddExtensionAndGetStorage(kId2, Manifest::Type::kLegacyPackagedApp);

  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[kId1]->size());
    EXPECT_PRED_FORMAT2(ValuesEq, &value1,
                        &(*extension_sync_data[kId1])[0]->value());

    SettingSyncDataMultimap app_sync_data =
        GetAllSyncData(syncer::APP_SETTINGS);
    EXPECT_EQ(1u, app_sync_data.size());
    EXPECT_EQ(1u, app_sync_data[kId2]->size());
    EXPECT_PRED_FORMAT2(ValuesEq, &value2, &(*app_sync_data[kId2])[0]->value());

    // Stop each separately, there should be no changes either time.
    syncer::SyncDataList sync_data;
    sync_data.push_back(settings_sync_util::CreateData(
        kId1, "foo", value1, syncer::EXTENSION_SETTINGS));

    GetSyncableService(syncer::EXTENSION_SETTINGS)
        ->MergeDataAndStartSyncing(syncer::EXTENSION_SETTINGS, sync_data,
                                   std::move(sync_processor_wrapper_));
    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(kId2, "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_));
    GetSyncableService(syncer::APP_SETTINGS)->StopSyncing(syncer::APP_SETTINGS);
    EXPECT_EQ(0u, sync_processor_->changes().size());
  });
}

TEST_F(ExtensionSettingsSyncTest, FailingStartSyncingDisablesSync) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  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.
  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    // Make bad fail for incoming sync changes.
    GetExisting(kBadExtension, data_type)
        ->set_status_code(ValueStore::CORRUPTION);
    {
      syncer::SyncDataList sync_data;
      sync_data.push_back(settings_sync_util::CreateData(kGoodExtension, "foo",
                                                         fooValue, data_type));
      sync_data.push_back(settings_sync_util::CreateData(kBadExtension, "foo",
                                                         fooValue, data_type));
      GetSyncableService(data_type)->MergeDataAndStartSyncing(
          data_type, sync_data, std::move(sync_processor_wrapper_));
    }
    GetExisting(kBadExtension, data_type)->set_status_code(ValueStore::OK);

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue 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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(1u, sync_processor_->changes().size());

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("bar", barValue.Clone());
      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(
          kGoodExtension, "foo", barValue, data_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(
          kBadExtension, "foo", barValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("bar", barValue.Clone());
      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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(1u, sync_processor_->changes().size());

    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      dict.Set("bar", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("bar", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }

    // Failing ProcessSyncChanges shouldn't go to the storage.
    GetExisting(kBadExtension, data_type)
        ->set_status_code(ValueStore::CORRUPTION);
    {
      syncer::SyncChangeList change_list;
      change_list.push_back(settings_sync_util::CreateUpdate(
          kGoodExtension, "foo", fooValue, data_type));
      // (Ditto.)
      change_list.push_back(settings_sync_util::CreateUpdate(
          kBadExtension, "foo", fooValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }
    GetExisting(kBadExtension, data_type)->set_status_code(ValueStore::OK);

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("bar", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }

    // Restarting sync should make bad start syncing again.
    sync_processor_->ClearChanges();
    GetSyncableService(data_type)->StopSyncing(data_type);
    sync_processor_wrapper_ =
        std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
            sync_processor_.get());
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    // 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(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_UPDATE,
        sync_processor_->GetOnlyChange(kBadExtension, "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(
          kGoodExtension, "bar", fooValue, data_type));
      change_list.push_back(settings_sync_util::CreateUpdate(
          kBadExtension, "bar", fooValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("bar", fooValue.Clone());
      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::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  base::Value fooValue("fooValue");
  base::Value barValue("barValue");

  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    // Unlike before, initially succeeding MergeDataAndStartSyncing.
    {
      syncer::SyncDataList sync_data;
      sync_data.push_back(settings_sync_util::CreateData(kGoodExtension, "foo",
                                                         fooValue, data_type));
      sync_data.push_back(settings_sync_util::CreateData(kBadExtension, "foo",
                                                         fooValue, data_type));
      GetSyncableService(data_type)->MergeDataAndStartSyncing(
          data_type, sync_data, std::move(sync_processor_wrapper_));
    }

    EXPECT_EQ(0u, sync_processor_->changes().size());

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }

    // Now fail ProcessSyncChanges for bad.
    GetExisting(kBadExtension, data_type)
        ->set_status_code(ValueStore::CORRUPTION);
    {
      syncer::SyncChangeList change_list;
      change_list.push_back(settings_sync_util::CreateAdd(kGoodExtension, "bar",
                                                          barValue, data_type));
      change_list.push_back(settings_sync_util::CreateAdd(kBadExtension, "bar",
                                                          barValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }
    GetExisting(kBadExtension, data_type)->set_status_code(ValueStore::OK);

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      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(kGoodExtension, "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(kGoodExtension, "foo",
                                                          fooValue, data_type));
      change_list.push_back(settings_sync_util::CreateAdd(kBadExtension, "foo",
                                                          fooValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }
  });
}

TEST_F(ExtensionSettingsSyncTest, FailingGetAllSyncDataDoesntStopSync) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value fooValue("fooValue");
  base::Value barValue("barValue");

  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, 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(kBadExtension, data_type)
        ->set_status_code(ValueStore::CORRUPTION);
    {
      syncer::SyncDataList all_sync_data =
          GetSyncableService(data_type)->GetAllSyncDataForTesting(data_type);
      EXPECT_EQ(1u, all_sync_data.size());
      EXPECT_EQ(syncer::ClientTagHash::FromUnhashed(
                    data_type, settings_sync_util::ConstructClientTag(
                                   kGoodExtension, "foo")),
                all_sync_data[0].GetClientTagHash());
    }
    GetExisting(kBadExtension, data_type)->set_status_code(ValueStore::OK);

    // Sync shouldn't be disabled for good (nor bad -- but this is unimportant).
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "bar")->change_type());
    EXPECT_EQ(2u, sync_processor_->changes().size());
  });
}

TEST_F(ExtensionSettingsSyncTest, FailureToReadChangesToPushDisablesSync) {
  syncer::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  base::Value fooValue("fooValue");
  base::Value barValue("barValue");

  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, 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(kBadExtension, data_type)
        ->set_status_code(ValueStore::CORRUPTION);
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));
    GetExisting(kBadExtension, data_type)->set_status_code(ValueStore::OK);

    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(1u, sync_processor_->changes().size());

    {
      syncer::SyncChangeList change_list;
      change_list.push_back(settings_sync_util::CreateUpdate(
          kGoodExtension, "foo", barValue, data_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(kBadExtension, "foo",
                                                          barValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", fooValue.Clone());
      dict.Set("bar", barValue.Clone());
      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(data_type)->StopSyncing(data_type);
    sync_processor_wrapper_ =
        std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
            sync_processor_.get());
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "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(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_UPDATE,
        sync_processor_->GetOnlyChange(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(2u, sync_processor_->changes().size());
  });
}

TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalStateDisablesSync) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value fooValue("fooValue");
  base::Value barValue("barValue");

  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, 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(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));
    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(kGoodExtension, "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(kGoodExtension, "bar",
                                                          barValue, data_type));
      change_list.push_back(settings_sync_util::CreateAdd(kBadExtension, "bar",
                                                          barValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }

    // Restarting sync makes everything work again.
    sync_processor_->ClearChanges();
    GetSyncableService(data_type)->StopSyncing(data_type);
    sync_processor_wrapper_ =
        std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
            sync_processor_.get());
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "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(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_UPDATE,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(2u, sync_processor_->changes().size());
  });
}

TEST_F(ExtensionSettingsSyncTest, FailureToPushLocalChangeDisablesSync) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value fooValue("fooValue");
  base::Value barValue("barValue");

  const char* kGoodExtension = kId1;
  const char* kBadExtension = kId2;

  ValueStore* good = AddExtensionAndGetStorage(kGoodExtension, type);
  ValueStore* bad = AddExtensionAndGetStorage(kBadExtension, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    // 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(kGoodExtension, "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(kGoodExtension, "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(kGoodExtension, "bar",
                                                          barValue, data_type));
      change_list.push_back(settings_sync_util::CreateAdd(kBadExtension, "bar",
                                                          barValue, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }

    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      dict.Set("bar", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, good->Get());
    }
    {
      base::DictValue dict;
      dict.Set("foo", barValue.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, dict, bad->Get());
    }

    // Restarting sync makes everything work again.
    sync_processor_->ClearChanges();
    GetSyncableService(data_type)->StopSyncing(data_type);
    sync_processor_wrapper_ =
        std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
            sync_processor_.get());
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));

    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kGoodExtension, "bar")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_ADD,
        sync_processor_->GetOnlyChange(kBadExtension, "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(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(
        syncer::SyncChange::ACTION_UPDATE,
        sync_processor_->GetOnlyChange(kGoodExtension, "foo")->change_type());
    EXPECT_EQ(2u, sync_processor_->changes().size());
  });
}

TEST_F(ExtensionSettingsSyncTest,
       LargeOutgoingChangeRejectedButIncomingAccepted) {
  syncer::DataType data_type = syncer::APP_SETTINGS;
  Manifest::Type type = Manifest::Type::kLegacyPackagedApp;

  // This value should be larger than the limit in sync_storage_backend.cc.
  base::Value large_value(std::string(10000, 'a'));

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, syncer::SyncDataList(), std::move(sync_processor_wrapper_));
  });

  // Large local change rejected and doesn't get sent out.
  ValueStore* storage1 = AddExtensionAndGetStorage(kId1, 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(kId2, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    {
      syncer::SyncChangeList change_list;
      change_list.push_back(settings_sync_util::CreateAdd(
          kId1, "large_value", large_value, data_type));
      change_list.push_back(settings_sync_util::CreateAdd(
          kId2, "large_value", large_value, data_type));
      GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);
    }
    {
      base::DictValue expected;
      expected.Set("large_value", large_value.Clone());
      EXPECT_PRED_FORMAT2(SettingsEq, expected, storage1->Get());
      EXPECT_PRED_FORMAT2(SettingsEq, expected, storage2->Get());
    }

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

TEST_F(ExtensionSettingsSyncTest, Dots) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  ValueStore* storage = AddExtensionAndGetStorage(kId5, 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(
          kId5, "key.with.dot", *string_value, data_type));

      GetSyncableService(data_type)->MergeDataAndStartSyncing(
          data_type, sync_data_list, std::move(sync_processor_wrapper_));
    }

    // Test dots in keys that come from sync.
    {
      ValueStore::ReadResult data = storage->Get();
      ASSERT_TRUE(data.status().ok());

      base::DictValue expected_data;
      expected_data.Set("key.with.dot", 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(kId5, sync_data->extension_id());
      EXPECT_EQ("key.with.spot", sync_data->key());
      EXPECT_EQ(sync_data->value(), *string_value);
    }
  });
}

TEST_F(ExtensionSettingsSyncTest, SyncDataWithInvalidExtensionIdIsIgnored) {
  syncer::DataType data_type = syncer::EXTENSION_SETTINGS;
  Manifest::Type type = Manifest::Type::kExtension;

  base::Value value("fooValue");

  const std::string invalid_ids[] = {"../../escaped", "bad/id", "x",
                                     std::string(32, 'z')};

  ValueStore* storage = AddExtensionAndGetStorage(kId1, type);

  PostOnBackendSequenceAndWait(FROM_HERE, [&, this]() {
    // Initial sync data mixing a valid extension ID with invalid ones.
    syncer::SyncDataList sync_data;
    sync_data.push_back(
        settings_sync_util::CreateData(kId1, "foo", value, data_type));
    for (const auto& id : invalid_ids) {
      sync_data.push_back(
          settings_sync_util::CreateData(id, "foo", value, data_type));
    }
    GetSyncableService(data_type)->MergeDataAndStartSyncing(
        data_type, sync_data, std::move(sync_processor_wrapper_));

    // Data for the valid ID is applied; entries with invalid IDs are dropped
    // and no storage areas are created for them.
    base::DictValue expected;
    expected.Set("foo", value.Clone());
    EXPECT_PRED_FORMAT2(SettingsEq, expected, storage->Get());

    SettingSyncDataMultimap all_sync_data = GetAllSyncData(data_type);
    EXPECT_EQ(1u, all_sync_data.size());
    EXPECT_TRUE(all_sync_data.contains(kId1));
    for (const auto& id : invalid_ids) {
      EXPECT_FALSE(all_sync_data.contains(id)) << id;
    }

    // Incremental sync changes with invalid IDs are also dropped.
    syncer::SyncChangeList change_list;
    for (const auto& id : invalid_ids) {
      change_list.push_back(
          settings_sync_util::CreateAdd(id, "bar", value, data_type));
    }
    GetSyncableService(data_type)->ProcessSyncChanges(FROM_HERE, change_list);

    all_sync_data = GetAllSyncData(data_type);
    EXPECT_EQ(1u, all_sync_data.size());
    for (const auto& id : invalid_ids) {
      EXPECT_FALSE(all_sync_data.contains(id)) << id;
    }

    GetSyncableService(data_type)->StopSyncing(data_type);
  });
}

// 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.
  base::Value kilobyte = settings_test_util::CreateKilobyte();
  for (int i = 0; i < 100; ++i) {
    sync_storage->Set(ValueStore::DEFAULTS, base::NumberToString(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.
  base::Value megabyte = settings_test_util::CreateMegabyte();
  for (int i = 0; i < 7; ++i) {
    local_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i), megabyte);
  }

  EXPECT_TRUE(local_storage->Set(ValueStore::DEFAULTS, "WontError", megabyte)
                  .status()
                  .ok());
}

}  // namespace

TEST_F(ExtensionSettingsSyncTest, UnlimitedStorageForLocalButNotSync) {
  const ExtensionId id = kId5;
  std::set<std::string> permissions;
  permissions.insert("unlimitedStorage");
  scoped_refptr<const Extension> extension =
      settings_test_util::AddExtensionWithIdAndPermissions(
          profile_.get(), id, Manifest::Type::kExtension, permissions);

  frontend_->RunWithStorage(extension, settings_namespace::SYNC,
                            base::BindOnce(&UnlimitedSyncStorageTestCallback));
  frontend_->RunWithStorage(extension, settings_namespace::LOCAL,
                            base::BindOnce(&UnlimitedLocalStorageTestCallback));

  content::RunAllTasksUntilIdle();
}

}  // namespace extensions
