chromium/ash/system/input_device_settings/pref_handlers/mouse_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/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.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/settings_updated_metrics_info.h"
#include "ash/test/ash_test_base.h"
#include "base/strings/strcat.h"
#include "base/test/scoped_feature_list.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 "mojo/public/cpp/bindings/clone_traits.h"

namespace ash {

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

const std::string kMouseKey1 = "device_key1";
const std::string kMouseKey2 = "device_key2";
const std::string kMouseKey3 = "device_key3";

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 bool kTestSwapRight = false;
const int kTestSensitivity = 2;
const bool kTestReverseScrolling = false;
const bool kTestAccelerationEnabled = false;
const int kTestScrollSensitivity = 3;
const bool kTestScrollAcceleration = false;

const mojom::ButtonRemapping button_remapping1(
    /*name=*/"test1",
    /*button=*/
    mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kMiddle),
    /*remapping_action=*/
    mojom::RemappingAction::NewAcceleratorAction(
        ash::AcceleratorAction::kBrightnessDown));

const mojom::ButtonRemapping button_remapping2(
    /*name=*/"test2",
    /*button=*/mojom::Button::NewVkey(::ui::KeyboardCode::VKEY_1),
    /*remapping_action=*/
    mojom::RemappingAction::NewStaticShortcutAction(
        mojom::StaticShortcutAction::kCopy));

const mojom::MouseSettings kMouseSettingsDefault(
    /*swap_right=*/kDefaultSwapRight,
    /*sensitivity=*/kDefaultSensitivity,
    /*reverse_scrolling=*/kDefaultReverseScrolling,
    /*acceleration_enabled=*/kDefaultAccelerationEnabled,
    /*scroll_sensitivity=*/kDefaultScrollSensitivity,
    /*scroll_acceleration=*/kDefaultScrollAccelerationEnabled,
    /*button_remappings=*/std::vector<mojom::ButtonRemappingPtr>());

const mojom::MouseSettings kMouseSettingsNotDefault(
    /*swap_right=*/!kDefaultSwapRight,
    /*sensitivity=*/1,
    /*reverse_scrolling=*/!kDefaultReverseScrolling,
    /*acceleration_enabled=*/!kDefaultAccelerationEnabled,
    /*scroll_sensitivity=*/1,
    /*scroll_acceleration=*/!kDefaultScrollAccelerationEnabled,
    /*button_remappings=*/std::vector<mojom::ButtonRemappingPtr>());

const mojom::MouseSettings kMouseSettings1(
    /*swap_right=*/false,
    /*sensitivity=*/1,
    /*reverse_scrolling=*/false,
    /*acceleration_enabled=*/false,
    /*scroll_sensitivity=*/1,
    /*scroll_acceleration=*/false,
    /*button_remappings=*/std::vector<mojom::ButtonRemappingPtr>());

