chromium/ash/accelerators/ash_accelerator_configuration_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/accelerators/ash_accelerator_configuration.h"

#include <memory>
#include <string>

#include "ash/accelerators/ash_accelerator_configuration.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/accelerator_configuration.h"
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/accelerators_util.h"
#include "ash/public/mojom/accelerator_configuration.mojom.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/containers/contains.h"
#include "base/ranges/algorithm.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/types/optional_ref.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_codes_array.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"

#include "testing/gtest/include/gtest/gtest.h"

namespace {

using ::ash::mojom::AcceleratorConfigResult;

constexpr char kAcceleratorModifiersKey[] = "modifiers";
constexpr char kAcceleratorKeyCodeKey[] = "key";
constexpr char kAcceleratorModificationActionKey[] = "action";
constexpr char kAcceleratorStateKey[] = "state";
constexpr char kAcceleratorTypeKey[] = "type";
constexpr char kAcceleratorKeyStateKey[] = "key_state";
constexpr char kFakeUserEmail[] = "[email protected]";
constexpr char kFakeUserEmail2[] = "[email protected]";

class UpdatedAcceleratorsObserver
    : public ash::AshAcceleratorConfiguration::Observer {
 public:
  UpdatedAcceleratorsObserver() = default;
  UpdatedAcceleratorsObserver(const UpdatedAcceleratorsObserver&) = delete;
  UpdatedAcceleratorsObserver& operator=(const UpdatedAcceleratorsObserver&) =
      delete;
  ~UpdatedAcceleratorsObserver() override = default;

  // ash::AshAcceleratorConfiguration::Observer:
  void OnAcceleratorsUpdated() override {
    ++num_times_accelerator_updated_called_;
  }

  int num_times_accelerator_updated_called() {
    return num_times_accelerator_updated_called_;
  }

 private:
  int num_times_accelerator_updated_called_ = 0;
};

base::Value AcceleratorModificationDataToValue(
    const ui::Accelerator& accelerator,
    AcceleratorModificationAction action) {
  base::Value::Dict accelerator_values;
  accelerator_values.Set(kAcceleratorModifiersKey, accelerator.modifiers());
  accelerator_values.Set(kAcceleratorKeyCodeKey,
                         static_cast<int>(accelerator.key_code()));
  accelerator_values.Set(
      kAcceleratorTypeKey,
      static_cast<int>(ash::mojom::AcceleratorType::kDefault));
  accelerator_values.Set(
      kAcceleratorStateKey,
      static_cast<int>(ash::mojom::AcceleratorState::kEnabled));
  accelerator_values.Set(kAcceleratorModificationActionKey,
                         static_cast<int>(action));
  return base::Value(std::move(accelerator_values));
}

base::Value::Dict GetOverridePref() {
  return ash::Shell::Get()
      ->session_controller()
      ->GetActivePrefService()
      ->GetDict(ash::prefs::kShortcutCustomizationOverrides)
      .Clone();
}

void SetOverridePref(const ui::Accelerator& accelerator,
                     AcceleratorModificationAction action,
                     uint32_t action_id) {
  base::Value::List override_list;
  override_list.Append(AcceleratorModificationDataToValue(accelerator, action));

  base::Value::Dict overrides;
  overrides.Set(base::NumberToString(action_id), std::move(override_list));
  ash::Shell::Get()->session_controller()->GetActivePrefService()->SetDict(
      ash::prefs::kShortcutCustomizationOverrides, std::move(overrides));
}

AcceleratorModificationData ValueToAcceleratorModificationData(
    const base::Value::Dict& value) {
  std::optional<int> keycode = value.FindInt(kAcceleratorKeyCodeKey);
  std::optional<int> modifier = value.FindInt(kAcceleratorModifiersKey);
  std::optional<int> modification_action =
      value.FindInt(kAcceleratorModificationActionKey);
  std::optional<int> key_state = value.FindInt(kAcceleratorKeyStateKey);
  CHECK(keycode.has_value());
  CHECK(modifier.has_value());
  CHECK(modification_action.has_value());
  CHECK(key_state.has_value());
  ui::Accelerator accelerator(
      static_cast<ui::KeyboardCode>(*keycode), static_cast<int>(*modifier),
      static_cast<ui::Accelerator::KeyState>(*key_state));
  return {accelerator,
          static_cast<AcceleratorModificationAction>(*modification_action)};
}

bool CompareAccelerators(const ash::AcceleratorData& expected_data,
                         const ui::Accelerator& actual_accelerator) {
  ui::Accelerator expected_accel(expected_data.keycode, expected_data.modifiers,
                                 expected_data.trigger_on_press
                                     ? ui::Accelerator::KeyState::PRESSED
                                     : ui::Accelerator::KeyState::RELEASED);
  return expected_accel.key_code() == actual_accelerator.key_code() &&
         expected_accel.modifiers() == actual_accelerator.modifiers() &&
         expected_accel.key_state() == actual_accelerator.key_state();
}

void ExpectAllAcceleratorsEqual(
    const base::span<const ash::AcceleratorData> expected,
    const std::vector<ui::Accelerator>& actual) {
  EXPECT_EQ(std::size(expected), actual.size());

  for (const auto& actual_accelerator : actual) {
    bool found_match = false;
    for (const auto& expected_data : expected) {
      found_match = CompareAccelerators(expected_data, actual_accelerator);
      if (found_match) {
        break;
      }
    }
    EXPECT_TRUE(found_match);
  }
}
}  // namespace

namespace ash {

class AshAcceleratorConfigurationTest : public AshTestBase {
 public:
  AshAcceleratorConfigurationTest() = default;

  ~AshAcceleratorConfigurationTest() override = default;

  void SetUp() override {
    scoped_feature_list_.InitAndEnableFeature(
        ::features::kShortcutCustomization);
    AshTestBase::SetUp();
    config_ = std::make_unique<AshAcceleratorConfiguration>();
    config_->AddObserver(&observer_);
    histogram_tester_ = std::make_unique<base::HistogramTester>();
  }

  void TearDown() override {
    config_->RemoveObserver(&observer_);
    AshTestBase::TearDown();
    histogram_tester_.reset();
  }

