chromium/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_unittest.cc

// Copyright 2023 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/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/mojom/input_device_settings.mojom-shared.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/shell.h"
#include "ash/system/input_device_settings/input_device_settings_defaults.h"
#include "ash/system/input_device_settings/input_device_settings_pref_names.h"
#include "ash/system/input_device_settings/input_device_settings_utils.h"
#include "ash/system/input_device_settings/input_device_tracker.h"
#include "ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_impl.h"
#include "ash/system/input_device_settings/settings_updated_metrics_info.h"
#include "ash/test/ash_test_base.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/to_string.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/known_user.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/ash/keyboard_capability.h"
#include "ui/events/ash/mojom/extended_fkeys_modifier.mojom-shared.h"
#include "ui/events/ash/mojom/extended_fkeys_modifier.mojom.h"
#include "ui/events/ash/mojom/modifier_key.mojom-shared.h"
#include "ui/events/ash/mojom/modifier_key.mojom.h"
#include "ui/events/ash/mojom/six_pack_shortcut_modifier.mojom-shared.h"
#include "ui/events/ash/mojom/six_pack_shortcut_modifier.mojom.h"
#include "ui/events/ash/pref_names.h"
#include "ui/events/devices/device_data_manager_test_api.h"

namespace ash {

namespace {

constexpr char kUserEmail[] = "[email protected]";
constexpr char kUserEmail2[] = "[email protected]";
const AccountId account_id_1 = AccountId::FromUserEmail(kUserEmail);
const AccountId account_id_2 = AccountId::FromUserEmail(kUserEmail2);

const std::string kDictFakeKey = "fake_key";
const std::string kDictFakeValue = "fake_value";

const std::string kKeyboardKey1 = "device_key1";
const std::string kKeyboardKey2 = "device_key2";
const std::string kKeyboardKey3 = "device_key3";

const bool kGlobalSendFunctionKeys = false;

const ui::KeyboardDevice kSampleSplitModifierKeyboard(
    21,
    ui::INPUT_DEVICE_INTERNAL,
    "kSampleSplitModifierKeyboard",
    /*has_assistant_key=*/true,
    /*has_function_key=*/true);

const mojom::KeyboardSettings kKeyboardSettingsDefault(
    /*modifier_remappings=*/{},
    /*top_row_are_fkeys=*/kDefaultTopRowAreFKeys,
    /*suppress_meta_fkey_rewrites=*/kDefaultSuppressMetaFKeyRewrites,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

const mojom::KeyboardSettings kKeyboardSettingsExternalDefault(
    /*modifier_remappings=*/{},
    /*top_row_are_fkeys=*/kDefaultTopRowAreFKeysExternal,
    /*suppress_meta_fkey_rewrites=*/kDefaultSuppressMetaFKeyRewrites,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

const mojom::KeyboardSettings kKeyboardSettingsNotDefault(
    /*modifier_remappings=*/{},
    /*top_row_are_fkeys=*/!kDefaultTopRowAreFKeys,
    /*suppress_meta_fkey_rewrites=*/!kDefaultSuppressMetaFKeyRewrites,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

const mojom::KeyboardSettings kKeyboardSettings1(
    /*modifier_remappings=*/{},
    /*top_row_are_fkeys=*/false,
    /*suppress_meta_fkey_rewrites=*/false,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

const mojom::KeyboardSettings kKeyboardSettings2(
    /*modifier_remappings=*/{{ui::mojom::ModifierKey::kControl,
                              ui::mojom::ModifierKey::kAlt},
                             {ui::mojom::ModifierKey::kAssistant,
                              ui::mojom::ModifierKey::kVoid}},
    /*top_row_are_fkeys=*/true,
    /*suppress_meta_fkey_rewrites=*/true,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

const mojom::KeyboardSettings kKeyboardSettings3(
    /*modifier_remappings=*/{{ui::mojom::ModifierKey::kAlt,
                              ui::mojom::ModifierKey::kCapsLock},
                             {ui::mojom::ModifierKey::kAssistant,
                              ui::mojom::ModifierKey::kVoid},
                             {ui::mojom::ModifierKey::kBackspace,
                              ui::mojom::ModifierKey::kEscape},
                             {ui::mojom::ModifierKey::kControl,
                              ui::mojom::ModifierKey::kAssistant}},
    /*top_row_are_fkeys=*/true,
    /*suppress_meta_fkey_rewrites=*/false,
    mojom::SixPackKeyInfo::New(),
    kDefaultFkey,
    kDefaultFkey);

}  // namespace

class KeyboardPrefHandlerTest : public AshTestBase {
 public:
  KeyboardPrefHandlerTest() = default;
  KeyboardPrefHandlerTest(const KeyboardPrefHandlerTest&) = delete;
  KeyboardPrefHandlerTest& operator=(const KeyboardPrefHandlerTest&) = delete;
  ~KeyboardPrefHandlerTest() override = default;

  // testing::Test:
  void SetUp() override {
    // scoped_feature_list_.InitAndEnableFeature(
    //     features::kInputDeviceSettingsSplit);

    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{features::kInputDeviceSettingsSplit,
                              features::kAltClickAndSixPackCustomization,
                              ::features::kSupportF11AndF12KeyShortcuts},
        /*disabled_features=*/{});

    AshTestBase::SetUp();
    InitializePrefService();
    pref_handler_ = std::make_unique<KeyboardPrefHandlerImpl>();
  }

  void TearDown() override {
    pref_handler_.reset();
    AshTestBase::TearDown();
  }

  void InitializePrefService() {
    pref_service_ = std::make_unique<TestingPrefServiceSimple>();

    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardDeviceSettingsDictPref);
    pref_service_->registry()->RegisterBooleanPref(prefs::kSendFunctionKeys,
                                                   kGlobalSendFunctionKeys);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardDefaultChromeOSSettings);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardDefaultNonChromeOSSettings);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardDefaultSplitModifierSettings);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardUpdateSettingsMetricInfo);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kKeyboardInternalSettings);