const mojom::MouseSettings kMouseSettings2(
    /*swap_right=*/true,
    /*sensitivity=*/10,
    /*reverse_scrolling=*/true,
    /*acceleration_enabled=*/true,
    /*scroll_sensitivity=*/24,
    /*scroll_acceleration=*/true,
    /*button_remappings=*/std::vector<mojom::ButtonRemappingPtr>());
}  // namespace

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

  // testing::Test:
  void SetUp() override {
    scoped_feature_list_.InitWithFeatures({features::kPeripheralCustomization,
                                           features::kInputDeviceSettingsSplit},
                                          {});
    AshTestBase::SetUp();
    InitializePrefService();
    pref_handler_ = std::make_unique<MousePrefHandlerImpl>();
  }

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

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

    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kMouseDeviceSettingsDictPref);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kMouseButtonRemappingsDictPref);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kMouseDefaultSettings);
    pref_service_->registry()->RegisterDictionaryPref(
        prefs::kMouseUpdateSettingsMetricInfo);

    // We are using these test constants as a a way to differentiate values
    // retrieved from prefs or default mouse settings.
    pref_service_->registry()->RegisterBooleanPref(
        prefs::kPrimaryMouseButtonRight, kDefaultSwapRight);
    pref_service_->registry()->RegisterIntegerPref(prefs::kMouseSensitivity,
                                                   kDefaultSensitivity);
    pref_service_->registry()->RegisterBooleanPref(prefs::kMouseReverseScroll,
                                                   kDefaultReverseScrolling);
    pref_service_->registry()->RegisterBooleanPref(prefs::kMouseAcceleration,
                                                   kDefaultAccelerationEnabled);
    pref_service_->registry()->RegisterIntegerPref(
        prefs::kMouseScrollSensitivity, kDefaultScrollSensitivity);
    pref_service_->registry()->RegisterBooleanPref(
        prefs::kMouseScrollAcceleration, kDefaultScrollAccelerationEnabled);

    pref_service_->SetUserPref(prefs::kPrimaryMouseButtonRight,
                               base::Value(kTestSwapRight));
    pref_service_->SetUserPref(prefs::kMouseSensitivity,
                               base::Value(kTestSensitivity));
    pref_service_->SetUserPref(prefs::kMouseReverseScroll,
                               base::Value(kTestReverseScrolling));
    pref_service_->SetUserPref(prefs::kMouseAcceleration,
                               base::Value(kTestAccelerationEnabled));
    pref_service_->SetUserPref(prefs::kMouseScrollSensitivity,
                               base::Value(kTestScrollSensitivity));
    pref_service_->SetUserPref(prefs::kMouseScrollAcceleration,
                               base::Value(kTestScrollAcceleration));
  }

  void CheckMouseSettingsAndDictAreEqual(
      const mojom::MouseSettings& settings,
      const base::Value::Dict& settings_dict) {
    const auto swap_right =
        settings_dict.FindBool(prefs::kMouseSettingSwapRight);
    if (swap_right.has_value()) {
      EXPECT_EQ(settings.swap_right, swap_right);
    } else {
      EXPECT_EQ(settings.swap_right, kDefaultSwapRight);
    }

    const auto sensitivity =
        settings_dict.FindInt(prefs::kMouseSettingSensitivity);
    if (sensitivity.has_value()) {
      EXPECT_EQ(settings.sensitivity, sensitivity);
    } else {
      EXPECT_EQ(settings.sensitivity, kDefaultSensitivity);
    }

    const auto reverse_scrolling =
        settings_dict.FindBool(prefs::kMouseSettingReverseScrolling);
    if (reverse_scrolling.has_value()) {
      EXPECT_EQ(settings.reverse_scrolling, reverse_scrolling);
    } else {
      EXPECT_EQ(settings.reverse_scrolling, kDefaultReverseScrolling);
    }

    const auto acceleration_enabled =
        settings_dict.FindBool(prefs::kMouseSettingAccelerationEnabled);
    if (acceleration_enabled.has_value()) {
      EXPECT_EQ(settings.acceleration_enabled, acceleration_enabled);
    } else {
      EXPECT_EQ(settings.acceleration_enabled, kDefaultAccelerationEnabled);
    }

    const auto scroll_sensitivity =
        settings_dict.FindInt(prefs::kMouseSettingScrollSensitivity);
    if (scroll_sensitivity.has_value()) {
      EXPECT_EQ(settings.scroll_sensitivity, scroll_sensitivity);
    } else {
      EXPECT_EQ(settings.scroll_sensitivity, kDefaultScrollSensitivity);
    }

    const auto scroll_acceleration =
        settings_dict.FindBool(prefs::kMouseSettingScrollAcceleration);
    if (scroll_acceleration.has_value()) {
      EXPECT_EQ(settings.scroll_acceleration, scroll_acceleration);
    } else {
      EXPECT_EQ(settings.scroll_acceleration,
                kDefaultScrollAccelerationEnabled);
    }
  }

  void CheckMouseSettingsAreSetToDefaultValues(
      const mojom::MouseSettings& settings) {
    EXPECT_EQ(kMouseSettingsDefault.swap_right, settings.swap_right);
    EXPECT_EQ(kMouseSettingsDefault.sensitivity, settings.sensitivity);
    EXPECT_EQ(kMouseSettingsDefault.reverse_scrolling,
              settings.reverse_scrolling);
    EXPECT_EQ(kMouseSettingsDefault.acceleration_enabled,
              settings.acceleration_enabled);
    EXPECT_EQ(kMouseSettingsDefault.scroll_sensitivity,
              settings.scroll_sensitivity);
    EXPECT_EQ(kMouseSettingsDefault.scroll_acceleration,
              settings.scroll_acceleration);
  }

  void CallUpdateMouseSettings(
      const std::string& device_key,
      const mojom::MouseSettings& settings,
      mojom::CustomizationRestriction customization_restriction =
          mojom::CustomizationRestriction::kAllowCustomizations) {
    mojom::MousePtr mouse = mojom::Mouse::New();
    mouse->settings = settings.Clone();
    mouse->device_key = device_key;
    mouse->customization_restriction = customization_restriction;

    pref_handler_->UpdateMouseSettings(pref_service_.get(),
                                       /*mouse_policies=*/{}, *mouse);
  }

  void CallUpdateLoginScreenMouseSettings(
      const AccountId& account_id,
      const std::string& device_key,
      const mojom::MouseSettings& settings,
      mojom::CustomizationRestriction customization_restriction) {
    mojom::MousePtr mouse = mojom::Mouse::New();
    mouse->settings = settings.Clone();
    mouse->customization_restriction = customization_restriction;
    pref_handler_->UpdateLoginScreenMouseSettings(
        local_state(), account_id, /*mouse_policies=*/{}, *mouse);
  }

  void CallUpdateDefaultMouseSettings(const std::string& device_key,
                                      const mojom::MouseSettings& settings) {
    mojom::MousePtr mouse = mojom::Mouse::New();
    mouse->settings = settings.Clone();
    mouse->device_key = device_key;

    pref_handler_->UpdateDefaultMouseSettings(pref_service_.get(),
                                              /*mouse_policies=*/{}, *mouse);
  }

  mojom::MouseSettingsPtr CallInitializeMouseSettings(
      const std::string& device_key,
      mojom::CustomizationRestriction customization_restriction =
          mojom::CustomizationRestriction::kAllowCustomizations,
      mojom::MouseButtonConfig mouse_button_config =
          mojom::MouseButtonConfig::kNoConfig) {
    mojom::MousePtr mouse = mojom::Mouse::New();
    mouse->device_key = device_key;
    mouse->customization_restriction = customization_restriction;
    mouse->mouse_button_config = mouse_button_config;

    pref_handler_->InitializeMouseSettings(pref_service_.get(),
                                           /*mouse_policies=*/{}, mouse.get());
    return std::move(mouse->settings);
  }

  mojom::MouseSettingsPtr CallInitializeLoginScreenMouseSettings(
      const AccountId& account_id,
      const mojom::Mouse& mouse) {
    const auto mouse_ptr = mouse.Clone();

    pref_handler_->InitializeLoginScreenMouseSettings(
        local_state(), account_id, /*mouse_policies=*/{}, mouse_ptr.get());
    return std::move(mouse_ptr->settings);
  }

  const base::Value::Dict* GetSettingsDict(const std::string& device_key) {
    const auto& devices_dict =
        pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref);
    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::kMouseLoginScreenInternalSettingsPref);
    return dict && dict->is_dict();
  }

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

  bool HasLoginScreenMouseButtonRemappingList(AccountId account_id) {
    const auto* button_remapping_list = known_user().FindPath(
        account_id, prefs::kMouseLoginScreenButtonRemappingListPref);
    return button_remapping_list && button_remapping_list->is_list();
  }

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

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