 protected:
  base::optional_ref<const std::vector<ui::Accelerator>>
  GetAcceleratorsForAction(AcceleratorActionId action_id) {
    return config_->GetAcceleratorsForAction(action_id);
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  UpdatedAcceleratorsObserver observer_;
  std::unique_ptr<AshAcceleratorConfiguration> config_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

TEST_F(AshAcceleratorConfigurationTest, VerifyAcceleratorMappingPopulated) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {true, ui::VKEY_SPACE, ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowEmojiPicker, true},
      {true, ui::VKEY_EMOJI_PICKER, ui::EF_NONE,
       AcceleratorAction::kShowEmojiPicker, true},
  };

  config_->Initialize(test_data);
  const std::vector<ui::Accelerator>& accelerators =
      config_->GetAllAccelerators();

  ExpectAllAcceleratorsEqual(test_data, accelerators);

  ui::Accelerator accelerator(ui::VKEY_EMOJI_PICKER, ui::EF_NONE);
  EXPECT_TRUE(config_->IsAcceleratorLocked(accelerator));
}

TEST_F(AshAcceleratorConfigurationTest, DeprecatedAccelerators) {
  // Test deprecated accelerators, in this case
  // `AcceleratorAction::kShowTaskManager` has two associated accelerators:
  // (deprecated) ESCAPE + SHIFT and (active) ESCAPE + COMMAND.
  const AcceleratorData initial_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  const AcceleratorData expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowTaskManager},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  const DeprecatedAcceleratorData deprecated_data[] = {
      {AcceleratorAction::kShowTaskManager,
       /*uma_histogram_name=*/"deprecated.showTaskManager",
       /*notification_message_id=*/1,
       /*new_shortcut_id=*/2,
       ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN),
       /*deprecated_enabled=*/true},
  };

  const AcceleratorData test_deprecated_accelerators[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  config_->Initialize(initial_test_data);
  config_->InitializeDeprecatedAccelerators(deprecated_data,
                                            test_deprecated_accelerators);

  const std::vector<ui::Accelerator>& accelerators =
      config_->GetAllAccelerators();

  // When initializing deprecated accelerators, expect them to be added to the
  // overall accelerators list too.
  ExpectAllAcceleratorsEqual(expected_test_data, accelerators);

  // Verify that the fetched deprecated accelerators are correct.
  std::vector<ui::Accelerator> deprecated_accelerators;
  for (const auto& accel : accelerators) {
    if (config_->IsDeprecated(accel)) {
      deprecated_accelerators.push_back(accel);
    }
  }
  ExpectAllAcceleratorsEqual(test_deprecated_accelerators,
                             deprecated_accelerators);

  // Verify ESCAPE + SHIFT is deprecated.
  const ui::Accelerator deprecated_accelerator(ui::VKEY_ESCAPE,
                                               ui::EF_SHIFT_DOWN);
  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator));
  // Verify fetching a deprecated accelerator works.
  EXPECT_EQ(deprecated_data, config_->GetDeprecatedAcceleratorData(
                                 AcceleratorAction::kShowTaskManager));
  // AcceleratorAction::kCycleBackwardMru is not a deprecated action, expect
  // nullptr.
  EXPECT_EQ(nullptr, config_->GetDeprecatedAcceleratorData(
                         AcceleratorAction::kCycleBackwardMru));

  // Verify that ESCAPE + COMMAND is not deprecated.
  const ui::Accelerator active_accelerator(ui::VKEY_ESCAPE,
                                           ui::EF_COMMAND_DOWN);
  EXPECT_FALSE(config_->IsDeprecated(active_accelerator));
}

TEST_F(AshAcceleratorConfigurationTest,
       RemoveAndRestoreDeprecatedAccelerators) {
  // Test deprecated accelerators, in this case
  // `AcceleratorAction::kShowTaskManager` has two associated accelerators:
  // (deprecated) ESCAPE + SHIFT and (active) ESCAPE + COMMAND.
  const AcceleratorData initial_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  const AcceleratorData expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowTaskManager},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  const DeprecatedAcceleratorData deprecated_data[] = {
      {AcceleratorAction::kShowTaskManager,
       /*uma_histogram_name=*/"deprecated.showTaskManager",
       /*notification_message_id=*/1,
       /*new_shortcut_id=*/2,
       ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN),
       /*deprecated_enabled=*/true},
  };

  const AcceleratorData test_deprecated_accelerators[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  config_->Initialize(initial_test_data);
  config_->InitializeDeprecatedAccelerators(deprecated_data,
                                            test_deprecated_accelerators);

  const ui::Accelerator deprecated_accelerator(ui::VKEY_ESCAPE,
                                               ui::EF_SHIFT_DOWN);

  // Remove the deprecated accelerator ESCAPE + SHIFT.
  const AcceleratorData updated_expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kShowTaskManager},
  };
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kShowTaskManager, deprecated_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  // Verify that the accelerator is no longer deprecated.
  EXPECT_FALSE(config_->IsDeprecated(deprecated_accelerator));
  EXPECT_FALSE(config_->GetDeprecatedAcceleratorData(
      AcceleratorAction::kShowTaskManager));
  ExpectAllAcceleratorsEqual(updated_expected_test_data,
                             config_->GetAllAccelerators());

  // Attempt to restore AcceleratorAction::kShowTaskManager, expect deprecated
  // accelerator to NOT be re-added.
  result = config_->RestoreDefault(AcceleratorAction::kShowTaskManager);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  EXPECT_FALSE(config_->IsDeprecated(deprecated_accelerator));
  EXPECT_FALSE(config_->GetDeprecatedAcceleratorData(
      AcceleratorAction::kShowTaskManager));
  ExpectAllAcceleratorsEqual(updated_expected_test_data,
                             config_->GetAllAccelerators());

  // Now restore all accelerators, this time deprecated accelerators should be
  // restored.
  result = config_->RestoreAllDefaults();
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator));
  EXPECT_EQ(deprecated_data, config_->GetDeprecatedAcceleratorData(
                                 AcceleratorAction::kShowTaskManager));
  ExpectAllAcceleratorsEqual(expected_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, IsDefaultAccelerator) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleMirrorMode},
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_ALT_DOWN,
       AcceleratorAction::kSwapPrimaryDisplay},
      {/*trigger_on_press=*/true, ui::VKEY_MEDIA_LAUNCH_APP1,
       ui::EF_CONTROL_DOWN, AcceleratorAction::kTakeScreenshot},
      {/*trigger_on_press=*/true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE,
       AcceleratorAction::kKeyboardBrightnessUp},
      {/*trigger_on_press=*/true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN,
       AcceleratorAction::kKeyboardBrightnessUp},
  };

  // `Initialize()` sets up the default accelerators.
  config_->Initialize(test_data);
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());

  // Verify that Control + Zoom is the default for
  // AcceleratorAction::kToggleMirrorMode.
  ui::Accelerator expected_default =
      ui::Accelerator(ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN);
  std::optional<AcceleratorAction> accelerator_id =
      config_->GetIdForDefaultAccelerator(expected_default);
  EXPECT_TRUE(accelerator_id.has_value());
  EXPECT_EQ(AcceleratorAction::kToggleMirrorMode, accelerator_id.value());
  std::vector<ui::Accelerator> default_accelerators =
      config_->GetDefaultAcceleratorsForId(
          AcceleratorAction::kToggleMirrorMode);
  EXPECT_EQ(1u, default_accelerators.size());
  EXPECT_EQ(expected_default, default_accelerators[0]);

  std::vector<ui::Accelerator> nonexistent_defaults =
      config_->GetDefaultAcceleratorsForId(
          /*id=*/789987);
  EXPECT_EQ(0u, nonexistent_defaults.size());
}