    // Register Integer prefs which determine how we remap modifiers.
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapAltKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kAlt));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapControlKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kControl));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapEscapeKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kEscape));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapBackspaceKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kBackspace));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapAssistantKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kAssistant));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapCapsLockKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kCapsLock));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapExternalMetaKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kMeta));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapSearchKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kMeta));
    pref_service_->registry()->RegisterIntegerPref(
        ::prefs::kLanguageRemapExternalCommandKeyTo,
        static_cast<int>(ui::mojom::ModifierKey::kMeta));

    pref_service_->registry()->RegisterIntegerPref(
        prefs::kKeyEventRemappedToSixPackDelete, 0);
    pref_service_->registry()->RegisterIntegerPref(
        prefs::kKeyEventRemappedToSixPackEnd, 0);
    pref_service_->registry()->RegisterIntegerPref(
        prefs::kKeyEventRemappedToSixPackHome, 0);
    pref_service_->registry()->RegisterIntegerPref(
        prefs::kKeyEventRemappedToSixPackPageUp, 0);
    pref_service_->registry()->RegisterIntegerPref(
        prefs::kKeyEventRemappedToSixPackPageDown, 0);
  }

  void CheckKeyboardSettingsAndDictAreEqual(
      const mojom::KeyboardSettings& settings,
      const base::Value::Dict& settings_dict,
      bool is_external = false) {
    auto suppress_meta_fkey_rewrites =
        settings_dict.FindBool(prefs::kKeyboardSettingSuppressMetaFKeyRewrites);
    if (suppress_meta_fkey_rewrites.has_value()) {
      EXPECT_EQ(settings.suppress_meta_fkey_rewrites,
                suppress_meta_fkey_rewrites);
    } else {
      EXPECT_EQ(settings.suppress_meta_fkey_rewrites,
                kDefaultSuppressMetaFKeyRewrites);
    }

    auto top_row_are_fkeys =
        settings_dict.FindBool(prefs::kKeyboardSettingTopRowAreFKeys);
    if (top_row_are_fkeys.has_value()) {
      EXPECT_EQ(settings.top_row_are_fkeys, top_row_are_fkeys);
    } else {
      EXPECT_EQ(settings.top_row_are_fkeys, is_external
                                                ? kDefaultTopRowAreFKeysExternal
                                                : kDefaultTopRowAreFKeys);
    }

    auto* modifier_remappings_dict =
        settings_dict.FindDict(prefs::kKeyboardSettingModifierRemappings);
    ASSERT_NE(modifier_remappings_dict, nullptr);
    for (const auto& [from_expected, to_expected] :
         settings.modifier_remappings) {
      auto to = modifier_remappings_dict->FindInt(
          base::NumberToString(static_cast<int>(from_expected)));
      ASSERT_TRUE(to.has_value());
      EXPECT_EQ(static_cast<int>(to_expected), to);
    }
  }

  void CheckKeyboardSettingsAreSetToDefaultValues(
      const mojom::KeyboardSettings& settings,
      bool is_external) {
    const bool default_top_row_are_fkeys =
        is_external ? !kKeyboardSettingsDefault.top_row_are_fkeys
                    : kKeyboardSettingsDefault.top_row_are_fkeys;
    EXPECT_EQ(settings.suppress_meta_fkey_rewrites,
              kKeyboardSettingsDefault.suppress_meta_fkey_rewrites);
    EXPECT_EQ(settings.top_row_are_fkeys, default_top_row_are_fkeys);
    EXPECT_EQ(0u, settings.modifier_remappings.size());
  }

  void CallUpdateKeyboardSettings(const std::string& device_key,
                                  const mojom::KeyboardSettings& settings,
                                  bool is_external = false) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->settings = settings.Clone();
    keyboard->device_key = device_key;
    keyboard->is_external = is_external;

    pref_handler_->UpdateKeyboardSettings(pref_service_.get(),
                                          /*keyboard_policies=*/{}, *keyboard);
  }

  void CallUpdateKeyboardSettings(const mojom::Keyboard& keyboard) {
    pref_handler_->UpdateKeyboardSettings(pref_service_.get(),
                                          /*keyboard_policies=*/{}, keyboard);
  }

  void CallUpdateLoginScreenKeyboardSettings(
      const AccountId& account_id,
      const std::string& device_key,
      const mojom::KeyboardSettings& settings) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->settings = settings.Clone();
    pref_handler_->UpdateLoginScreenKeyboardSettings(local_state(), account_id,
                                                     /*keyboard_policies=*/{},
                                                     *keyboard);
  }

  void CallUpdateDefaultChromeOSKeyboardSettings(
      const std::string& device_key,
      const mojom::KeyboardSettings& settings) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->settings = settings.Clone();
    keyboard->device_key = device_key;
    keyboard->meta_key = ui::mojom::MetaKey::kLauncher;

    pref_handler_->UpdateDefaultChromeOSKeyboardSettings(
        pref_service_.get(),
        /*keyboard_policies=*/{}, *keyboard);
  }

  void CallUpdateDefaultNonChromeOSKeyboardSettings(
      const std::string& device_key,
      const mojom::KeyboardSettings& settings) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->settings = settings.Clone();
    keyboard->device_key = device_key;
    keyboard->meta_key = ui::mojom::MetaKey::kExternalMeta;

    pref_handler_->UpdateDefaultNonChromeOSKeyboardSettings(
        pref_service_.get(),
        /*keyboard_policies=*/{}, *keyboard);
  }

  void CallUpdateDefaultSplitModifierKeyboardSettings(
      const std::string& device_key,
      const mojom::KeyboardSettings& settings) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->settings = settings.Clone();
    keyboard->device_key = device_key;
    keyboard->meta_key = ui::mojom::MetaKey::kLauncherRefresh;
    keyboard->modifier_keys = {ui::mojom::ModifierKey::kFunction,
                               ui::mojom::ModifierKey::kRightAlt};

    pref_handler_->UpdateDefaultSplitModifierKeyboardSettings(
        pref_service_.get(),
        /*keyboard_policies=*/{}, *keyboard);
  }

  mojom::KeyboardSettingsPtr CallInitializeKeyboardSettings(
      const std::string& device_key,
      bool is_external = false) {
    mojom::KeyboardPtr keyboard = mojom::Keyboard::New();
    keyboard->device_key = device_key;
    keyboard->is_external = is_external;
    keyboard->modifier_keys = {
        ui::mojom::ModifierKey::kAlt,       ui::mojom::ModifierKey::kControl,
        ui::mojom::ModifierKey::kAssistant, ui::mojom::ModifierKey::kBackspace,
        ui::mojom::ModifierKey::kMeta,      ui::mojom::ModifierKey::kEscape};

    pref_handler_->InitializeKeyboardSettings(
        pref_service_.get(), /*keyboard_policies=*/{}, keyboard.get());
    return std::move(keyboard->settings);
  }

  mojom::KeyboardSettingsPtr CallInitializeKeyboardSettings(
      const mojom::Keyboard& keyboard) {
    auto keyboard_ptr = keyboard.Clone();
    keyboard_ptr->modifier_keys = {
        ui::mojom::ModifierKey::kAlt,       ui::mojom::ModifierKey::kControl,
        ui::mojom::ModifierKey::kAssistant, ui::mojom::ModifierKey::kBackspace,
        ui::mojom::ModifierKey::kMeta,      ui::mojom::ModifierKey::kEscape};

    pref_handler_->InitializeKeyboardSettings(
        pref_service_.get(), /*keyboard_policies=*/{}, keyboard_ptr.get());
    return std::move(keyboard_ptr->settings);
  }

  mojom::KeyboardSettingsPtr CallInitializeLoginScreenKeyboardSettings(
      const AccountId& account_id,
      const mojom::Keyboard& keyboard) {
    auto keyboard_ptr = keyboard.Clone();
    keyboard_ptr->modifier_keys = {
        ui::mojom::ModifierKey::kAlt,       ui::mojom::ModifierKey::kControl,
        ui::mojom::ModifierKey::kAssistant, ui::mojom::ModifierKey::kBackspace,
        ui::mojom::ModifierKey::kMeta,      ui::mojom::ModifierKey::kEscape};

    pref_handler_->InitializeLoginScreenKeyboardSettings(
        local_state(), account_id, /*keyboard_policies=*/{},
        keyboard_ptr.get());
    return std::move(keyboard_ptr->settings);
  }

  const base::Value::Dict* GetSettingsDictForDeviceKey(
      const std::string& device_key,
      bool is_external = false) {
    if (!is_external) {
      return &pref_service_->GetDict(prefs::kKeyboardInternalSettings);
    }

    const auto& devices_dict =
        pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref);
    EXPECT_EQ(1u, devices_dict.size());
    const auto* settings_dict = devices_dict.FindDict(device_key);
    EXPECT_NE(nullptr, settings_dict);

    return settings_dict;
  }

  user_manager::KnownUser known_user() {
    return user_manager::KnownUser(local_state());
  }

  bool HasInternalLoginScreenSettingsDict(AccountId account_id) {
    const auto* dict = known_user().FindPath(
        account_id, prefs::kKeyboardLoginScreenInternalSettingsPref);
    return dict && dict->is_dict();
  }

  bool HasExternalLoginScreenSettingsDict(AccountId account_id) {
    const auto* dict = known_user().FindPath(
        account_id, prefs::kKeyboardLoginScreenExternalSettingsPref);
    return dict && dict->is_dict();
  }

  base::Value::Dict GetInternalLoginScreenSettingsDict(AccountId account_id) {
    return known_user()
        .FindPath(account_id, prefs::kKeyboardLoginScreenInternalSettingsPref)
        ->GetDict()
        .Clone();
  }

  base::Value::Dict GetExternalLoginScreenSettingsDict(AccountId account_id) {
    return known_user()
        .FindPath(account_id, prefs::kKeyboardLoginScreenExternalSettingsPref)
        ->GetDict()
        .Clone();
  }

 protected:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<KeyboardPrefHandlerImpl> pref_handler_;
  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
};