TEST_F(MousePrefHandlerTest, UpdateButtonRemappingsWithCompleteList) {
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  mouse.is_external = false;

  // Update the button remappings pref dict to mock adding a new
  // button remapping in the future.
  std::vector<mojom::ButtonRemappingPtr> button_remappings;
  button_remappings.push_back(button_remapping1.Clone());
  base::Value::Dict updated_button_remappings_dict;
  updated_button_remappings_dict.Set(
      kMouseKey1, ConvertButtonRemappingArrayToList(
                      button_remappings,
                      mojom::CustomizationRestriction::kAllowCustomizations));

  pref_service_->SetDict(prefs::kMouseButtonRemappingsDictPref,
                         updated_button_remappings_dict.Clone());

  mojom::MouseSettingsPtr updated_settings = CallInitializeMouseSettings(
      kMouseKey1, mojom::CustomizationRestriction::kAllowCustomizations,
      mojom::MouseButtonConfig::kFiveKey);
  EXPECT_NE(button_remappings, updated_settings->button_remappings);
  EXPECT_EQ(3u, updated_settings->button_remappings.size());
}

TEST_F(MousePrefHandlerTest, InitializeLoginScreenMouseSettings) {
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  mouse.is_external = false;
  mouse.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;
  mojom::Mouse mouse2;
  mouse2.device_key = kMouseKey2;
  mouse2.is_external = false;
  mouse2.customization_restriction =
      mojom::CustomizationRestriction::kDisallowCustomizations;
  mojom::Mouse mouse3;
  mouse3.device_key = kMouseKey3;
  mouse3.is_external = false;
  mouse3.customization_restriction =
      mojom::CustomizationRestriction::kDisableKeyEventRewrites;

  mojom::MouseSettingsPtr settings =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse);
  mojom::MouseSettingsPtr settings2 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse2);
  mojom::MouseSettingsPtr settings3 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse3);

  EXPECT_FALSE(HasInternalLoginScreenSettingsDict(account_id_1));
  CheckMouseSettingsAreSetToDefaultValues(*settings);

  EXPECT_FALSE(HasLoginScreenMouseButtonRemappingList(account_id_1));
  EXPECT_EQ(std::vector<mojom::ButtonRemappingPtr>(),
            settings->button_remappings);

  // Update the button remappings pref list to mock adding a new
  // button remapping in the future.
  std::vector<mojom::ButtonRemappingPtr> button_remappings;
  button_remappings.push_back(button_remapping2.Clone());
  known_user().SetPath(
      account_id_1, prefs::kMouseLoginScreenButtonRemappingListPref,
      std::optional<base::Value>(ConvertButtonRemappingArrayToList(
          button_remappings,
          mojom::CustomizationRestriction::kAllowCustomizations)));

  // updated_settings have updated button remappings since mouse
  // has kAllowCustomizations customization restriction.
  mojom::MouseSettingsPtr updated_settings =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse);
  EXPECT_EQ(button_remappings, updated_settings->button_remappings);

  // updated_settings2 have no button remappings since mouse2
  // has kDisallowCustomizations customization restriction.
  mojom::MouseSettingsPtr updated_settings2 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse2);
  EXPECT_EQ(std::vector<mojom::ButtonRemappingPtr>(),
            updated_settings2->button_remappings);

  // updated_settings3 have no button remappings since mouse3
  // has kDisableKeyEventRewrites customization restriction and the
  // button is a VKey.
  mojom::MouseSettingsPtr updated_settings3 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse3);
  EXPECT_EQ(std::vector<mojom::ButtonRemappingPtr>(),
            updated_settings3->button_remappings);
}