TEST_F(AshAcceleratorConfigurationTest, MultipleDefaultAccelerators) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleMirrorMode},
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_ALT_DOWN,
       AcceleratorAction::kToggleMirrorMode},
      {/*trigger_on_press=*/true, ui::VKEY_MEDIA_LAUNCH_APP1,
       ui::EF_CONTROL_DOWN, AcceleratorAction::kTakeScreenshot},
      {/*trigger_on_press=*/true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE,
       AcceleratorAction::kKeyboardBrightnessUp},
      {/*trigger_on_press=*/true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN,
       AcceleratorAction::kKeyboardBrightnessUp},
  };

  // `Initialize()` sets up the default accelerators.
  config_->Initialize(test_data);
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());

  // Verify that Control + Zoom and Alt + Zoom are defaults for
  // AcceleratorAction::kToggleMirrorMode.
  ui::Accelerator expected_default =
      ui::Accelerator(ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN);
  ui::Accelerator expected_default_2 =
      ui::Accelerator(ui::VKEY_ZOOM, ui::EF_ALT_DOWN);

  std::optional<AcceleratorAction> accelerator_id =
      config_->GetIdForDefaultAccelerator(expected_default);
  EXPECT_TRUE(accelerator_id.has_value());
  EXPECT_EQ(AcceleratorAction::kToggleMirrorMode, accelerator_id.value());

  accelerator_id = config_->GetIdForDefaultAccelerator(expected_default_2);
  EXPECT_TRUE(accelerator_id.has_value());
  EXPECT_EQ(AcceleratorAction::kToggleMirrorMode, accelerator_id.value());

  std::vector<ui::Accelerator> default_accelerators =
      config_->GetDefaultAcceleratorsForId(
          AcceleratorAction::kToggleMirrorMode);

  EXPECT_EQ(2u, default_accelerators.size());

  EXPECT_TRUE(base::Contains(default_accelerators, expected_default));
  EXPECT_TRUE(base::Contains(default_accelerators, expected_default_2));
}
TEST_F(AshAcceleratorConfigurationTest, DefaultNotFound) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleMirrorMode},
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_ALT_DOWN,
       AcceleratorAction::kSwapPrimaryDisplay},
      {/*trigger_on_press=*/true, ui::VKEY_MEDIA_LAUNCH_APP1,
       ui::EF_CONTROL_DOWN, AcceleratorAction::kTakeScreenshot},
      {/*trigger_on_press=*/true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE,
       AcceleratorAction::kKeyboardBrightnessUp},
      {/*trigger_on_press=*/true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN,
       AcceleratorAction::kKeyboardBrightnessUp},
  };

  // `Initialize()` sets up the default accelerators.
  config_->Initialize(test_data);
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());

  // Verify that Ctrl + U is not a default accelerator in this test set.
  ui::Accelerator fake_default =
      ui::Accelerator(ui::VKEY_U, ui::EF_CONTROL_DOWN);
  std::optional<AcceleratorAction> accelerator_id =
      config_->GetIdForDefaultAccelerator(fake_default);
  EXPECT_FALSE(accelerator_id.has_value());
}

TEST_F(AshAcceleratorConfigurationTest, GetAcceleratorsFromActionId) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleMirrorMode},
      {/*trigger_on_press=*/true, ui::VKEY_ZOOM, ui::EF_ALT_DOWN,
       AcceleratorAction::kSwapPrimaryDisplay},
      {/*trigger_on_press=*/true, ui::VKEY_MEDIA_LAUNCH_APP1,
       ui::EF_CONTROL_DOWN, AcceleratorAction::kTakeScreenshot},
      {/*trigger_on_press=*/true, ui::VKEY_KBD_BRIGHTNESS_UP, ui::EF_NONE,
       AcceleratorAction::kKeyboardBrightnessUp},
      {/*trigger_on_press=*/true, ui::VKEY_BRIGHTNESS_UP, ui::EF_ALT_DOWN,
       AcceleratorAction::kKeyboardBrightnessUp},
  };
  config_->Initialize(test_data);

  // Create expected id_to_accelerator_data map.
  std::map<AcceleratorActionId, std::vector<AcceleratorData>>
      id_to_accelerator_data;
  for (const auto& data : test_data) {
    id_to_accelerator_data[static_cast<uint32_t>(data.action)].push_back(data);
  }

  // Verify that expected and actual are equal.
  for (const auto& data : test_data) {
    std::vector<AcceleratorData> expected =
        id_to_accelerator_data.at(data.action);
    base::optional_ref<const std::vector<ui::Accelerator>> actual =
        GetAcceleratorsForAction(data.action);
    ASSERT_TRUE(actual.has_value());
    ExpectAllAcceleratorsEqual(expected, *actual);
  }
}