TEST_F(KeyboardPrefHandlerTest, MultipleDevices) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);
  CallUpdateKeyboardSettings(kKeyboardKey2, kKeyboardSettings2,
                             /*is_external=*/true);
  CallUpdateKeyboardSettings(kKeyboardKey3, kKeyboardSettings3,
                             /*is_external=*/true);

  const auto& devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref);
  ASSERT_EQ(3u, devices_dict.size());

  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings1, *settings_dict,
                                       /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings2, *settings_dict,
                                       /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey3);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings3, *settings_dict,
                                       /*is_external=*/true);
}

TEST_F(KeyboardPrefHandlerTest, InitializeLoginScreenKeyboardSettings) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.is_external = false;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeLoginScreenKeyboardSettings(account_id_1, keyboard);

  EXPECT_FALSE(HasInternalLoginScreenSettingsDict(account_id_1));
  CheckKeyboardSettingsAreSetToDefaultValues(*settings, keyboard.is_external);

  keyboard.is_external = true;
  settings = CallInitializeLoginScreenKeyboardSettings(account_id_2, keyboard);
  EXPECT_FALSE(HasExternalLoginScreenSettingsDict(account_id_2));

  CheckKeyboardSettingsAreSetToDefaultValues(*settings, keyboard.is_external);
}

TEST_F(KeyboardPrefHandlerTest, UpdateLoginScreenKeyboardSettings) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.is_external = false;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeLoginScreenKeyboardSettings(account_id_1, keyboard);

  CheckKeyboardSettingsAreSetToDefaultValues(*settings, keyboard.is_external);
  mojom::KeyboardSettingsPtr updated_settings = mojo::Clone(settings);
  updated_settings->modifier_remappings = {
      {ui::mojom::ModifierKey::kAlt, ui::mojom::ModifierKey::kControl}};
  updated_settings->suppress_meta_fkey_rewrites =
      !updated_settings->suppress_meta_fkey_rewrites;
  updated_settings->top_row_are_fkeys = !updated_settings->top_row_are_fkeys;
  CallUpdateLoginScreenKeyboardSettings(account_id_1, kKeyboardKey1,
                                        *updated_settings);
  const auto& updated_settings_dict =
      GetInternalLoginScreenSettingsDict(account_id_1);
  CheckKeyboardSettingsAndDictAreEqual(*updated_settings,
                                       updated_settings_dict);
}

TEST_F(KeyboardPrefHandlerTest,
       LoginScreenPrefsNotPersistedWhenFlagIsDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
  mojom::Keyboard keyboard1;
  keyboard1.device_key = kKeyboardKey1;
  keyboard1.is_external = false;
  mojom::Keyboard keyboard2;
  keyboard2.device_key = kKeyboardKey2;
  keyboard2.is_external = true;
  CallInitializeLoginScreenKeyboardSettings(account_id_1, keyboard1);
  CallInitializeLoginScreenKeyboardSettings(account_id_1, keyboard2);
  EXPECT_FALSE(HasInternalLoginScreenSettingsDict(account_id_1));
  EXPECT_FALSE(HasExternalLoginScreenSettingsDict(account_id_1));
}

TEST_F(KeyboardPrefHandlerTest, PreservesOldSettings) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);

  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);

  // Set a fake key to simulate a setting being removed from 1 milestone to the
  // next.
  settings_dict->Set(kDictFakeKey, kDictFakeValue);
  pref_service_->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
                         std::move(devices_dict));

  // Update the settings again and verify the fake key and value still exist.
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);

  const auto& updated_devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref);
  const auto* updated_settings_dict =
      updated_devices_dict.FindDict(kKeyboardKey1);

  const std::string* value = updated_settings_dict->FindString(kDictFakeKey);
  ASSERT_NE(nullptr, value);
  EXPECT_EQ(kDictFakeValue, *value);
}

TEST_F(KeyboardPrefHandlerTest, LastUpdated) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);
  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);
  auto* time_stamp1 = settings_dict->Find(prefs::kLastUpdatedKey);
  ASSERT_NE(nullptr, time_stamp1);

  mojom::KeyboardSettingsPtr updated_settings = kKeyboardSettings1.Clone();
  updated_settings->top_row_are_fkeys = !updated_settings->top_row_are_fkeys;
  CallUpdateKeyboardSettings(kKeyboardKey1, *updated_settings);

  const auto& updated_devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref);
  const auto* updated_settings_dict =
      updated_devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, updated_settings_dict);
  auto* updated_time_stamp1 =
      updated_settings_dict->Find(prefs::kLastUpdatedKey);
  ASSERT_NE(nullptr, updated_time_stamp1);
  ASSERT_NE(time_stamp1, updated_time_stamp1);
}

TEST_F(KeyboardPrefHandlerTest, UpdateSettings) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);
  CallUpdateKeyboardSettings(kKeyboardKey2, kKeyboardSettings2,
                             /*is_external=*/true);

  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings1, *settings_dict,
                                       /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings2, *settings_dict,
                                       /*is_external=*/true);

  mojom::KeyboardSettingsPtr updated_settings = kKeyboardSettings1.Clone();
  updated_settings->modifier_remappings = {
      {ui::mojom::ModifierKey::kAlt, ui::mojom::ModifierKey::kControl}};
  updated_settings->suppress_meta_fkey_rewrites =
      !updated_settings->suppress_meta_fkey_rewrites;
  updated_settings->top_row_are_fkeys = !updated_settings->top_row_are_fkeys;

  // Update the settings again and verify the settings are updated in place.
  CallUpdateKeyboardSettings(kKeyboardKey1, *updated_settings,
                             /*is_external=*/true);

  const auto& updated_devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref);
  const auto* updated_settings_dict =
      updated_devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, updated_settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(
      *updated_settings, *updated_settings_dict, /*is_external=*/true);

  // Verify other device remains unmodified.
  const auto* unchanged_settings_dict =
      updated_devices_dict.FindDict(kKeyboardKey2);
  ASSERT_NE(nullptr, unchanged_settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(
      kKeyboardSettings2, *unchanged_settings_dict, /*is_external=*/true);
}