TEST_F(MousePrefHandlerTest, UpdateLoginScreenButtonRemappingList) {
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  mouse.is_external = false;
  mouse.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;
  mojom::Mouse mouse2;
  mouse2.device_key = kMouseKey2;
  mouse2.is_external = false;
  mouse2.customization_restriction =
      mojom::CustomizationRestriction::kDisallowCustomizations;
  mojom::Mouse mouse3;
  mouse3.device_key = kMouseKey3;
  mouse3.is_external = false;
  mouse3.customization_restriction =
      mojom::CustomizationRestriction::kDisableKeyEventRewrites;

  mojom::MouseSettingsPtr settings =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse);
  mojom::MouseSettingsPtr settings2 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse2);
  mojom::MouseSettingsPtr settings3 =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse3);

  // Update button_remappings in mouse settings.
  mojom::MouseSettingsPtr updated_settings = settings->Clone();
  std::vector<mojom::ButtonRemappingPtr> button_remapping_list;
  button_remapping_list.push_back(button_remapping1.Clone());
  updated_settings->button_remappings = mojo::Clone(button_remapping_list);
  CallUpdateLoginScreenMouseSettings(account_id_1, kMouseKey1,
                                     *updated_settings,
                                     mouse.customization_restriction);
  EXPECT_TRUE(HasLoginScreenMouseButtonRemappingList(account_id_1));

  // Verify the updated button remapping list. It should have updated prefs
  // since mouse has kAllowCustomizations customization restriction.
  const auto* updated_button_remapping_list = GetLoginScreenButtonRemappingList(
      local_state(), account_id_1,
      prefs::kMouseLoginScreenButtonRemappingListPref);
  ASSERT_NE(nullptr, updated_button_remapping_list);
  ASSERT_EQ(1u, updated_button_remapping_list->size());
  const auto& button_remapping = (*updated_button_remapping_list)[0].GetDict();
  EXPECT_EQ("REDACTED",
            *button_remapping.FindString(prefs::kButtonRemappingName));
  EXPECT_EQ(
      static_cast<int>(button_remapping1.button->get_customizable_button()),
      *button_remapping.FindInt(prefs::kButtonRemappingCustomizableButton));
  EXPECT_EQ(
      static_cast<int>(
          button_remapping1.remapping_action->get_accelerator_action()),
      *button_remapping.FindInt(prefs::kButtonRemappingAcceleratorAction));

  // Update button_remappings in mouse2 settings.
  mojom::MouseSettingsPtr updated_settings2 = settings2->Clone();
  std::vector<mojom::ButtonRemappingPtr> button_remapping_list2;
  button_remapping_list2.push_back(button_remapping2.Clone());
  updated_settings2->button_remappings = mojo::Clone(button_remapping_list2);
  CallUpdateLoginScreenMouseSettings(account_id_1, kMouseKey1,
                                     *updated_settings2,
                                     mouse2.customization_restriction);
  EXPECT_TRUE(HasLoginScreenMouseButtonRemappingList(account_id_1));

  // Verify the updated button remapping list2. It should be empty
  // since mouse2 has kDisallowCustomizations customization restriction.
  const auto* updated_button_remapping_list2 =
      GetLoginScreenButtonRemappingList(
          local_state(), account_id_1,
          prefs::kMouseLoginScreenButtonRemappingListPref);
  ASSERT_NE(nullptr, updated_button_remapping_list2);
  ASSERT_EQ(0u, updated_button_remapping_list2->size());

  // Update button_remappings in mouse3 settings.
  mojom::MouseSettingsPtr updated_settings3 = settings3->Clone();
  std::vector<mojom::ButtonRemappingPtr> button_remapping_list3;
  button_remapping_list3.push_back(button_remapping2.Clone());
  updated_settings3->button_remappings = mojo::Clone(button_remapping_list3);
  CallUpdateLoginScreenMouseSettings(account_id_1, kMouseKey1,
                                     *updated_settings3,
                                     mouse3.customization_restriction);
  EXPECT_TRUE(HasLoginScreenMouseButtonRemappingList(account_id_1));

  // Verify the updated button remapping list3. It should be empty
  // since mouse3 has has kDisableKeyEventRewrites customization restriction and
  // the button is a VKey.
  const auto* updated_button_remapping_list3 =
      GetLoginScreenButtonRemappingList(
          local_state(), account_id_1,
          prefs::kMouseLoginScreenButtonRemappingListPref);
  ASSERT_NE(nullptr, updated_button_remapping_list3);
  ASSERT_EQ(0u, updated_button_remapping_list3->size());
}

TEST_F(MousePrefHandlerTest, UpdateLoginScreenMouseSettings) {
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  mouse.is_external = false;
  mouse.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;
  mojom::MouseSettingsPtr settings =
      CallInitializeLoginScreenMouseSettings(account_id_1, mouse);
  mojom::MouseSettingsPtr updated_settings = settings->Clone();
  updated_settings->reverse_scrolling = !updated_settings->reverse_scrolling;
  updated_settings->acceleration_enabled =
      !updated_settings->acceleration_enabled;
  CallUpdateLoginScreenMouseSettings(account_id_1, kMouseKey1,
                                     *updated_settings,
                                     mouse.customization_restriction);
  const auto& updated_settings_dict =
      GetInternalLoginScreenSettingsDict(account_id_1);
  CheckMouseSettingsAndDictAreEqual(*updated_settings, updated_settings_dict);
  EXPECT_TRUE(HasInternalLoginScreenSettingsDict(account_id_1));
}

TEST_F(MousePrefHandlerTest, LoginScreenPrefsNotPersistedWhenFlagIsDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
  mojom::Mouse mouse1;
  mouse1.device_key = kMouseKey1;
  mouse1.is_external = false;
  mouse1.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;
  mojom::Mouse mouse2;
  mouse2.device_key = kMouseKey2;
  mouse2.is_external = true;
  mouse2.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;
  CallInitializeLoginScreenMouseSettings(account_id_1, mouse1);
  CallInitializeLoginScreenMouseSettings(account_id_1, mouse2);
  EXPECT_FALSE(HasInternalLoginScreenSettingsDict(account_id_1));
  EXPECT_FALSE(HasExternalLoginScreenSettingsDict(account_id_1));
}