TEST_F(AshAcceleratorConfigurationTest, VerifyObserversAreNotified) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());

  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);
  const std::vector<ui::Accelerator>& accelerators =
      config_->GetAllAccelerators();
  ExpectAllAcceleratorsEqual(test_data, accelerators);
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Now update accelerators with a different set of accelerators.
  const AcceleratorData test_data_updated[] = {
      {/*trigger_on_press=*/true, ui::VKEY_J, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleFullscreen},
  };

  config_->Initialize(test_data_updated);
  const std::vector<ui::Accelerator>& accelerators_updated =
      config_->GetAllAccelerators();
  ExpectAllAcceleratorsEqual(test_data_updated, accelerators_updated);
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  // Adding an additional kSwitchToLastUsedIme with trigger_on_press = false
  // here to verify it will be also removed together when removing
  // kSwitchToLastUsedIme with trigger_on_press = true.
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/false, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Remove `SWITCH_TO_LAST_USE_IME`.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Removing one accelerator in `kSwitchToLastUsedIme` will result in
  // the removed accelerator in the override with `kRemove`.
  EXPECT_EQ(1u, accelerator_overrides->size());
  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove, override_data.action);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());

  // Attempt to remove the accelerator again, expect to return error.
  AcceleratorConfigResult re_remove_result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kNotFound, re_remove_result);

  // Expect no changes to be made.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAcceleratorIDThatDoesntExist) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Attempt to remove an accelerator with an action ID that doesn't exist.
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kCycleBackwardMru,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kNotFound, result);

  // Nothing should change.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAcceleratorThatDoesntExist) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Remove an accelerator that doesn't exist, but with an
  // existing action ID. Expect no change.
  AcceleratorConfigResult updated_result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_M, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kNotFound, updated_result);

  // Nothing should change.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveDefaultAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kBrightnessDown},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Remove `SWITCH_TO_LAST_USE_IME`.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  ui::Accelerator removed_accelerator =
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, removed_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // We removed a default accelerator, it should still be cached as a default.
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme,
            config_->GetIdForDefaultAccelerator(removed_accelerator));
  std::vector<ui::Accelerator> default_accelerators =
      config_->GetDefaultAcceleratorsForId(
          AcceleratorAction::kSwitchToLastUsedIme);
  EXPECT_EQ(1u, default_accelerators.size());
  EXPECT_EQ(removed_accelerator, default_accelerators[0]);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAndRestoreDefault) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Remove `SWITCH_TO_LAST_USE_IME`.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());

  // Restore all defaults.
  result = config_->RestoreAllDefaults();
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Expect accelerators to revert back to the default state and observer
  // is called.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RestoreAllConsecutively) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Restore all defaults, even though no change was made.
  AcceleratorConfigResult reset_result = config_->RestoreAllDefaults();
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, reset_result);

  // Nothing should have changed, but the observer is called.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());

  // Restore all defaults again, even though no change was made.
  reset_result = config_->RestoreAllDefaults();
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, reset_result);

  // Nothing should have changed, but the observer is called.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAndRestoreOneAction) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Remove `SWITCH_TO_LAST_USE_IME`.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());

  // Reset the `AcceleratorAction::kSwitchToLastUsedIme` action..
  result = config_->RestoreDefault(AcceleratorAction::kSwitchToLastUsedIme);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Expect accelerators to revert back to the default state and observer
  // is called.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());

  // Reset one more time, no changes should be made.
  result = config_->RestoreDefault(AcceleratorAction::kSwitchToLastUsedIme);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // No changes to be made, but observer should be called.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(4, observer_.num_times_accelerator_updated_called());
}

TEST_F(AshAcceleratorConfigurationTest, RestoreInvalidAction) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // `BRIGHTNESS_DOWN` is not a valid accelerator, so this should return an
  // error.
  const AcceleratorConfigResult result =
      config_->RestoreDefault(AcceleratorAction::kBrightnessDown);
  EXPECT_EQ(AcceleratorConfigResult::kNotFound, result);

  // Expect nothing to change.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());
}

// Add accelerator with no conflict.
TEST_F(AshAcceleratorConfigurationTest, AddAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add CTRL + SPACE to AcceleratorAction::kSwitchToLastUsedIme.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);
}

// Add accelerator that conflict with default accelerator.
TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorDefaultConflict) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add ALT + SHIFT + TAB to AcceleratorAction::kSwitchToLastUsedIme, which
  // conflicts with AcceleratorAction::kCycleBackwardMru.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_TAB,
                                        ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Confirm that conflicting accelerator was removed.
  base::optional_ref<const std::vector<ui::Accelerator>>
      backward_mru_accelerators =
          GetAcceleratorsForAction(AcceleratorAction::kCycleBackwardMru);
  ASSERT_TRUE(backward_mru_accelerators->empty());
}

// Add accelerator that conflicts with a deprecated accelerator.
TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorDeprecatedConflict) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  const DeprecatedAcceleratorData deprecated_data[] = {
      {AcceleratorAction::kShowTaskManager,
       /*uma_histogram_name=*/"deprecated.showTaskManager",
       /*notification_message_id=*/1,
       /*new_shortcut_id=*/2,
       ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_COMMAND_DOWN),
       /*deprecated_enabled=*/true},
  };

  const AcceleratorData test_deprecated_accelerators[] = {
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  const AcceleratorData initial_expected_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kShowTaskManager},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  config_->Initialize(test_data);
  config_->InitializeDeprecatedAccelerators(deprecated_data,
                                            test_deprecated_accelerators);

  ExpectAllAcceleratorsEqual(initial_expected_data,
                             config_->GetAllAccelerators());
  // Initializing deprecated accelerators will also trigger the observer.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());

  const ui::Accelerator deprecated_accelerator(ui::VKEY_ESCAPE,
                                               ui::EF_SHIFT_DOWN);
  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator));

  const ui::Accelerator deprecated_accelerator_2(
      ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator_2));

  // Add SHIFT + ESCAPE to AcceleratorAction::kSwitchToLastUsedIme, which
  // conflicts with a deprecated accelerator.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_ESCAPE,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kShowTaskManager},
  };

  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, deprecated_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(deprecated_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Confirm that the deprecated accelerator was removed.
  EXPECT_FALSE(config_->IsDeprecated(deprecated_accelerator));

  // Ensure that the second deprecated accelerator still maps to a deprecated
  // action.
  const DeprecatedAcceleratorData* task_manager_deprecated_data =
      config_->GetDeprecatedAcceleratorData(
          AcceleratorAction::kShowTaskManager);
  EXPECT_EQ(AcceleratorAction::kShowTaskManager,
            task_manager_deprecated_data->action);
  EXPECT_TRUE(config_->IsDeprecated(deprecated_accelerator_2));
}