TEST_F(KeyboardPrefHandlerTest, NewSettingAddedRoundTrip) {
  mojom::KeyboardSettingsPtr test_settings = kKeyboardSettings1.Clone();
  test_settings->suppress_meta_fkey_rewrites =
      !kDefaultSuppressMetaFKeyRewrites;

  CallUpdateKeyboardSettings(kKeyboardKey1, *test_settings,
                             /*is_external=*/true);
  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);

  // Remove keys from the dict to mock adding a new setting in the future.
  settings_dict->Remove(prefs::kKeyboardSettingSuppressMetaFKeyRewrites);
  pref_service_->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
                         std::move(devices_dict));

  // Initialize keyboard settings for the device and check that
  // "new settings" match their default values.
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  EXPECT_EQ(kDefaultSuppressMetaFKeyRewrites,
            settings->suppress_meta_fkey_rewrites);

  // Reset "new settings" to the values that match `test_settings` and check
  // that the rest of the fields are equal.
  settings->suppress_meta_fkey_rewrites = !kDefaultSuppressMetaFKeyRewrites;
  EXPECT_EQ(*test_settings, *settings);
}

TEST_F(KeyboardPrefHandlerTest, DefaultSettingsWhenPrefServiceNull) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  pref_handler_->InitializeKeyboardSettings(nullptr, /*keyboard_policies=*/{},
                                            &keyboard);
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);
}

TEST_F(KeyboardPrefHandlerTest, NewKeyboardsDefaultSettings) {
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  EXPECT_EQ(*settings, kKeyboardSettingsExternalDefault);
  settings =
      CallInitializeKeyboardSettings(kKeyboardKey2, /*is_external=*/true);
  EXPECT_EQ(*settings, kKeyboardSettingsExternalDefault);
  settings =
      CallInitializeKeyboardSettings(kKeyboardKey3, /*is_external=*/true);
  EXPECT_EQ(*settings, kKeyboardSettingsExternalDefault);

  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  ASSERT_EQ(3u, devices_dict.size());
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsExternalDefault,
                                       *settings_dict, /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsExternalDefault,
                                       *settings_dict, /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsExternalDefault,
                                       *settings_dict, /*is_external=*/true);

  settings_dict = devices_dict.FindDict(kKeyboardKey3);
  ASSERT_NE(nullptr, settings_dict);
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsExternalDefault,
                                       *settings_dict, /*is_external=*/true);
}

TEST_F(KeyboardPrefHandlerTest,
       TopRowAreFKeysEnabledByDefaultForExternalKeyboard) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.is_external = kDefaultTopRowAreFKeysExternal;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);
  EXPECT_TRUE(settings->top_row_are_fkeys);

  mojom::Keyboard keyboard2;
  keyboard.device_key = kKeyboardKey2;
  mojom::KeyboardSettingsPtr settings2 =
      CallInitializeKeyboardSettings(keyboard2);
  // `top_row_are_fkeys` defaults to false for internal keyboards.
  EXPECT_FALSE(settings2->top_row_are_fkeys);
}

TEST_F(KeyboardPrefHandlerTest, InvalidModifierRemappings) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettings1,
                             /*is_external=*/true);
  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);

  base::Value::Dict invalid_modifier_remappings;
  invalid_modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kMaxValue) +
                           1),
      static_cast<int>(ui::mojom::ModifierKey::kControl));
  invalid_modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kMinValue) -
                           1),
      static_cast<int>(ui::mojom::ModifierKey::kControl));
  invalid_modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kControl)),
      static_cast<int>(ui::mojom::ModifierKey::kMaxValue) + 1);
  invalid_modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kAlt)),
      static_cast<int>(ui::mojom::ModifierKey::kMinValue) - 1);
  invalid_modifier_remappings.Set(
      base::NumberToString(
          static_cast<int>(ui::mojom::ModifierKey::kBackspace)),
      static_cast<int>(ui::mojom::ModifierKey::kFunction));

  // Set 1 valid modifier remapping to check that it skips invalid remappings,
  // and keeps the valid.
  invalid_modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kAlt)),
      static_cast<int>(ui::mojom::ModifierKey::kControl));

  // Set modifier remappings to the invalid set.
  settings_dict->Set(prefs::kKeyboardSettingModifierRemappings,
                     invalid_modifier_remappings.Clone());
  pref_service_->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
                         std::move(devices_dict));

  // Initialize keyboard settings for the device and check that the remapped
  // modifiers only include the valid value.
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);

  ASSERT_EQ(1u, settings->modifier_remappings.size());
  ASSERT_TRUE(base::Contains(settings->modifier_remappings,
                             ui::mojom::ModifierKey::kAlt));
  EXPECT_EQ(ui::mojom::ModifierKey::kControl,
            settings->modifier_remappings[ui::mojom::ModifierKey::kAlt]);
}

TEST_F(KeyboardPrefHandlerTest, KeyboardObserveredInTransitionPeriod) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  Shell::Get()->input_device_tracker()->OnKeyboardConnected(keyboard);
  // Initialize keyboard settings for the device and check that the global
  // prefs were used as defaults.
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard.device_key);
  ASSERT_EQ(settings->top_row_are_fkeys, kGlobalSendFunctionKeys);
  ASSERT_EQ(settings->suppress_meta_fkey_rewrites,
            kDefaultSuppressMetaFKeyRewrites);
}

TEST_F(KeyboardPrefHandlerTest,
       KeyboardSendFunctionKeysTransitionPrefAlwaysConsistent) {
  mojom::Keyboard keyboard;
  keyboard.is_external = true;
  keyboard.meta_key = ui::mojom::MetaKey::kExternalMeta;
  keyboard.device_key = kKeyboardKey1;
  {
    base::test::ScopedFeatureList disable_settings_split_feature_list;
    disable_settings_split_feature_list.InitAndDisableFeature(
        features::kInputDeviceSettingsSplit);
    Shell::Get()->input_device_tracker()->OnKeyboardConnected(keyboard);
  }

  // Initialize keyboard settings for the device and check that the global
  // prefs were used as defaults even though the `kSendFunctionKeys` pref was
  // never changed and it goes against the default since the keyboard is
  // external.
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);
  ASSERT_EQ(settings->top_row_are_fkeys, kGlobalSendFunctionKeys);
  ASSERT_EQ(settings->suppress_meta_fkey_rewrites,
            kDefaultSuppressMetaFKeyRewrites);
}

TEST_F(KeyboardPrefHandlerTest, ModifierRemappingsFromGlobalPrefs) {
  // Disable flag in this test since `InputDeviceTracker` only records
  // connected devices when the settings split flag is disabled.
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.modifier_keys = {
      ui::mojom::ModifierKey::kAlt,       ui::mojom::ModifierKey::kControl,
      ui::mojom::ModifierKey::kAssistant, ui::mojom::ModifierKey::kBackspace,
      ui::mojom::ModifierKey::kMeta,      ui::mojom::ModifierKey::kEscape};
  keyboard.meta_key = ui::mojom::MetaKey::kSearch;
  // Remap Alt + Meta keys.
  pref_service_->SetInteger(
      ::prefs::kLanguageRemapAltKeyTo,
      static_cast<int>(ui::mojom::ModifierKey::kCapsLock));
  pref_service_->SetInteger(::prefs::kLanguageRemapSearchKeyTo,
                            static_cast<int>(ui::mojom::ModifierKey::kEscape));

  Shell::Get()->input_device_tracker()->OnKeyboardConnected(keyboard);
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);

  ASSERT_EQ(settings->modifier_remappings.size(), 2u);
  ASSERT_EQ(settings->modifier_remappings.at(ui::mojom::ModifierKey::kAlt),
            ui::mojom::ModifierKey::kCapsLock);
  ASSERT_EQ(settings->modifier_remappings.at(ui::mojom::ModifierKey::kMeta),
            ui::mojom::ModifierKey::kEscape);
}