TEST_F(MousePrefHandlerTest,
       LoginScreenButtonRemappingListNotPersistedWhenFlagIsDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kPeripheralCustomization);
  mojom::Mouse mouse1;
  mouse1.device_key = kMouseKey1;
  mouse1.is_external = false;
  mouse1.customization_restriction =
      mojom::CustomizationRestriction::kAllowCustomizations;

  CallInitializeLoginScreenMouseSettings(account_id_1, mouse1);
  EXPECT_FALSE(HasLoginScreenMouseButtonRemappingList(account_id_1));
}

TEST_F(MousePrefHandlerTest, MultipleDevices) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettings1);
  CallUpdateMouseSettings(kMouseKey2, kMouseSettings2);

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

  auto* settings_dict = devices_dict.FindDict(kMouseKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettings1, *settings_dict);

  settings_dict = devices_dict.FindDict(kMouseKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettings2, *settings_dict);
}

TEST_F(MousePrefHandlerTest, PreservesOldSettings) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettings1);

  auto devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kMouseKey1);
  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::kMouseDeviceSettingsDictPref,
                         std::move(devices_dict));

  // Update the settings again and verify the fake key and value still exist.
  CallUpdateMouseSettings(kMouseKey1, kMouseSettings1);

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

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

TEST_F(MousePrefHandlerTest, LastUpdated) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettings1);
  auto devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kMouseKey1);
  ASSERT_NE(nullptr, settings_dict);
  auto* time_stamp1 = settings_dict->Find(prefs::kLastUpdatedKey);
  ASSERT_NE(nullptr, time_stamp1);
  auto button_remapping_time_stamp_path =
      base::StrCat({prefs::kLastUpdatedKey, ".", kMouseKey1});
  auto* button_remapping_time_stamp =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindByDottedPath(button_remapping_time_stamp_path);
  ASSERT_NE(nullptr, button_remapping_time_stamp);

  mojom::MouseSettingsPtr updated_settings = kMouseSettings1.Clone();
  updated_settings->swap_right = !updated_settings->swap_right;
  CallUpdateMouseSettings(kMouseKey1, *updated_settings);

  const auto& updated_devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref);
  const auto* updated_settings_dict = updated_devices_dict.FindDict(kMouseKey1);
  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);

  std::vector<mojom::ButtonRemappingPtr> button_remappings;
  button_remappings.push_back(button_remapping2.Clone());
  mojom::MouseSettings kUpdatedMouseSettingsWithButtonRemapping(
      /*swap_right=*/kDefaultSwapRight,
      /*sensitivity=*/kDefaultSensitivity,
      /*reverse_scrolling=*/kDefaultReverseScrolling,
      /*acceleration_enabled=*/kDefaultAccelerationEnabled,
      /*scroll_sensitivity=*/kDefaultScrollSensitivity,
      /*scroll_acceleration=*/kDefaultScrollAccelerationEnabled,
      /*button_remappings=*/mojo::Clone(button_remappings));

  CallUpdateMouseSettings(kMouseKey1, kUpdatedMouseSettingsWithButtonRemapping);
  auto* updated_button_remapping_time_stamp =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindByDottedPath(button_remapping_time_stamp_path);
  ASSERT_NE(nullptr, updated_button_remapping_time_stamp);
  ASSERT_NE(button_remapping_time_stamp, updated_button_remapping_time_stamp);
}

TEST_F(MousePrefHandlerTest, UpdateSettings) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettings1);
  CallUpdateMouseSettings(kMouseKey2, kMouseSettings2);

  auto devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kMouseKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettings1, *settings_dict);

  settings_dict = devices_dict.FindDict(kMouseKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettings2, *settings_dict);

  mojom::MouseSettingsPtr updated_settings = kMouseSettings1.Clone();
  updated_settings->swap_right = !updated_settings->swap_right;

  // Update the settings again and verify the settings are updated in place.
  CallUpdateMouseSettings(kMouseKey1, *updated_settings);

  const auto& updated_devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref);
  const auto* updated_settings_dict = updated_devices_dict.FindDict(kMouseKey1);
  ASSERT_NE(nullptr, updated_settings_dict);
  CheckMouseSettingsAndDictAreEqual(*updated_settings, *updated_settings_dict);

  // Verify other device remains unmodified.
  const auto* unchanged_settings_dict =
      updated_devices_dict.FindDict(kMouseKey2);
  ASSERT_NE(nullptr, unchanged_settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettings2, *unchanged_settings_dict);
}

TEST_F(MousePrefHandlerTest, NewSettingAddedRoundTrip) {
  mojom::MouseSettingsPtr test_settings = kMouseSettings1.Clone();
  test_settings->swap_right = !kDefaultSwapRight;

  CallUpdateMouseSettings(kMouseKey1, *test_settings);
  auto devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref).Clone();
  auto* settings_dict = devices_dict.FindDict(kMouseKey1);

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

  // Initialize mouse settings for the device and check that
  // "new settings" matches "test_settings".
  mojom::MouseSettingsPtr settings = CallInitializeMouseSettings(kMouseKey1);
  EXPECT_EQ(kDefaultSwapRight, settings->swap_right);

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