// Add and then remove an accelerator.
TEST_F(AshAcceleratorConfigurationTest, AddRemoveAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add CTRL + SPACE to AcceleratorAction::kSwitchToLastUsedIme.
  const AcceleratorData added_updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(added_updated_test_data,
                             config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Remove CTRL + SPACE from AcceleratorAction::kSwitchToLastUsedIme.
  const AcceleratorData removed_updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  // Remove the accelerator now.
  result = config_->RemoveAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                      new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(removed_updated_test_data,
                             config_->GetAllAccelerators());
  EXPECT_FALSE(config_->FindAcceleratorAction(new_accelerator));
}

// Add accelerator and restore its default.
TEST_F(AshAcceleratorConfigurationTest, AddRestoreAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add CTRL + SPACE to AcceleratorAction::kSwitchToLastUsedIme.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Restore default, expect to be back to default state.
  result = config_->RestoreDefault(AcceleratorAction::kSwitchToLastUsedIme);
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  EXPECT_FALSE(config_->FindAcceleratorAction(new_accelerator));
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, RestoreWithDefaultConflicts) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add ALT + SHIFT + TAB to AcceleratorAction::kSwitchToLastUsedIme, which
  // conflicts with AcceleratorAction::kCycleBackwardMru.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_C, ui::EF_ALT_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // removing an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Confirm that conflicting accelerator was removed.
  base::optional_ref<const std::vector<ui::Accelerator>>
      forward_mru_accelerators =
          GetAcceleratorsForAction(AcceleratorAction::kCycleForwardMru);
  ASSERT_TRUE(forward_mru_accelerators.has_value());

  EXPECT_EQ(1u, forward_mru_accelerators->size());

  // Now restore the default of `kCycleForwardMru`, this will effectively be a
  // no-opt since one of its default is a used by `kSwitchToLastUsedIme`.
  result = config_->RestoreDefault(AcceleratorAction::kCycleForwardMru);
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
  EXPECT_EQ(AcceleratorConfigResult::kRestoreSuccessWithConflicts, result);
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

// Add an accelerator, then add the same accelerator to another action.
TEST_F(AshAcceleratorConfigurationTest, ReAddAcceleratorToAnotherAction) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Add CTRL + SPACE to AcceleratorAction::kSwitchToLastUsedIme.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  const ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Add CTRL + SPACE to AcceleratorAction::kCycleBackwardMru.
  const AcceleratorData reupdated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  result = config_->AddUserAccelerator(AcceleratorAction::kCycleBackwardMru,
                                       new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  ExpectAllAcceleratorsEqual(reupdated_test_data,
                             config_->GetAllAccelerators());
  const AcceleratorAction* found_action2 =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action2);
  EXPECT_EQ(AcceleratorAction::kCycleBackwardMru, *found_action2);
}

TEST_F(AshAcceleratorConfigurationTest, ReplaceAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Replace  CTRL + ALT + SPACE -> CTRL + VKEY_M.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_M, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  const ui::Accelerator old_accelerator(ui::VKEY_SPACE,
                                        ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN);
  const ui::Accelerator new_accelerator(ui::VKEY_M, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result =
      config_->ReplaceAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                  old_accelerator, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);
}

TEST_F(AshAcceleratorConfigurationTest, ReplaceNonExistentAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  const ui::Accelerator old_accelerator(ui::VKEY_J, ui::EF_CONTROL_DOWN);
  const ui::Accelerator new_accelerator(ui::VKEY_C, ui::EF_COMMAND_DOWN);

  AcceleratorConfigResult result =
      config_->ReplaceAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                  old_accelerator, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kNotFound, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, ReplaceThenRestoreAccelerator) {
  EXPECT_EQ(0, observer_.num_times_accelerator_updated_called());
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  EXPECT_EQ(1, observer_.num_times_accelerator_updated_called());

  // Replace  CTRL + ALT + SPACE -> CTRL + VKEY_M.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_M, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  const ui::Accelerator old_accelerator(ui::VKEY_SPACE,
                                        ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN);
  const ui::Accelerator new_accelerator(ui::VKEY_M, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result =
      config_->ReplaceAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                  old_accelerator, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Compare expected accelerators and that the observer was fired after
  // adding an accelerator.
  EXPECT_EQ(2, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(new_accelerator);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme, *found_action);

  // Restore defaults, expect everything to be back to default state.
  config_->RestoreAllDefaults();
  EXPECT_EQ(3, observer_.num_times_accelerator_updated_called());
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAcceleratorPref) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Removing one accelerator in `AcceleratorAction::kSwitchToLastUsedIme` will
  // result in one default accelerator remaining.
  EXPECT_EQ(1u, accelerator_overrides->size());
  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove, override_data.action);

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  // Verify pref overrides were applied correctly.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAcceleratorThenResetAllPref) {
  SimulateNewUserFirstLogin(kFakeUserEmail);

  // Check histogram. There are two counts initially since there has been
  // two separate logins in this test.
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 2);

  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsBeforeResetAll", 1, 0);

  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  EXPECT_EQ(1u, accelerator_overrides->size());
  // Removing one accelerator in `AcceleratorAction::kSwitchToLastUsedIme` will
  // result in one entry with the `kRemove` tag.
  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove, override_data.action);

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  // Verify pref overrides were applied correctly.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  // `SimuateUserLogin` triggers the metric twice in tests.
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 1, 2);

  // Now reset all to default.
  result = config_->RestoreAllDefaults();
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  EXPECT_TRUE(GetOverridePref().empty());

  // Relogin, expect shortcuts to be back to default.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& reset_pref_overrides = GetOverridePref();
  EXPECT_TRUE(reset_pref_overrides.empty());
  // `test_data` is the default state of accelerators.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
  // Expect increases in the `0` bucket, it gets incremented by 2 due to
  // `SimulateUserLogin`.
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 4);

  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsBeforeResetAll", 1, 1);
}