TEST_F(KeyboardPrefHandlerTest, SwitchControlAndCommandForAppleKeyboard) {
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.meta_key = ui::mojom::MetaKey::kCommand;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);

  ASSERT_TRUE(
      settings->modifier_remappings.contains(ui::mojom::ModifierKey::kControl));
  ASSERT_EQ(settings->modifier_remappings.at(ui::mojom::ModifierKey::kControl),
            ui::mojom::ModifierKey::kMeta);
  ASSERT_TRUE(
      settings->modifier_remappings.contains(ui::mojom::ModifierKey::kMeta));
  ASSERT_EQ(settings->modifier_remappings.at(ui::mojom::ModifierKey::kMeta),
            ui::mojom::ModifierKey::kControl);
}

TEST_F(KeyboardPrefHandlerTest, DefaultNotPersistedUntilUpdated) {
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettingsDefault);

  // Default settings are not persisted to storage.
  const auto* settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_FALSE(
      settings_dict->contains(prefs::kKeyboardSettingSuppressMetaFKeyRewrites));
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsDefault,
                                       *settings_dict);

  // When the settings are updated, their values should be persisted.
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettingsNotDefault);
  settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_TRUE(
      settings_dict->contains(prefs::kKeyboardSettingSuppressMetaFKeyRewrites));
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsNotDefault,
                                       *settings_dict);

  // When the settings are set back to the default, values should be persisted
  // as they are now "user chosen".
  CallUpdateKeyboardSettings(kKeyboardKey1, kKeyboardSettingsDefault);
  settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_TRUE(
      settings_dict->contains(prefs::kKeyboardSettingSuppressMetaFKeyRewrites));
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettingsDefault,
                                       *settings_dict);
}

TEST_F(KeyboardPrefHandlerTest,
       NewKeyboard_ManagedEnterprisePolicy_GetsDefaults) {
  mojom::KeyboardPolicies policies;
  policies.top_row_are_fkeys_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kManaged, !kDefaultTopRowAreFKeys);
  policies.enable_meta_fkey_rewrites_policy =
      mojom::InputDeviceSettingsPolicy::New(mojom::PolicyStatus::kManaged,
                                            kDefaultSuppressMetaFKeyRewrites);
  policies.f11_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kManaged, ui::mojom::ExtendedFkeysModifier::kShift);
  policies.f12_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kManaged, ui::mojom::ExtendedFkeysModifier::kAlt);
  policies.home_and_end_keys_policy =
      mojom::InputDeviceSettingsSixPackKeyPolicy::New(
          mojom::PolicyStatus::kManaged,
          ui::mojom::SixPackShortcutModifier::kSearch);
  policies.page_up_and_page_down_keys_policy =
      mojom::InputDeviceSettingsSixPackKeyPolicy::New(
          mojom::PolicyStatus::kManaged,
          ui::mojom::SixPackShortcutModifier::kAlt);
  policies.delete_key_policy = mojom::InputDeviceSettingsSixPackKeyPolicy::New(
      mojom::PolicyStatus::kManaged, ui::mojom::SixPackShortcutModifier::kAlt);
  policies.insert_key_policy = mojom::InputDeviceSettingsSixPackKeyPolicy::New(
      mojom::PolicyStatus::kManaged, ui::mojom::SixPackShortcutModifier::kNone);

  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.meta_key = ui::mojom::MetaKey::kSearch;

  pref_handler_->InitializeKeyboardSettings(pref_service_.get(), policies,
                                            &keyboard);

  EXPECT_EQ(!kDefaultTopRowAreFKeys, keyboard.settings->top_row_are_fkeys);
  // For a non-external keyboard, the value of the EnabledMetaFkeyRewrites
  // policy doesn't affect the value of the setting.
  EXPECT_EQ(kDefaultSuppressMetaFKeyRewrites,
            keyboard.settings->suppress_meta_fkey_rewrites);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kShift, keyboard.settings->f11);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kAlt, keyboard.settings->f12);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            keyboard.settings->six_pack_key_remappings->page_up);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            keyboard.settings->six_pack_key_remappings->page_down);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            keyboard.settings->six_pack_key_remappings->home);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            keyboard.settings->six_pack_key_remappings->end);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            keyboard.settings->six_pack_key_remappings->del);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kNone,
            keyboard.settings->six_pack_key_remappings->insert);
  keyboard.settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
  keyboard.settings->f11 = kDefaultFkey;
  keyboard.settings->f12 = kDefaultFkey;
  keyboard.settings->six_pack_key_remappings = mojom::SixPackKeyInfo::New();

  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  const auto* settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_FALSE(
      settings_dict->contains(prefs::kKeyboardSettingSuppressMetaFKeyRewrites));

  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingF11));
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingF12));
}

TEST_F(KeyboardPrefHandlerTest,
       NewKeyboard_RecommendedEnterprisePolicy_GetsDefaults) {
  mojom::KeyboardPolicies policies;
  policies.top_row_are_fkeys_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kRecommended, !kDefaultTopRowAreFKeys);
  policies.enable_meta_fkey_rewrites_policy =
      mojom::InputDeviceSettingsPolicy::New(mojom::PolicyStatus::kRecommended,
                                            !kDefaultSuppressMetaFKeyRewrites);
  policies.f11_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kCtrlShift);
  policies.f12_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kAlt);
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.meta_key = ui::mojom::MetaKey::kSearch;

  pref_handler_->InitializeKeyboardSettings(pref_service_.get(), policies,
                                            &keyboard);

  EXPECT_EQ(!kDefaultTopRowAreFKeys, keyboard.settings->top_row_are_fkeys);
  // For a non-external keyboard, the value of the EnabledMetaFkeyRewrites
  // policy doesn't affect the value of the setting.
  EXPECT_EQ(kDefaultSuppressMetaFKeyRewrites,
            keyboard.settings->suppress_meta_fkey_rewrites);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kCtrlShift,
            keyboard.settings->f11);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kAlt, keyboard.settings->f12);
  keyboard.settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
  keyboard.settings->f11 = kDefaultFkey;
  keyboard.settings->f12 = kDefaultFkey;
  keyboard.settings->suppress_meta_fkey_rewrites =
      kDefaultSuppressMetaFKeyRewrites;
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  const auto* settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingF11));
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingF12));
}

TEST_F(KeyboardPrefHandlerTest,
       ExistingKeyboard_RecommendedEnterprisePolicy_GetsNewPolicy) {
  mojom::KeyboardPolicies policies;
  policies.top_row_are_fkeys_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kRecommended, !kDefaultTopRowAreFKeys);
  policies.enable_meta_fkey_rewrites_policy =
      mojom::InputDeviceSettingsPolicy::New(mojom::PolicyStatus::kRecommended,
                                            kDefaultSuppressMetaFKeyRewrites);
  policies.f11_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kCtrlShift);
  policies.f12_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kAlt);
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.meta_key = ui::mojom::MetaKey::kSearch;

  pref_handler_->InitializeKeyboardSettings(
      pref_service_.get(), /*keyboard_policies=*/{}, &keyboard);
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  pref_handler_->InitializeKeyboardSettings(pref_service_.get(), policies,
                                            &keyboard);
  EXPECT_EQ(!kDefaultTopRowAreFKeys, keyboard.settings->top_row_are_fkeys);
  EXPECT_EQ(!kDefaultSuppressMetaFKeyRewrites,
            keyboard.settings->suppress_meta_fkey_rewrites);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kCtrlShift,
            keyboard.settings->f11);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kAlt, keyboard.settings->f12);
  keyboard.settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
  keyboard.settings->suppress_meta_fkey_rewrites =
      kDefaultSuppressMetaFKeyRewrites;
  keyboard.settings->f11 = kDefaultFkey;
  keyboard.settings->f12 = kDefaultFkey;
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  const auto* settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
}