TEST_F(MousePrefHandlerTest, DefaultSettingsWhenPrefServiceNull) {
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  pref_handler_->InitializeMouseSettings(nullptr, /*mouse_policies=*/{},
                                         &mouse);
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);
}

TEST_F(MousePrefHandlerTest, NewMouseDefaultSettings) {
  mojom::MouseSettingsPtr settings = CallInitializeMouseSettings(kMouseKey1);
  EXPECT_EQ(*settings, kMouseSettingsDefault);
  settings = CallInitializeMouseSettings(kMouseKey2);
  EXPECT_EQ(*settings, kMouseSettingsDefault);

  auto devices_dict =
      pref_service_->GetDict(prefs::kMouseDeviceSettingsDictPref).Clone();
  ASSERT_EQ(2u, devices_dict.size());
  auto* settings_dict = devices_dict.FindDict(kMouseKey1);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsDefault, *settings_dict);

  settings_dict = devices_dict.FindDict(kMouseKey2);
  ASSERT_NE(nullptr, settings_dict);
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsDefault, *settings_dict);
}

TEST_F(MousePrefHandlerTest, MouseObserveredInTransitionPeriod) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  Shell::Get()->input_device_tracker()->OnMouseConnected(mouse);
  // Initialize mouse settings for the device and check that the Test
  // prefs were used as defaults.
  mojom::MouseSettingsPtr settings =
      CallInitializeMouseSettings(mouse.device_key);
  ASSERT_EQ(settings->swap_right, kTestSwapRight);
  ASSERT_EQ(settings->sensitivity, kTestSensitivity);
  ASSERT_EQ(settings->reverse_scrolling, kTestReverseScrolling);
  ASSERT_EQ(settings->acceleration_enabled, kTestAccelerationEnabled);
  ASSERT_EQ(settings->scroll_sensitivity, kTestScrollSensitivity);
  ASSERT_EQ(settings->scroll_acceleration, kTestScrollAcceleration);
}

TEST_F(MousePrefHandlerTest, TransitionPeriodSettingsPersistedWhenUserChosen) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;
  Shell::Get()->input_device_tracker()->OnMouseConnected(mouse);

  pref_service_->SetUserPref(prefs::kPrimaryMouseButtonRight,
                             base::Value(kDefaultSwapRight));
  pref_service_->SetUserPref(prefs::kMouseSensitivity,
                             base::Value(kDefaultSensitivity));
  pref_service_->SetUserPref(prefs::kMouseReverseScroll,
                             base::Value(kDefaultReverseScrolling));
  pref_service_->SetUserPref(prefs::kMouseAcceleration,
                             base::Value(kDefaultAccelerationEnabled));
  pref_service_->SetUserPref(prefs::kMouseScrollSensitivity,
                             base::Value(kDefaultScrollSensitivity));
  pref_service_->SetUserPref(prefs::kMouseScrollAcceleration,
                             base::Value(kDefaultScrollAccelerationEnabled));
  mojom::MouseSettingsPtr settings =
      CallInitializeMouseSettings(mouse.device_key);
  EXPECT_EQ(kMouseSettingsDefault, *settings);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSwapRight));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingReverseScrolling));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingAccelerationEnabled));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollAcceleration));
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsDefault, *settings_dict);
}

TEST_F(MousePrefHandlerTest, DefaultNotPersistedUntilUpdated) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettingsDefault);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingSwapRight));
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingSensitivity));
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingReverseScrolling));
  EXPECT_FALSE(
      settings_dict->contains(prefs::kMouseSettingAccelerationEnabled));
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingScrollSensitivity));
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingScrollAcceleration));
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsDefault, *settings_dict);

  CallUpdateMouseSettings(kMouseKey1, kMouseSettingsNotDefault);
  settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSwapRight));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingReverseScrolling));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingAccelerationEnabled));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollAcceleration));
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsNotDefault, *settings_dict);

  CallUpdateMouseSettings(kMouseKey1, kMouseSettingsDefault);
  settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSwapRight));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingReverseScrolling));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingAccelerationEnabled));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollSensitivity));
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingScrollAcceleration));
  CheckMouseSettingsAndDictAreEqual(kMouseSettingsDefault, *settings_dict);
}

TEST_F(MousePrefHandlerTest, NewMouse_ManagedEnterprisePolicy_GetsDefaults) {
  mojom::MousePolicies policies;
  policies.swap_right_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kManaged, !kDefaultSwapRight);

  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;

  pref_handler_->InitializeMouseSettings(pref_service_.get(), policies, &mouse);

  EXPECT_EQ(!kDefaultSwapRight, mouse.settings->swap_right);
  mouse.settings->swap_right = kDefaultSwapRight;
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingSwapRight));
}

TEST_F(MousePrefHandlerTest, LoginScreen_ManagedEnterprisePolicy) {
  mojom::MousePolicies policies;
  policies.swap_right_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kManaged, !kDefaultSwapRight);

  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;

  pref_handler_->InitializeLoginScreenMouseSettings(local_state(), account_id_1,
                                                    policies, &mouse);

  EXPECT_EQ(!kDefaultSwapRight, mouse.settings->swap_right);
  mouse.settings->swap_right = kDefaultSwapRight;
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);
}