TEST_F(AshAcceleratorConfigurationTest, RemoveAcceleratorThenResetPref) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme,
      ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN));
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Removing one accelerator in `AcceleratorAction::kSwitchToLastUsedIme` will
  // result in one entry with the `kRemove` tag.
  EXPECT_EQ(1u, accelerator_overrides->size());
  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove, override_data.action);

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  // Verify pref overrides were applied correctly.
  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleForwardMru},
      {/*trigger_on_press=*/true, ui::VKEY_TAB,
       ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kCycleBackwardMru},
  };
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Now reset the action to default.
  result = config_->RestoreDefault(AcceleratorAction::kSwitchToLastUsedIme);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Relogin, expect shortcuts to be back to default.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& reset_pref_overrides = GetOverridePref();
  EXPECT_TRUE(reset_pref_overrides.empty());
  // `test_data` is the default state of accelerators.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 2);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  const ui::Accelerator new_accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Expect 1 override accelerator for
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  EXPECT_EQ(1u, accelerator_overrides->size());

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 1, 2);
}

TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorWithConflictWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Search + C exists in `AcceleratorAction::kToggleCalendar`, so this should
  // result in adding a new accelerator to
  // `AcceleratorAction::kSwitchToLastUsedIme` and removing an accelerator from
  // `AcceleratorAction::kToggleCalendar`.
  const ui::Accelerator new_accelerator(ui::VKEY_C, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There two entries in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* switch_to_last_used_ime_overrides =
      updated_overrides.FindList(
          base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));

  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, switch_to_last_used_ime_overrides->size());
  AcceleratorModificationData switch_ime_override_data =
      ValueToAcceleratorModificationData(
          switch_to_last_used_ime_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      switch_ime_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd,
            switch_ime_override_data.action);

  const AcceleratorData expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };
  // `AcceleratorAction::kSwitchToLastUsedIme_data` has all the available
  // accelerators.
  ExpectAllAcceleratorsEqual(expected_test_data, config_->GetAllAccelerators());

  // Lock screen and sign into another user.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());

  // Verify pref overrides were applied correctly.
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme,
            *config_->FindAcceleratorAction(new_accelerator));

  // `AcceleratorAction::kSwitchToLastUsedIme_data` has all the available
  // accelerators.
  ExpectAllAcceleratorsEqual(expected_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest,
       MultipleActionsAddSameAcceleratorWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
  };

  config_->Initialize(test_data);
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 2);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Search + C exists in `AcceleratorAction::kToggleCalendar`, so this should
  // result in adding a new accelerator to
  // `AcceleratorAction::kSwitchToLastUsedIme` and removing an accelerator from
  // `AcceleratorAction::kToggleCalendar`.
  const ui::Accelerator new_accelerator(ui::VKEY_C, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There is one entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* switch_to_last_used_ime_overrides =
      updated_overrides.FindList(
          base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));

  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, switch_to_last_used_ime_overrides->size());
  AcceleratorModificationData switch_ime_override_data =
      ValueToAcceleratorModificationData(
          switch_to_last_used_ime_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      switch_ime_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd,
            switch_ime_override_data.action);

  const AcceleratorData expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
  };
  // `AcceleratorAction::kSwitchToLastUsedIme_data` has all the available
  // accelerators.
  ExpectAllAcceleratorsEqual(expected_test_data, config_->GetAllAccelerators());

  // Now have `AcceleratorAction::kEnableOrToggleDictation` add Search + C,
  // removing it from `AcceleratorAction::kSwitchToLastUsedIme`.
  result = config_->AddUserAccelerator(
      AcceleratorAction::kEnableOrToggleDictation, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  // Expect just one entry, since `AcceleratorAction::kSwitchToLastUsedIme` no
  // longer holds the Search + C accelerator.
  const base::Value::Dict& updated_overrides_2 = GetOverridePref();
  EXPECT_EQ(1u, updated_overrides_2.size());

  const base::Value::List* toggle_dictation_overrides =
      updated_overrides_2.FindList(
          base::NumberToString(AcceleratorAction::kEnableOrToggleDictation));
  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, toggle_dictation_overrides->size());
  AcceleratorModificationData toggle_dictation_data =
      ValueToAcceleratorModificationData(
          toggle_dictation_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      toggle_dictation_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, toggle_dictation_data.action);

  const AcceleratorData expected_test_data_2[] = {
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };
  ExpectAllAcceleratorsEqual(expected_test_data_2,
                             config_->GetAllAccelerators());

  // Lock screen and sign into another user.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 4);

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());

  // Verify pref overrides were applied correctly.
  EXPECT_EQ(AcceleratorAction::kEnableOrToggleDictation,
            *config_->FindAcceleratorAction(new_accelerator));

  // `AcceleratorAction::kSwitchToLastUsedIme_data` has all the available
  // accelerators.
  ExpectAllAcceleratorsEqual(expected_test_data_2,
                             config_->GetAllAccelerators());
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 1, 2);
}