TEST_F(KeyboardPrefHandlerTest,
       ExistingKeyboard_ManagedEnterprisePolicy_GetsNewPolicy) {
  mojom::KeyboardPolicies policies;
  policies.top_row_are_fkeys_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kManaged, !kDefaultTopRowAreFKeys);
  policies.enable_meta_fkey_rewrites_policy =
      mojom::InputDeviceSettingsPolicy::New(mojom::PolicyStatus::kManaged,
                                            kDefaultSuppressMetaFKeyRewrites);
  policies.f11_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kCtrlShift);
  policies.f12_key_policy = mojom::InputDeviceSettingsFkeyPolicy::New(
      mojom::PolicyStatus::kRecommended,
      ui::mojom::ExtendedFkeysModifier::kAlt);
  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.meta_key = ui::mojom::MetaKey::kSearch;

  pref_handler_->InitializeKeyboardSettings(
      pref_service_.get(), /*keyboard_policies=*/{}, &keyboard);
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  keyboard.settings->top_row_are_fkeys = !kDefaultTopRowAreFKeys;
  keyboard.settings->suppress_meta_fkey_rewrites =
      !kDefaultSuppressMetaFKeyRewrites;
  keyboard.settings->f11 = ui::mojom::ExtendedFkeysModifier::kAlt;
  keyboard.settings->f12 = ui::mojom::ExtendedFkeysModifier::kAlt;
  CallUpdateKeyboardSettings(kKeyboardKey1, *keyboard.settings);

  pref_handler_->InitializeKeyboardSettings(pref_service_.get(), policies,
                                            &keyboard);
  EXPECT_EQ(!kDefaultTopRowAreFKeys, keyboard.settings->top_row_are_fkeys);
  EXPECT_EQ(!kDefaultSuppressMetaFKeyRewrites,
            keyboard.settings->suppress_meta_fkey_rewrites);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kAlt, keyboard.settings->f11);
  EXPECT_EQ(ui::mojom::ExtendedFkeysModifier::kAlt, keyboard.settings->f12);
  keyboard.settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
  keyboard.settings->suppress_meta_fkey_rewrites =
      kDefaultSuppressMetaFKeyRewrites;
  keyboard.settings->f11 = kDefaultFkey;
  keyboard.settings->f12 = kDefaultFkey;
  EXPECT_EQ(kKeyboardSettingsDefault, *keyboard.settings);

  const auto* settings_dict = GetSettingsDictForDeviceKey(kKeyboardKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kKeyboardSettingTopRowAreFKeys));
  EXPECT_EQ(
      !kDefaultTopRowAreFKeys,
      settings_dict->FindBool(prefs::kKeyboardSettingTopRowAreFKeys).value());
  EXPECT_TRUE(settings_dict->contains(prefs::kKeyboardSettingF11));
  EXPECT_TRUE(settings_dict->contains(prefs::kKeyboardSettingF12));
  EXPECT_NE(static_cast<int>(kDefaultFkey),
            settings_dict->FindInt(prefs::kKeyboardSettingF11).value());
  EXPECT_NE(static_cast<int>(kDefaultFkey),
            settings_dict->FindInt(prefs::kKeyboardSettingF12).value());
}

TEST_F(KeyboardPrefHandlerTest,
       ExternalKeyboard_SuppressMetaFkeyRewritesPolicy) {
  mojom::KeyboardPolicies policies;
  policies.enable_meta_fkey_rewrites_policy =
      mojom::InputDeviceSettingsPolicy::New(mojom::PolicyStatus::kManaged,
                                            kDefaultSuppressMetaFKeyRewrites);

  mojom::Keyboard keyboard;
  keyboard.device_key = kKeyboardKey1;
  keyboard.is_external = true;

  pref_handler_->InitializeKeyboardSettings(pref_service_.get(), policies,
                                            &keyboard);

  EXPECT_EQ(!kDefaultSuppressMetaFKeyRewrites,
            keyboard.settings->suppress_meta_fkey_rewrites);

  const auto* settings_dict =
      GetSettingsDictForDeviceKey(kKeyboardKey1, /*is_external=*/true);
  EXPECT_FALSE(
      settings_dict->contains(prefs::kKeyboardSettingSuppressMetaFKeyRewrites));
}

TEST_F(KeyboardPrefHandlerTest, SixPackKeyRemappingsFlagDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      features::kAltClickAndSixPackCustomization);
  CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  const auto* settings_dict =
      GetSettingsDictForDeviceKey(kKeyboardKey1, /*is_external=*/true);
  EXPECT_FALSE(
      settings_dict->contains(prefs::kKeyboardSettingSixPackKeyRemappings));
}

TEST_F(KeyboardPrefHandlerTest, SixPackKeyRemappings_Default) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(features::kAltClickAndSixPackCustomization);

  auto settings = CallInitializeKeyboardSettings(kKeyboardKey1);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->page_up);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->page_down);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->home);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->end);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->del);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kSearch,
            settings->six_pack_key_remappings->insert);
}

TEST_F(KeyboardPrefHandlerTest, SixPackKeyRemappings_Group) {
  {
    base::test::ScopedFeatureList disable_feature_list;
    disable_feature_list.InitAndDisableFeature(
        features::kAltClickAndSixPackCustomization);
    CallInitializeKeyboardSettings(kKeyboardKey1);
  }

  pref_service_->SetUserPref(prefs::kKeyEventRemappedToSixPackPageUp,
                             base::Value(15));
  pref_service_->SetUserPref(prefs::kKeyEventRemappedToSixPackPageDown,
                             base::Value(0));
  // `kAlt` is the expected default since the `PageUp` pref has a positive
  // value and the PageDown pref rewrite is either unused or usage is split
  // equally between both the Search and Alt rewrites.
  auto settings = CallInitializeKeyboardSettings(kKeyboardKey1);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            settings->six_pack_key_remappings->page_up);
}

TEST_F(KeyboardPrefHandlerTest, SixPackKeyRemappings_RetrieveSettings) {
  {
    base::test::ScopedFeatureList disable_feature_list;
    disable_feature_list.InitAndDisableFeature(
        features::kAltClickAndSixPackCustomization);
    CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  }

  pref_service_->SetUserPref(prefs::kKeyEventRemappedToSixPackPageUp,
                             base::Value(15));
  auto settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            settings->six_pack_key_remappings->page_up);
  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kKeyboardKey1);
  EXPECT_TRUE(
      settings_dict->contains(prefs::kKeyboardSettingSixPackKeyRemappings));
  EXPECT_EQ(
      *settings_dict->FindDict(prefs::kKeyboardSettingSixPackKeyRemappings)
           ->FindInt(prefs::kSixPackKeyPageUp),
      static_cast<int>(ui::mojom::SixPackShortcutModifier::kAlt));
  settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  EXPECT_EQ(ui::mojom::SixPackShortcutModifier::kAlt,
            settings->six_pack_key_remappings->page_up);
}