TEST_F(MousePrefHandlerTest,
       NewMouse_RecommendedEnterprisePolicy_GetsDefaults) {
  mojom::MousePolicies policies;
  policies.swap_right_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kRecommended, !kDefaultSwapRight);

  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;

  pref_handler_->InitializeMouseSettings(pref_service_.get(), policies, &mouse);

  EXPECT_EQ(!kDefaultSwapRight, mouse.settings->swap_right);
  mouse.settings->swap_right = kDefaultSwapRight;
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingSwapRight));
}

TEST_F(MousePrefHandlerTest,
       ExistingMouse_RecommendedEnterprisePolicy_GetsNewPolicy) {
  mojom::MousePolicies policies;
  policies.swap_right_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kRecommended, !kDefaultSwapRight);

  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;

  pref_handler_->InitializeMouseSettings(pref_service_.get(),
                                         /*mouse_policies=*/{}, &mouse);
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  pref_handler_->InitializeMouseSettings(pref_service_.get(), policies, &mouse);
  EXPECT_EQ(!kDefaultSwapRight, mouse.settings->swap_right);
  mouse.settings->swap_right = kDefaultSwapRight;
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_FALSE(settings_dict->contains(prefs::kMouseSettingSwapRight));
}

TEST_F(MousePrefHandlerTest,
       ExistingMouse_ManagedEnterprisePolicy_GetsNewPolicy) {
  mojom::MousePolicies policies;
  policies.swap_right_policy = mojom::InputDeviceSettingsPolicy::New(
      mojom::PolicyStatus::kManaged, !kDefaultSwapRight);

  mojom::Mouse mouse;
  mouse.device_key = kMouseKey1;

  pref_handler_->InitializeMouseSettings(pref_service_.get(),
                                         /*mouse_policies=*/{}, &mouse);
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  mouse.settings->swap_right = !kDefaultSwapRight;
  CallUpdateMouseSettings(kMouseKey1, *mouse.settings);

  pref_handler_->InitializeMouseSettings(pref_service_.get(), policies, &mouse);
  EXPECT_EQ(!kDefaultSwapRight, mouse.settings->swap_right);
  mouse.settings->swap_right = kDefaultSwapRight;
  EXPECT_EQ(kMouseSettingsDefault, *mouse.settings);

  const auto* settings_dict = GetSettingsDict(kMouseKey1);
  EXPECT_TRUE(settings_dict->contains(prefs::kMouseSettingSwapRight));
  EXPECT_EQ(!kDefaultSwapRight,
            settings_dict->FindBool(prefs::kMouseSettingSwapRight).value());
}

TEST_F(MousePrefHandlerTest, UpdateButtonRemapping) {
  CallUpdateMouseSettings(kMouseKey1, kMouseSettingsDefault);
  const auto* button_remappings_list =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindList(kMouseKey1);
  ASSERT_EQ(0u, button_remappings_list->size());

  std::vector<mojom::ButtonRemappingPtr> button_remappings;
  button_remappings.push_back(button_remapping2.Clone());
  mojom::MouseSettings kUpdatedMouseSettings(
      /*swap_right=*/kDefaultSwapRight,
      /*sensitivity=*/kDefaultSensitivity,
      /*reverse_scrolling=*/kDefaultReverseScrolling,
      /*acceleration_enabled=*/kDefaultAccelerationEnabled,
      /*scroll_sensitivity=*/kDefaultScrollSensitivity,
      /*scroll_acceleration=*/kDefaultScrollAccelerationEnabled,
      /*button_remappings=*/mojo::Clone(button_remappings));

  CallUpdateMouseSettings(kMouseKey1, kUpdatedMouseSettings);
  auto* updated_button_remappings_list =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindList(kMouseKey1);
  ASSERT_NE(nullptr, updated_button_remappings_list);
  ASSERT_EQ(1u, updated_button_remappings_list->size());
  const auto& updated_button_remapping_dict =
      (*updated_button_remappings_list)[0].GetDict();
  EXPECT_EQ(
      button_remappings[0]->name,
      *updated_button_remapping_dict.FindString(prefs::kButtonRemappingName));
  EXPECT_EQ(static_cast<int>(button_remappings[0]->button->get_vkey()),
            *updated_button_remapping_dict.FindInt(
                prefs::kButtonRemappingKeyboardCode));
  EXPECT_EQ(
      static_cast<int>(
          button_remappings[0]->remapping_action->get_static_shortcut_action()),
      *updated_button_remapping_dict.FindInt(
          prefs::kButtonRemappingStaticShortcutAction));

  CallUpdateMouseSettings(
      kMouseKey1, kUpdatedMouseSettings,
      mojom::CustomizationRestriction::kDisallowCustomizations);
  auto* updated_button_remappings_list2 =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindList(kMouseKey1);
  ASSERT_NE(nullptr, updated_button_remappings_list2);
  ASSERT_EQ(0u, updated_button_remappings_list2->size());

  CallUpdateMouseSettings(
      kMouseKey1, kUpdatedMouseSettings,
      mojom::CustomizationRestriction::kDisableKeyEventRewrites);
  auto* updated_button_remappings_list3 =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref)
          .FindList(kMouseKey1);
  ASSERT_NE(nullptr, updated_button_remappings_list3);
  ASSERT_EQ(0u, updated_button_remappings_list3->size());
}