TEST_F(AshAcceleratorConfigurationTest,
       AddCustomThenReAddAfterRemovedWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
  };

  config_->Initialize(test_data);
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 0, 2);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Add a new custom accelerator, Search + Alt + M to `kSwitchToLastUsedIme`.
  const ui::Accelerator new_accelerator(ui::VKEY_M,
                                        ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There is one entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* switch_to_last_used_ime_overrides =
      updated_overrides.FindList(
          base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));

  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, switch_to_last_used_ime_overrides->size());
  AcceleratorModificationData switch_ime_override_data =
      ValueToAcceleratorModificationData(
          switch_to_last_used_ime_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators({/*trigger_on_press=*/true, ui::VKEY_M,
                                   ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
                                   AcceleratorAction::kSwitchToLastUsedIme},
                                  switch_ime_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd,
            switch_ime_override_data.action);

  const AcceleratorData expected_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_M,
       ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
  };
  ExpectAllAcceleratorsEqual(expected_test_data, config_->GetAllAccelerators());

  // Now have `AcceleratorAction::kEnableOrToggleDictation` add
  // Search + Alt + M, removing it from
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  result = config_->AddUserAccelerator(
      AcceleratorAction::kEnableOrToggleDictation, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  // Expect just one entry, since `AcceleratorAction::kSwitchToLastUsedIme` no
  // longer holds the Search + Alt + M accelerator.
  const base::Value::Dict& updated_overrides_2 = GetOverridePref();
  EXPECT_EQ(1u, updated_overrides_2.size());

  const base::Value::List* toggle_dictation_overrides =
      updated_overrides_2.FindList(
          base::NumberToString(AcceleratorAction::kEnableOrToggleDictation));
  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, toggle_dictation_overrides->size());
  AcceleratorModificationData toggle_dictation_data =
      ValueToAcceleratorModificationData(
          toggle_dictation_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators({/*trigger_on_press=*/true, ui::VKEY_M,
                                   ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
                                   AcceleratorAction::kEnableOrToggleDictation},
                                  toggle_dictation_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, toggle_dictation_data.action);

  const AcceleratorData expected_test_data_2[] = {
      {/*trigger_on_press=*/true, ui::VKEY_M,
       ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
  };
  ExpectAllAcceleratorsEqual(expected_test_data_2,
                             config_->GetAllAccelerators());

  // Re-add a new custom accelerator, Search + Alt + M to
  // `kSwitchToLastUsedIme`.
  result = config_->AddUserAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                       new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides_3 = GetOverridePref();
  // There is one entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides_3.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* switch_to_last_used_ime_overrides_2 =
      updated_overrides_3.FindList(
          base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));

  // Confirm that prefs are stored correctly.
  EXPECT_EQ(1u, switch_to_last_used_ime_overrides_2->size());
  switch_ime_override_data = ValueToAcceleratorModificationData(
      switch_to_last_used_ime_overrides_2->front().GetDict());
  EXPECT_TRUE(CompareAccelerators({/*trigger_on_press=*/true, ui::VKEY_M,
                                   ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
                                   AcceleratorAction::kSwitchToLastUsedIme},
                                  switch_ime_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd,
            switch_ime_override_data.action);

  const AcceleratorData expected_test_data_3[] = {
      {/*trigger_on_press=*/true, ui::VKEY_M,
       ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kEnableOrToggleDictation},
  };
  ExpectAllAcceleratorsEqual(expected_test_data_3,
                             config_->GetAllAccelerators());
  // Lock screen and sign into another user.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());

  // Verify pref overrides were applied correctly.
  EXPECT_EQ(AcceleratorAction::kSwitchToLastUsedIme,
            *config_->FindAcceleratorAction(new_accelerator));

  // `AcceleratorAction::kSwitchToLastUsedIme_data` has all the available
  // accelerators.
  ExpectAllAcceleratorsEqual(expected_test_data_3,
                             config_->GetAllAccelerators());
  histogram_tester_->ExpectBucketCount(
      "Ash.ShortcutCustomization.CustomizationsLoadedOnStartup", 1, 2);
}

TEST_F(AshAcceleratorConfigurationTest, RemoveThenAddAcceleratorWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Remove Ctrl + space from `AcceleratorAction::kSwitchToLastUsedIme`.
  ui::Accelerator new_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  AcceleratorConfigResult result = config_->RemoveAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Now add Ctrl + Space to `AcceleratorAction::kToggleCalendar`.
  result = config_->AddUserAccelerator(AcceleratorAction::kToggleCalendar,
                                       new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  const base::Value::Dict& remove_add_overrides = GetOverridePref();
  EXPECT_EQ(2u, remove_add_overrides.size());

  // Verify prefs are populated correctly.
  const base::Value::Dict& updated_overrides = GetOverridePref();
  const base::Value::List* last_used_ime_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          last_used_ime_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove, override_data.action);

  // Now verify add pref is present.
  const base::Value::List* toggle_calendar_overrides =
      updated_overrides.FindList(
          base::NumberToString(AcceleratorAction::kToggleCalendar));
  override_data = ValueToAcceleratorModificationData(
      toggle_calendar_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleCalendar},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kToggleCalendar},
  };
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(2u, relogin_overrides.size());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, ReplaceAcceleratorWithPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Replace Ctrl + Space with Meta + A in
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  ui::Accelerator old_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
  ui::Accelerator new_accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result =
      config_->ReplaceAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                  old_accelerator, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  // Verify prefs are populated correctly.
  const base::Value::Dict& updated_overrides = GetOverridePref();
  const base::Value::List* last_used_ime_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  AcceleratorModificationData remove_override_data =
      ValueToAcceleratorModificationData(
          last_used_ime_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      remove_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kRemove,
            remove_override_data.action);

  AcceleratorModificationData add_override_data =
      ValueToAcceleratorModificationData(
          last_used_ime_overrides->back().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      add_override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, add_override_data.action);

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_C, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kToggleCalendar},
  };
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, IgnoreBadActionIdPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Simulate setting a pref with bad values (invalid action_id).
  const ui::Accelerator bad_accelerator(ui::VKEY_B, ui::EF_ALT_DOWN);
  SetOverridePref(bad_accelerator, AcceleratorModificationAction::kAdd,
                  /*action_id*/ 7777777);

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile, expect that no prefs are available
  // since the bad pref should've been removed.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_TRUE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_TRUE(relogin_overrides.empty());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, IgnoreBadAcceleratorPrefs) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  // Simulate setting a pref with bad values (invalid action_id).
  const ui::Accelerator bad_accelerator(ui::VKEY_B, ui::EF_ALT_DOWN);
  SetOverridePref(bad_accelerator, AcceleratorModificationAction::kRemove,
                  kSwitchToLastUsedIme);

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile, expect that no prefs are available
  // since the bad pref should've been removed.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_TRUE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_TRUE(relogin_overrides.empty());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, AddAcceleratorWithPrefReleasedState) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  const ui::Accelerator released_accelerator(
      ui::VKEY_A, ui::EF_COMMAND_DOWN, ui::Accelerator::KeyState::RELEASED);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, released_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const ui::Accelerator pressed_accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN,
                                            ui::Accelerator::KeyState::PRESSED);
  result = config_->AddUserAccelerator(AcceleratorAction::kSwitchToLastUsedIme,
                                       pressed_accelerator);

  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);
  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Expect 2 overrides accelerator for
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  EXPECT_EQ(2u, accelerator_overrides->size());

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/false, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/false, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  override_data = ValueToAcceleratorModificationData(
      accelerator_overrides->back().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, SwitchUserPrefsAreSeparate) {
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_D,
       ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
       AcceleratorAction::kMagnifierZoomIn},
  };

  config_->Initialize(test_data);

  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  const ui::Accelerator new_accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kMagnifierZoomIn, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kMagnifierZoomIn));
  // Expect 1 override accelerator for
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  EXPECT_EQ(1u, accelerator_overrides->size());

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_D,
       ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
       AcceleratorAction::kMagnifierZoomIn},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kMagnifierZoomIn},
  };

  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kMagnifierZoomIn},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Expect the second user to have all defaults.
  ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators());

  // Now re-login to the original profile.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_FALSE(original_pref_overrides.empty());

  const base::Value::Dict& relogin_overrides = GetOverridePref();
  EXPECT_EQ(1u, relogin_overrides.size());
  // Verify pref overrides were loaded correctly.
  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());
}