TEST_F(KeyboardPrefHandlerTest,
       NewSplitModifierKeyboardUseDefaultsFromLastUpdatedSettings) {
  mojom::Keyboard split_modifier_keyboard;
  split_modifier_keyboard.device_key = kKeyboardKey3;
  split_modifier_keyboard.meta_key = ui::mojom::MetaKey::kLauncher;
  split_modifier_keyboard.modifier_keys = {ui::mojom::ModifierKey::kFunction};

  base::Value::Dict dict1;
  base::Value::Dict modifier_remappings;
  modifier_remappings.Set(
      base::NumberToString(static_cast<int>(ui::mojom::ModifierKey::kFunction)),
      static_cast<int>(ui::mojom::ModifierKey::kControl));
  dict1.Set(prefs::kKeyboardSettingModifierRemappings,
            modifier_remappings.Clone());

  pref_service_->SetDict(prefs::kKeyboardDefaultSplitModifierSettings,
                         dict1.Clone());

  pref_handler_->InitializeKeyboardSettings(
      pref_service_.get(), /*keyboard_policies=*/{}, &split_modifier_keyboard);

  CheckKeyboardSettingsAndDictAreEqual(*split_modifier_keyboard.settings, dict1,
                                       /*is_external=*/false);
}

TEST_F(KeyboardPrefHandlerTest, UpdateSplitModifierKeyboardDefaultSettings) {
  CallUpdateDefaultSplitModifierKeyboardSettings(kKeyboardKey1,
                                                 kKeyboardSettings3);

  const auto& default_split_modifier_settings =
      pref_service_->GetDict(prefs::kKeyboardDefaultSplitModifierSettings);
  EXPECT_FALSE(default_split_modifier_settings.empty());
  CheckKeyboardSettingsAndDictAreEqual(kKeyboardSettings3,
                                       default_split_modifier_settings,
                                       /*is_external=*/false);
}

TEST_F(KeyboardPrefHandlerTest, RememberDefaultsFromLastUpdatedSettings) {
  mojom::Keyboard chromeos_keyboard;
  chromeos_keyboard.device_key = kKeyboardKey2;
  chromeos_keyboard.meta_key = ui::mojom::MetaKey::kLauncher;
  chromeos_keyboard.is_external = true;

  mojom::Keyboard non_chromeos_keyboard;
  non_chromeos_keyboard.device_key = kKeyboardKey3;
  non_chromeos_keyboard.meta_key = ui::mojom::MetaKey::kExternalMeta;
  non_chromeos_keyboard.modifier_keys = {ui::mojom::ModifierKey::kControl};
  non_chromeos_keyboard.is_external = true;

  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);
  settings->top_row_are_fkeys = !kDefaultTopRowAreFKeys;
  settings->suppress_meta_fkey_rewrites = !kDefaultSuppressMetaFKeyRewrites;

  CallUpdateKeyboardSettings(kKeyboardKey1, *settings, /*is_external=*/true);
  CallUpdateDefaultChromeOSKeyboardSettings(kKeyboardKey1, *settings);

  pref_handler_->InitializeKeyboardSettings(
      pref_service_.get(), /*keyboard_policies=*/{}, &chromeos_keyboard);
  EXPECT_EQ(*settings, *chromeos_keyboard.settings);

  settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
  settings->suppress_meta_fkey_rewrites = kDefaultSuppressMetaFKeyRewrites;
  settings->modifier_remappings[ui::mojom::ModifierKey::kControl] =
      ui::mojom::ModifierKey::kVoid;
  settings->f11.reset();
  settings->f12.reset();
  CallUpdateDefaultNonChromeOSKeyboardSettings(kKeyboardKey1, *settings);

  pref_handler_->InitializeKeyboardSettings(
      pref_service_.get(), /*keyboard_policies=*/{}, &non_chromeos_keyboard);
  EXPECT_EQ(*settings, *non_chromeos_keyboard.settings);
}

TEST_F(KeyboardPrefHandlerTest, SettingsUpdateMetricTest) {
  const auto settings1 =
      CallInitializeKeyboardSettings(kKeyboardKey1, /*is_external=*/true);

  // When its the first device of the type the category should be kFirstEver.
  {
    const auto& metric_dict =
        pref_service_->GetDict(prefs::kKeyboardUpdateSettingsMetricInfo);
    ASSERT_TRUE(metric_dict.contains(kKeyboardKey1));

    auto metrics_info = SettingsUpdatedMetricsInfo::FromDict(
        *metric_dict.FindDict(kKeyboardKey1));
    ASSERT_TRUE(metrics_info);
    EXPECT_EQ(SettingsUpdatedMetricsInfo::Category::kFirstEver,
              metrics_info->category());
  }

  // When its taken off the the defaults, the category should be kDefault.
  {
    CallUpdateDefaultChromeOSKeyboardSettings(kKeyboardKey1, *settings1);
    CallInitializeKeyboardSettings(kKeyboardKey2, /*is_external=*/true);
    const auto& metric_dict =
        pref_service_->GetDict(prefs::kKeyboardUpdateSettingsMetricInfo);
    ASSERT_TRUE(metric_dict.contains(kKeyboardKey2));

    auto metrics_info = SettingsUpdatedMetricsInfo::FromDict(
        *metric_dict.FindDict(kKeyboardKey2));
    ASSERT_TRUE(metrics_info);
    EXPECT_EQ(SettingsUpdatedMetricsInfo::Category::kDefault,
              metrics_info->category());
  }

  // When its taken from synced prefs on a different device, category should
  // match.
  {
    auto devices_dict =
        pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
    devices_dict.Set(kKeyboardKey3, base::Value::Dict());
    pref_service_->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
                           std::move(devices_dict));

    CallInitializeKeyboardSettings(kKeyboardKey3, /*is_external=*/true);
    const auto& metric_dict =
        pref_service_->GetDict(prefs::kKeyboardUpdateSettingsMetricInfo);
    ASSERT_TRUE(metric_dict.contains(kKeyboardKey3));

    auto metrics_info = SettingsUpdatedMetricsInfo::FromDict(
        *metric_dict.FindDict(kKeyboardKey3));
    ASSERT_TRUE(metrics_info);
    EXPECT_EQ(SettingsUpdatedMetricsInfo::Category::kSynced,
              metrics_info->category());
  }
}

TEST_F(KeyboardPrefHandlerTest,
       InitializeSplitModifierKeyboardPreFeatureEnable) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kModifierSplit);

  ui::DeviceDataManagerTestApi().SetKeyboardDevices(
      {kSampleSplitModifierKeyboard});

  mojom::Keyboard keyboard;
  keyboard.id = kSampleSplitModifierKeyboard.id;
  keyboard.is_external = false;
  keyboard.modifier_keys = {ui::mojom::ModifierKey::kAssistant};
  pref_handler_->InitializeKeyboardSettings(nullptr, /*keyboard_policies=*/{},
                                            &keyboard);
  const auto& settings = keyboard.settings;
  EXPECT_EQ(1u, settings->modifier_remappings.size());
  ASSERT_TRUE(settings->modifier_remappings.contains(
      ui::mojom::ModifierKey::kAssistant));
  EXPECT_EQ(
      ui::mojom::ModifierKey::kCapsLock,
      settings->modifier_remappings.at(ui::mojom::ModifierKey::kAssistant));
}