TEST_F(MousePrefHandlerTest, InitializeButtonRemappings) {
  mojom::MouseSettingsPtr settings = CallInitializeMouseSettings(kMouseKey1);
  ASSERT_NE(nullptr, settings.get());
  EXPECT_EQ(0u, settings->button_remappings.size());

  const auto& button_remappings_dict =
      pref_service_->GetDict(prefs::kMouseButtonRemappingsDictPref);
  auto* button_remappings_list = button_remappings_dict.FindList(kMouseKey1);
  ASSERT_NE(nullptr, button_remappings_list);
  ASSERT_EQ(0u, button_remappings_list->size());

  // Update the button remappings pref dict to mock adding a new
  // button remapping in the future.
  std::vector<mojom::ButtonRemappingPtr> button_remappings;
  button_remappings.push_back(button_remapping2.Clone());
  base::Value::Dict updated_button_remappings_dict;
  updated_button_remappings_dict.Set(
      kMouseKey1, ConvertButtonRemappingArrayToList(
                      button_remappings,
                      mojom::CustomizationRestriction::kAllowCustomizations));

  pref_service_->SetDict(prefs::kMouseButtonRemappingsDictPref,
                         updated_button_remappings_dict.Clone());

  // updated_settings have updated button remappings since mouse
  // has kAllowCustomizations customization restriction.
  mojom::MouseSettingsPtr updated_settings =
      CallInitializeMouseSettings(kMouseKey1);
  EXPECT_EQ(button_remappings, updated_settings->button_remappings);

  // updated_settings2 have no button remappings since mouse2
  // has kDisallowCustomizations customization restriction.
  mojom::MouseSettingsPtr updated_settings2 = CallInitializeMouseSettings(
      kMouseKey1, mojom::CustomizationRestriction::kDisallowCustomizations);
  EXPECT_EQ(std::vector<mojom::ButtonRemappingPtr>(),
            updated_settings2->button_remappings);

  // updated_settings3 have no button remappings since mouse3
  // has kDisableKeyEventRewrites customization restriction and the
  // button is a VKey.
  mojom::MouseSettingsPtr updated_settings3 = CallInitializeMouseSettings(
      kMouseKey1, mojom::CustomizationRestriction::kDisableKeyEventRewrites);
  EXPECT_EQ(std::vector<mojom::ButtonRemappingPtr>(),
            updated_settings3->button_remappings);
}

TEST_F(MousePrefHandlerTest, RememberDefaultsFromLastUpdatedSettings) {
  mojom::MouseSettingsPtr settings = CallInitializeMouseSettings(kMouseKey1);
  settings->swap_right = !kDefaultSwapRight;
  settings->sensitivity = 1;
  CallUpdateMouseSettings(kMouseKey1, *settings);
  CallUpdateDefaultMouseSettings(kMouseKey1, *settings);

  mojom::MouseSettingsPtr settings2 = CallInitializeMouseSettings(kMouseKey2);
  EXPECT_EQ(*settings2, *settings);

  settings2->sensitivity = 5;
  CallUpdateDefaultMouseSettings(kMouseKey2, *settings2);

  mojom::MouseSettingsPtr settings_duplicate =
      CallInitializeMouseSettings(kMouseKey1);
  EXPECT_EQ(*settings, *settings_duplicate);
}

TEST_F(MousePrefHandlerTest, SettingsUpdateMetricTest) {
  const auto settings1 = CallInitializeMouseSettings(kMouseKey1);

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

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

  // When its taken off the the defaults, the category should be kDefault.
  {
    CallUpdateDefaultMouseSettings(kMouseKey1, *settings1);
    CallInitializeMouseSettings(kMouseKey2);
    const auto& metric_dict =
        pref_service_->GetDict(prefs::kMouseUpdateSettingsMetricInfo);
    ASSERT_TRUE(metric_dict.contains(kMouseKey2));

    auto metrics_info =
        SettingsUpdatedMetricsInfo::FromDict(*metric_dict.FindDict(kMouseKey2));
    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::kMouseDeviceSettingsDictPref).Clone();
    devices_dict.Set(kMouseKey3, base::Value::Dict());
    pref_service_->SetDict(prefs::kMouseDeviceSettingsDictPref,
                           std::move(devices_dict));

    CallInitializeMouseSettings(kMouseKey3);
    const auto& metric_dict =
        pref_service_->GetDict(prefs::kMouseUpdateSettingsMetricInfo);
    ASSERT_TRUE(metric_dict.contains(kMouseKey3));

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

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

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

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

INSTANTIATE_TEST_SUITE_P(
    // Empty to simplify gtest output
    ,
    MouseSettingsPrefConversionTest,
    testing::Combine(testing::Values(kMouseKey1, kMouseKey2),
                     testing::Values(&kMouseSettings1, &kMouseSettings2)));

TEST_P(MouseSettingsPrefConversionTest, CheckConversion) {
  CallUpdateMouseSettings(device_key_, *settings_);

  const auto* settings_dict = GetSettingsDict(device_key_);
  CheckMouseSettingsAndDictAreEqual(*settings_, *settings_dict);
}

}  // namespace ash