TEST_F(AshAcceleratorConfigurationTest, PrefsResetWithFlag) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.InitWithFeatures(
      /*enabled_features=*/{::features::kShortcutCustomization,
                            features::kResetShortcutCustomizations},
      /*disabled_features=*/{});
  SimulateNewUserFirstLogin(kFakeUserEmail);
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  config_->Initialize(test_data);
  // Expect that there are no entries stored in the override pref.
  const base::Value::Dict& pref_overrides = GetOverridePref();
  EXPECT_TRUE(pref_overrides.empty());

  const ui::Accelerator new_accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN);
  AcceleratorConfigResult result = config_->AddUserAccelerator(
      AcceleratorAction::kSwitchToLastUsedIme, new_accelerator);
  EXPECT_EQ(AcceleratorConfigResult::kSuccess, result);

  const base::Value::Dict& updated_overrides = GetOverridePref();
  // There should now be an entry in the pref overrides.
  EXPECT_EQ(1u, updated_overrides.size());
  // Expect the pref to have one entry that has the key of
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  const base::Value::List* accelerator_overrides = updated_overrides.FindList(
      base::NumberToString(AcceleratorAction::kSwitchToLastUsedIme));
  // Expect 1 override accelerator for
  // `AcceleratorAction::kSwitchToLastUsedIme`.
  EXPECT_EQ(1u, accelerator_overrides->size());

  const AcceleratorData updated_test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_SPACE,
       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
  };

  AcceleratorModificationData override_data =
      ValueToAcceleratorModificationData(
          accelerator_overrides->front().GetDict());
  EXPECT_TRUE(CompareAccelerators(
      {/*trigger_on_press=*/true, ui::VKEY_A, ui::EF_COMMAND_DOWN,
       AcceleratorAction::kSwitchToLastUsedIme},
      override_data.accelerator));
  EXPECT_EQ(AcceleratorModificationAction::kAdd, override_data.action);

  ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators());

  // Simulate login on another account, expect the pref to not be present.
  GetSessionControllerClient()->LockScreen();
  SimulateNewUserFirstLogin(kFakeUserEmail2);
  const base::Value::Dict& other_user_pref_overrides = GetOverridePref();
  EXPECT_TRUE(other_user_pref_overrides.empty());

  // Now re-login to the original profile. Since #reset-shortcut-customizations
  // is enabled, expect that no prefs were saved.
  GetSessionControllerClient()->LockScreen();
  config_->Initialize(test_data);
  SimulateUserLogin(kFakeUserEmail);
  const base::Value::Dict& original_pref_overrides = GetOverridePref();
  EXPECT_TRUE(original_pref_overrides.empty());
}

TEST_F(AshAcceleratorConfigurationTest, FindAcceleratorActionPositionalKeys) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN,
       AcceleratorAction::kWindowCycleSnapLeft},
  };

  config_->Initialize(test_data);
  config_->SetUsePositionalLookup(/*use_positional_lookup=*/true);

  // The the DE-de layout, alt + bracket left is mapped to alt + VKEY_OEM_1.
  // Performing a lookup should be able to remap the lookup correctly from
  // VKEY_OEM_1 -> VKEY_OEM_4.
  const ui::Accelerator de_alt_left_bracket(
      ui::VKEY_OEM_1, ui::DomCode::BRACKET_LEFT, ui::EF_ALT_DOWN);
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(de_alt_left_bracket);
  EXPECT_TRUE(found_action);
  EXPECT_EQ(AcceleratorAction::kWindowCycleSnapLeft, *found_action);

  // Now reset all accelerators and still expect the lookup to succeed.
  config_->RestoreAllDefaults();
  const AcceleratorAction* found_action2 =
      config_->FindAcceleratorAction(de_alt_left_bracket);
  EXPECT_TRUE(found_action2);
  EXPECT_EQ(AcceleratorAction::kWindowCycleSnapLeft, *found_action2);
}

TEST_F(AshAcceleratorConfigurationTest,
       FindAcceleratorActionPositionalDisabledKeys) {
  const AcceleratorData test_data[] = {
      {/*trigger_on_press=*/true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN,
       AcceleratorAction::kWindowCycleSnapLeft},
  };

  config_->Initialize(test_data);
  config_->SetUsePositionalLookup(/*use_positional_lookup=*/false);

  // The inputted accelerator here will not be positionally remapped.
  const ui::Accelerator de_alt_left_bracket(
      ui::VKEY_OEM_1, ui::DomCode::BRACKET_LEFT, ui::EF_ALT_DOWN);
  const AcceleratorAction* found_action =
      config_->FindAcceleratorAction(de_alt_left_bracket);
  EXPECT_FALSE(found_action);
}

}  // namespace ash