TEST_F(KeyboardPrefHandlerTest,
       InitializeSplitModifierKeyboardPostFeatureEnable) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(features::kModifierSplit);
  auto reset = switches::SetIgnoreModifierSplitSecretKeyForTest();
  Shell::Get()
      ->keyboard_capability()
      ->ResetModifierSplitDogfoodControllerForTesting();

  ui::DeviceDataManagerTestApi().SetKeyboardDevices(
      {kSampleSplitModifierKeyboard});

  mojom::Keyboard keyboard;
  keyboard.id = kSampleSplitModifierKeyboard.id;
  keyboard.is_external = false;
  keyboard.modifier_keys = {ui::mojom::ModifierKey::kAssistant};
  pref_handler_->InitializeKeyboardSettings(nullptr, /*keyboard_policies=*/{},
                                            &keyboard);
  const auto& settings = keyboard.settings;
  EXPECT_EQ(0u, settings->modifier_remappings.size());
}

class KeyboardSettingsPrefConversionTest
    : public KeyboardPrefHandlerTest,
      public testing::WithParamInterface<
          std::tuple<std::string, const mojom::KeyboardSettings*>> {
 public:
  KeyboardSettingsPrefConversionTest() = default;
  KeyboardSettingsPrefConversionTest(
      const KeyboardSettingsPrefConversionTest&) = delete;
  KeyboardSettingsPrefConversionTest& operator=(
      const KeyboardSettingsPrefConversionTest&) = delete;
  ~KeyboardSettingsPrefConversionTest() override = default;

  // testing::Test:
  void SetUp() override {
    KeyboardPrefHandlerTest::SetUp();
    std::tie(device_key_, settings_) = GetParam();
  }

 protected:
  std::string device_key_;
  raw_ptr<const mojom::KeyboardSettings> settings_;
};

INSTANTIATE_TEST_SUITE_P(
    // Empty to simplify gtest output
    ,
    KeyboardSettingsPrefConversionTest,
    testing::Combine(
        testing::Values(kKeyboardKey1, kKeyboardKey2, kKeyboardKey3),
        testing::Values(&kKeyboardSettings1,
                        &kKeyboardSettings2,
                        &kKeyboardSettings3)));

TEST_P(KeyboardSettingsPrefConversionTest, CheckConversion) {
  CallUpdateKeyboardSettings(device_key_, *settings_);

  auto* settings_dict = GetSettingsDictForDeviceKey(device_key_);
  CheckKeyboardSettingsAndDictAreEqual(*settings_, *settings_dict);
}

TEST_P(KeyboardSettingsPrefConversionTest, CheckRoundtripConversion) {
  CallUpdateKeyboardSettings(device_key_, *settings_);

  auto* settings_dict = GetSettingsDictForDeviceKey(device_key_);
  CheckKeyboardSettingsAndDictAreEqual(*settings_, *settings_dict);

  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(device_key_);
  EXPECT_EQ(*settings_, *settings);
}

TEST_P(KeyboardSettingsPrefConversionTest,
       CheckRoundtripConversionWithExtraKey) {
  CallUpdateKeyboardSettings(device_key_, *settings_, /*is_external=*/true);

  auto devices_dict =
      pref_service_->GetDict(prefs::kKeyboardDeviceSettingsDictPref).Clone();
  ASSERT_EQ(1u, devices_dict.size());
  auto* settings_dict = devices_dict.FindDict(device_key_);
  ASSERT_NE(nullptr, settings_dict);

  // Set a fake key to simulate a setting being removed from 1 milestone to
  // the next.
  settings_dict->Set(kDictFakeKey, kDictFakeValue);
  pref_service_->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
                         std::move(devices_dict));

  CheckKeyboardSettingsAndDictAreEqual(*settings_, *settings_dict,
                                       /*is_external=*/true);

  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(device_key_, /*is_external=*/true);
  EXPECT_EQ(*settings_, *settings);
}

TEST_F(KeyboardPrefHandlerTest, ExtendedFkeysReceiveDefaultSettings) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(::features::kSupportF11AndF12KeyShortcuts);
  mojom::Keyboard keyboard;
  keyboard.is_external = false;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);
  EXPECT_EQ(kDefaultFkey, settings->f11);
  EXPECT_EQ(kDefaultFkey, settings->f12);
}

TEST_F(KeyboardPrefHandlerTest, ExtendedFkeysOnlyAddedForChromeOSKeyboards) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(::features::kSupportF11AndF12KeyShortcuts);
  mojom::Keyboard keyboard;
  keyboard.meta_key = ui::mojom::MetaKey::kCommand;
  mojom::KeyboardSettingsPtr settings =
      CallInitializeKeyboardSettings(keyboard);
  EXPECT_FALSE(settings->f11.has_value());
  EXPECT_FALSE(settings->f12.has_value());
}

TEST_F(KeyboardPrefHandlerTest, AppleKeyboardDefaultRemappingsNotSaved) {
  mojom::Keyboard keyboard;
  keyboard.meta_key = ui::mojom::MetaKey::kCommand;
  keyboard.device_key = kKeyboardKey1;
  keyboard.is_external = true;

  // Initial settings for Apple keyboard contain Control -> Meta and Meta ->
  // Control.
  keyboard.settings = CallInitializeKeyboardSettings(keyboard);
  EXPECT_EQ(
      ui::mojom::ModifierKey::kControl,
      keyboard.settings->modifier_remappings.at(ui::mojom::ModifierKey::kMeta));
  EXPECT_EQ(ui::mojom::ModifierKey::kMeta,
            keyboard.settings->modifier_remappings.at(
                ui::mojom::ModifierKey::kControl));

  // Since this is the default remapping, the dict in prefs should be empty.
  auto* settings_dict =
      GetSettingsDictForDeviceKey(kKeyboardKey1, /*is_external=*/true);
  ASSERT_TRUE(settings_dict);
  auto* modifier_remapping_dict =
      settings_dict->FindDict(prefs::kKeyboardSettingModifierRemappings);
  ASSERT_TRUE(modifier_remapping_dict);
  EXPECT_TRUE(modifier_remapping_dict->empty());

  keyboard.settings->modifier_remappings.erase(ui::mojom::ModifierKey::kMeta);
  CallUpdateKeyboardSettings(keyboard);

  // Since Meta -> Meta now, this entry should exist in prefs.
  settings_dict =
      GetSettingsDictForDeviceKey(kKeyboardKey1, /*is_external=*/true);
  ASSERT_TRUE(settings_dict);
  modifier_remapping_dict =
      settings_dict->FindDict(prefs::kKeyboardSettingModifierRemappings);
  ASSERT_TRUE(modifier_remapping_dict);
  ASSERT_FALSE(modifier_remapping_dict->empty());
  EXPECT_EQ(static_cast<int>(ui::mojom::ModifierKey::kMeta),
            *modifier_remapping_dict->FindInt(base::ToString(
                static_cast<int>(ui::mojom::ModifierKey::kMeta))));

  // Verify when re-initialized from prefs that Meta -> Meta and Ctrl -> Meta.
  keyboard.settings = CallInitializeKeyboardSettings(keyboard);
  EXPECT_FALSE(keyboard.settings->modifier_remappings.contains(
      ui::mojom::ModifierKey::kMeta));
  EXPECT_EQ(ui::mojom::ModifierKey::kMeta,
            keyboard.settings->modifier_remappings.at(
                ui::mojom::ModifierKey::kControl));
}

}  // namespace ash