chromium/chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings/input_device_settings_provider_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 "chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings/input_device_settings_provider.h"

#include <memory>
#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/input_device_settings_controller.h"
#include "ash/public/cpp/test/mock_input_device_settings_controller.h"
#include "ash/public/mojom/input_device_settings.mojom-forward.h"
#include "ash/public/mojom/input_device_settings.mojom-shared.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/system/keyboard_brightness_control_delegate.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "chrome/browser/ui/webui/ash/settings/pages/device/input_device_settings/input_device_settings_provider.mojom.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "mojo/public/cpp/bindings/clone_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"

namespace ash::settings {
namespace {
const ::ash::mojom::Keyboard kKeyboard1 =
    ::ash::mojom::Keyboard(/*name=*/"AT Translated Set 2",
                           /*is_external=*/false,
                           /*id=*/1,
                           /*device_key=*/"fake-device-key1",
                           /*meta_key=*/::ui::mojom::MetaKey::kLauncher,
                           /*modifier_keys=*/{},
                           /*top_row_action_keys=*/{},
                           ::ash::mojom::KeyboardSettings::New(),
                           ::ash::mojom::BatteryInfo::New(),
                           ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::Keyboard kKeyboard2 =
    ::ash::mojom::Keyboard(/*name=*/"Logitech K580",
                           /*is_external=*/true,
                           /*id=*/2,
                           /*device_key=*/"fake-device-key2",
                           /*meta_key=*/::ui::mojom::MetaKey::kExternalMeta,
                           /*modifier_keys=*/{},
                           /*top_row_action_keys=*/{},
                           ::ash::mojom::KeyboardSettings::New(),
                           ::ash::mojom::BatteryInfo::New(),
                           ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::Keyboard kKeyboard3 =
    ::ash::mojom::Keyboard(/*name=*/"HP 910 White Bluetooth Keyboard",
                           /*is_external=*/true,
                           /*id=*/3,
                           /*device_key=*/"fake-device-key3",
                           /*meta_key=*/::ui::mojom::MetaKey::kExternalMeta,
                           /*modifier_keys=*/{},
                           /*top_row_action_keys=*/{},
                           ::ash::mojom::KeyboardSettings::New(),
                           ::ash::mojom::BatteryInfo::New(),
                           ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::Touchpad kTouchpad1 =
    ::ash::mojom::Touchpad(/*name=*/"test touchpad",
                           /*is_external=*/false,
                           /*id=*/3,
                           /*device_key=*/"fake-device-key3",
                           /*is_haptic=*/true,
                           ::ash::mojom::TouchpadSettings::New(),
                           ::ash::mojom::BatteryInfo::New(),
                           ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::Touchpad kTouchpad2 =
    ::ash::mojom::Touchpad(/*name=*/"Logitech T650",
                           /*is_external=*/true,
                           /*id=*/4,
                           /*device_key=*/"fake-device-key4",
                           /*is_haptic=*/false,
                           ::ash::mojom::TouchpadSettings::New(),
                           ::ash::mojom::BatteryInfo::New(),
                           ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::PointingStick kPointingStick1 =
    ::ash::mojom::PointingStick(/*name=*/"test pointing stick",
                                /*is_external=*/false,
                                /*id=*/5,
                                /*device_key=*/"fake-device-key5",
                                ::ash::mojom::PointingStickSettings::New());
const ::ash::mojom::PointingStick kPointingStick2 =
    ::ash::mojom::PointingStick(/*name=*/"Lexmark-Unicomp FSR",
                                /*is_external=*/true,
                                /*id=*/6,
                                /*device_key=*/"fake-device-key6",
                                ::ash::mojom::PointingStickSettings::New());
const ::ash::mojom::Mouse kMouse1 = ::ash::mojom::Mouse(
    /*name=*/"Razer Basilisk V3",
    /*is_external=*/false,
    /*id=*/7,
    /*device_key=*/"fake-device-key7",
    /*customization_restriction=*/
    ::ash::mojom::CustomizationRestriction::kAllowCustomizations,
    /*mouse_button_config=*/::ash::mojom::MouseButtonConfig::kNoConfig,
    ::ash::mojom::MouseSettings::New(),
    ::ash::mojom::BatteryInfo::New(),
    ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::Mouse kMouse2 = ::ash::mojom::Mouse(
    /*name=*/"MX Anywhere 2S",
    /*is_external=*/true,
    /*id=*/8,
    /*device_key=*/"fake-device-key8",
    /*customization_restriction=*/
    ::ash::mojom::CustomizationRestriction::kAllowCustomizations,
    /*mouse_button_config=*/::ash::mojom::MouseButtonConfig::kNoConfig,
    ::ash::mojom::MouseSettings::New(),
    ::ash::mojom::BatteryInfo::New(),
    ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::GraphicsTablet kGraphicsTablet1 =
    ::ash::mojom::GraphicsTablet(
        /*name=*/"Wacom Intuos S",
        /*id=*/9,
        /*device_key=*/"fake-device-key9",
        /*customization_restriction=*/
        ::ash::mojom::CustomizationRestriction::kAllowCustomizations,
        ::ash::mojom::GraphicsTabletButtonConfig::kNoConfig,
        ::ash::mojom::GraphicsTabletSettings::New(),
        ::ash::mojom::BatteryInfo::New(),
        ::ash::mojom::CompanionAppInfo::New());
const ::ash::mojom::GraphicsTablet kGraphicsTablet2 =
    ::ash::mojom::GraphicsTablet(
        /*name=*/"Huion H1060P",
        /*id=*/10,
        /*device_key=*/"fake-device-key10",
        /*customization_restriction=*/
        ::ash::mojom::CustomizationRestriction::kAllowCustomizations,
        ::ash::mojom::GraphicsTabletButtonConfig::kNoConfig,
        ::ash::mojom::GraphicsTabletSettings::New(),
        ::ash::mojom::BatteryInfo::New(),
        ::ash::mojom::CompanionAppInfo::New());

template <bool sorted = false, typename T>
void ExpectListsEqual(const std::vector<T>& expected_list,
                      const std::vector<T>& actual_list) {
  ASSERT_EQ(expected_list.size(), actual_list.size());
  if constexpr (sorted) {
    for (size_t i = 0; i < expected_list.size(); i++) {
      EXPECT_EQ(expected_list[i], actual_list[i]);
    }
    return;
  }

  for (size_t i = 0; i < expected_list.size(); i++) {
    auto actual_iter = base::ranges::find(actual_list, expected_list[i]);
    EXPECT_NE(actual_list.end(), actual_iter);
    if (actual_iter != actual_list.end()) {
      EXPECT_EQ(expected_list[i], *actual_iter);
    }
  }
}

class FakeKeyboardSettingsObserver : public mojom::KeyboardSettingsObserver {
 public:
  void OnKeyboardListUpdated(
      std::vector<::ash::mojom::KeyboardPtr> keyboards) override {
    keyboards_ = std::move(keyboards);
    ++num_times_keyboard_list_updated_;
  }

  void OnKeyboardPoliciesUpdated(
      ::ash::mojom::KeyboardPoliciesPtr keyboard_policies) override {
    ++num_times_keyboard_policies_updated_;
  }

  const std::vector<::ash::mojom::KeyboardPtr>& keyboards() {
    return keyboards_;
  }

  int num_times_keyboard_list_updated() {
    return num_times_keyboard_list_updated_;
  }

  int num_times_keyboard_policies_updated() {
    return num_times_keyboard_policies_updated_;
  }

  mojo::Receiver<mojom::KeyboardSettingsObserver> receiver{this};

 private:
  std::vector<::ash::mojom::KeyboardPtr> keyboards_;
  int num_times_keyboard_list_updated_ = 0;
  int num_times_keyboard_policies_updated_ = 0;
};

class FakeTouchpadSettingsObserver : public mojom::TouchpadSettingsObserver {
 public:
  void OnTouchpadListUpdated(
      std::vector<::ash::mojom::TouchpadPtr> touchpads) override {
    touchpads_ = std::move(touchpads);
    ++num_times_called_;
  }

  const std::vector<::ash::mojom::TouchpadPtr>& touchpads() {
    return touchpads_;
  }

  int num_times_called() { return num_times_called_; }
  mojo::Receiver<mojom::TouchpadSettingsObserver> receiver{this};

 private:
  std::vector<::ash::mojom::TouchpadPtr> touchpads_;
  int num_times_called_ = 0;
};

class FakePointingStickSettingsObserver
    : public mojom::PointingStickSettingsObserver {
 public:
  void OnPointingStickListUpdated(
      std::vector<::ash::mojom::PointingStickPtr> pointing_sticks) override {
    pointing_sticks_ = std::move(pointing_sticks);
    ++num_times_called_;
  }

  const std::vector<::ash::mojom::PointingStickPtr>& pointing_sticks() {
    return pointing_sticks_;
  }

  int num_times_called() { return num_times_called_; }
  mojo::Receiver<mojom::PointingStickSettingsObserver> receiver{this};

 private:
  std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_;
  int num_times_called_ = 0;
};

class FakeMouseSettingsObserver : public mojom::MouseSettingsObserver {
 public:
  void OnMouseListUpdated(std::vector<::ash::mojom::MousePtr> mice) override {
    mice_ = std::move(mice);
    ++num_times_mouse_list_updated_;
  }

  void OnMousePoliciesUpdated(
      ::ash::mojom::MousePoliciesPtr mouse_policies) override {
    ++num_times_mouse_policies_updated_;
  }

  const std::vector<::ash::mojom::MousePtr>& mice() { return mice_; }

  int num_times_mouse_list_updated() { return num_times_mouse_list_updated_; }
  int num_times_mouse_policies_updated() {
    return num_times_mouse_policies_updated_;
  }
  mojo::Receiver<mojom::MouseSettingsObserver> receiver{this};

 private:
  std::vector<::ash::mojom::MousePtr> mice_;
  int num_times_mouse_list_updated_ = 0;
  int num_times_mouse_policies_updated_ = 0;
};

class FakeGraphicsTabletSettingsObserver
    : public mojom::GraphicsTabletSettingsObserver {
 public:
  void OnGraphicsTabletListUpdated(
      std::vector<::ash::mojom::GraphicsTabletPtr> graphics_tablets) override {
    graphics_tablets_ = std::move(graphics_tablets);
    ++num_times_graphics_tablet_list_updated_;
  }

  const std::vector<::ash::mojom::GraphicsTabletPtr>& graphics_tablets() {
    return graphics_tablets_;
  }

  int num_times_graphics_tablet_list_updated() {
    return num_times_graphics_tablet_list_updated_;
  }
  mojo::Receiver<mojom::GraphicsTabletSettingsObserver> receiver{this};

 private:
  std::vector<::ash::mojom::GraphicsTabletPtr> graphics_tablets_;
  int num_times_graphics_tablet_list_updated_ = 0;
};

class FakeButtonPressObserver : public mojom::ButtonPressObserver {
 public:
  void OnButtonPressed(::ash::mojom::ButtonPtr button) override {
    last_pressed_button_ = std::move(button);
  }

  bool has_last_pressed_button() {
    return last_pressed_button_.get() != nullptr;
  }

  const ::ash::mojom::Button& last_pressed_button() {
    DCHECK(last_pressed_button_);
    return *last_pressed_button_;
  }

  mojo::Receiver<mojom::ButtonPressObserver> receiver{this};

 private:
  ::ash::mojom::ButtonPtr last_pressed_button_;
};

class FakeKeyboardBrightnessObserver
    : public mojom::KeyboardBrightnessObserver {
 public:
  void OnKeyboardBrightnessChanged(double percent) override {
    keyboard_brightness_ = percent;
    ++num_times_called_;
  }
  double keyboard_brightness() { return keyboard_brightness_; }

  int num_times_called() { return num_times_called_; }

  mojo::Receiver<mojom::KeyboardBrightnessObserver> receiver{this};

 private:
  int num_times_called_ = 0;
  double keyboard_brightness_ = 0;
};

class FakeKeyboardAmbientLightSensorObserver
    : public mojom::KeyboardAmbientLightSensorObserver {
 public:
  void OnKeyboardAmbientLightSensorEnabledChanged(bool enabled) override {
    keyboard_ambient_light_sensor_enabled_ = enabled;
    ++num_times_called_;
  }
  bool keyboard_ambient_light_sensor_enabled() {
    return keyboard_ambient_light_sensor_enabled_;
  }
  int num_times_called() { return num_times_called_; }
  mojo::Receiver<mojom::KeyboardAmbientLightSensorObserver> receiver{this};

 private:
  int num_times_called_ = 0;
  bool keyboard_ambient_light_sensor_enabled_ = true;
};

class FakeLidStateObserver : public mojom::LidStateObserver {
 public:
  // mojom::LidStateObserver:
  void OnLidStateChanged(bool is_lid_open) override {
    ++num_lid_state_change_calls_;
    is_lid_open_ = is_lid_open;
  }

  bool is_lid_open() { return is_lid_open_; }

  int num_lid_state_change_calls() const { return num_lid_state_change_calls_; }

  mojo::Receiver<mojom::LidStateObserver> receiver{this};

 private:
  int num_lid_state_change_calls_ = 0;
  bool is_lid_open_ = true;
};

class FakeKeyboardBrightnessControlDelegate
    : public KeyboardBrightnessControlDelegate {
 public:
  FakeKeyboardBrightnessControlDelegate() = default;
  ~FakeKeyboardBrightnessControlDelegate() override = default;

  // override methods:
  void HandleKeyboardBrightnessDown() override {}
  void HandleKeyboardBrightnessUp() override {}
  void HandleToggleKeyboardBacklight() override {}
  void HandleSetKeyboardBrightness(
      double percent,
      bool gradual,
      KeyboardBrightnessChangeSource source) override {
    keyboard_brightness_ = percent;
    keyboard_brightness_change_source_ = source;
  }
  void HandleGetKeyboardBrightness(
      base::OnceCallback<void(std::optional<double>)> callback) override {
    std::move(callback).Run(keyboard_brightness_);
  }
  void HandleSetKeyboardAmbientLightSensorEnabled(
      bool enabled,
      KeyboardAmbientLightSensorEnabledChangeSource source) override {
    keyboard_ambient_light_sensor_enabled_ = enabled;
  }
  void HandleGetKeyboardAmbientLightSensorEnabled(
      base::OnceCallback<void(std::optional<bool>)> callback) override {
    std::move(callback).Run(keyboard_ambient_light_sensor_enabled_);
  }

  double keyboard_brightness() { return keyboard_brightness_; }
  KeyboardBrightnessChangeSource keyboard_brightness_change_source() const {
    return keyboard_brightness_change_source_;
  }
  bool keyboard_ambient_light_sensor_enabled() {
    return keyboard_ambient_light_sensor_enabled_;
  }

 private:
  double keyboard_brightness_ = 0;
  bool keyboard_ambient_light_sensor_enabled_ = true;
  KeyboardBrightnessChangeSource keyboard_brightness_change_source_ =
      KeyboardBrightnessChangeSource::kRestoredFromUserPref;
};

class FakeInputDeviceSettingsController
    : public MockInputDeviceSettingsController {
 public:
  // InputDeviceSettingsController:
  std::vector<::ash::mojom::KeyboardPtr> GetConnectedKeyboards() override {
    return mojo::Clone(keyboards_);
  }
  std::vector<::ash::mojom::TouchpadPtr> GetConnectedTouchpads() override {
    return mojo::Clone(touchpads_);
  }
  std::vector<::ash::mojom::MousePtr> GetConnectedMice() override {
    return mojo::Clone(mice_);
  }
  std::vector<::ash::mojom::PointingStickPtr> GetConnectedPointingSticks()
      override {
    return mojo::Clone(pointing_sticks_);
  }
  std::vector<::ash::mojom::GraphicsTabletPtr> GetConnectedGraphicsTablets()
      override {
    return mojo::Clone(graphics_tablets_);
  }
  const ::ash::mojom::KeyboardPolicies& GetKeyboardPolicies() override {
    return *keyboard_policies_;
  }
  const ::ash::mojom::MousePolicies& GetMousePolicies() override {
    return *mouse_policies_;
  }
  void RestoreDefaultKeyboardRemappings(DeviceId id) override {
    ++num_times_restore_default_keyboard_remappings_called_;
  }
  bool SetKeyboardSettings(
      DeviceId id,
      ::ash::mojom::KeyboardSettingsPtr settings) override {
    return MockInputDeviceSettingsController::SetKeyboardSettings(
        id, std::move(settings));
  }
  void AddObserver(Observer* observer) override { observer_ = observer; }
  void RemoveObserver(Observer* observer) override { observer_ = nullptr; }
  bool SetTouchpadSettings(
      DeviceId id,
      ::ash::mojom::TouchpadSettingsPtr settings) override {
    return MockInputDeviceSettingsController::SetTouchpadSettings(
        id, std::move(settings));
  }
  bool SetMouseSettings(DeviceId id,
                        ::ash::mojom::MouseSettingsPtr settings) override {
    return MockInputDeviceSettingsController::SetMouseSettings(
        id, std::move(settings));
  }
  bool SetPointingStickSettings(
      DeviceId id,
      ::ash::mojom::PointingStickSettingsPtr settings) override {
    return MockInputDeviceSettingsController::SetPointingStickSettings(
        id, std::move(settings));
  }
  bool SetGraphicsTabletSettings(
      DeviceId id,
      ::ash::mojom::GraphicsTabletSettingsPtr settings) override {
    return MockInputDeviceSettingsController::SetGraphicsTabletSettings(
        id, std::move(settings));
  }

  void StartObservingButtons(DeviceId id) override {
    observed_currently_ = true;
  }
  void StopObservingButtons() override { observed_currently_ = false; }

  void AddKeyboard(::ash::mojom::KeyboardPtr keyboard) {
    keyboards_.push_back(std::move(keyboard));
    observer_->OnKeyboardConnected(*keyboards_.back());
  }
  void RemoveKeyboard(uint32_t device_id) {
    auto iter =
        base::ranges::find_if(keyboards_, [device_id](const auto& keyboard) {
          return keyboard->id == device_id;
        });
    if (iter == keyboards_.end()) {
      return;
    }
    auto temp_keyboard = std::move(*iter);
    keyboards_.erase(iter);
    observer_->OnKeyboardDisconnected(*temp_keyboard);
  }
  void SetKeyboardPolicies(::ash::mojom::KeyboardPoliciesPtr policies) {
    keyboard_policies_ = std::move(policies);
    observer_->OnKeyboardPoliciesUpdated(*keyboard_policies_);
  }
  void SetMousePolicies(::ash::mojom::MousePoliciesPtr policies) {
    mouse_policies_ = std::move(policies);
    observer_->OnMousePoliciesUpdated(*mouse_policies_);
  }
  void AddMouse(::ash::mojom::MousePtr mouse) {
    mice_.push_back(std::move(mouse));
    observer_->OnMouseConnected(*mice_.back());
  }
  void RemoveMouse(uint32_t device_id) {
    auto iter = base::ranges::find_if(mice_, [device_id](const auto& mouse) {
      return mouse->id == device_id;
    });
    if (iter == mice_.end()) {
      return;
    }
    auto temp_mouse = std::move(*iter);
    mice_.erase(iter);
    observer_->OnMouseDisconnected(*temp_mouse);
  }
  void AddTouchpad(::ash::mojom::TouchpadPtr touchpad) {
    touchpads_.push_back(std::move(touchpad));
    observer_->OnTouchpadConnected(*touchpads_.back());
  }
  void RemoveTouchpad(uint32_t device_id) {
    auto iter =
        base::ranges::find_if(touchpads_, [device_id](const auto& touchpad) {
          return touchpad->id == device_id;
        });
    if (iter == touchpads_.end()) {
      return;
    }
    auto temp_touchpad = std::move(*iter);
    touchpads_.erase(iter);
    observer_->OnTouchpadDisconnected(*temp_touchpad);
  }
  void AddPointingStick(::ash::mojom::PointingStickPtr pointing_stick) {
    pointing_sticks_.push_back(std::move(pointing_stick));
    observer_->OnPointingStickConnected(*pointing_sticks_.back());
  }
  void RemovePointingStick(uint32_t device_id) {
    auto iter = base::ranges::find_if(pointing_sticks_,
                                      [device_id](const auto& pointing_stick) {
                                        return pointing_stick->id == device_id;
                                      });
    if (iter == pointing_sticks_.end()) {
      return;
    }
    auto temp_pointing_stick = std::move(*iter);
    pointing_sticks_.erase(iter);
    observer_->OnPointingStickDisconnected(*temp_pointing_stick);
  }
  void AddGraphicsTablet(::ash::mojom::GraphicsTabletPtr graphics_tablet) {
    graphics_tablets_.push_back(std::move(graphics_tablet));
    observer_->OnGraphicsTabletConnected(*graphics_tablets_.back());
  }
  void RemoveGraphicsTablet(uint32_t device_id) {
    auto iter = base::ranges::find_if(graphics_tablets_,
                                      [device_id](const auto& graphics_tablet) {
                                        return graphics_tablet->id == device_id;
                                      });
    if (iter == graphics_tablets_.end()) {
      return;
    }
    auto temp_pointing_stick = std::move(*iter);
    graphics_tablets_.erase(iter);
    observer_->OnGraphicsTabletDisconnected(*temp_pointing_stick);
  }
  int num_times_restore_default_keyboard_remappings_called() {
    return num_times_restore_default_keyboard_remappings_called_;
  }
  bool observed_currently() { return observed_currently_; }

 private:
  std::vector<::ash::mojom::KeyboardPtr> keyboards_;
  std::vector<::ash::mojom::TouchpadPtr> touchpads_;
  std::vector<::ash::mojom::MousePtr> mice_;
  std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_;
  std::vector<::ash::mojom::GraphicsTabletPtr> graphics_tablets_;
  ::ash::mojom::KeyboardPoliciesPtr keyboard_policies_ =
      ::ash::mojom::KeyboardPolicies::New();
  ::ash::mojom::MousePoliciesPtr mouse_policies_ =
      ::ash::mojom::MousePolicies::New();

  raw_ptr<InputDeviceSettingsController::Observer> observer_ = nullptr;
  int num_times_restore_default_keyboard_remappings_called_ = 0;
  bool observed_currently_ = false;
};

}  // namespace

class InputDeviceSettingsProviderTest : public views::ViewsTestBase {
 public:
  InputDeviceSettingsProviderTest() = default;
  ~InputDeviceSettingsProviderTest() override = default;

  void SetUp() override {
    feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
    feature_list_->InitWithFeatures(
        {features::kInputDeviceSettingsSplit,
         features::kPeripheralCustomization,
         features::kEnableKeyboardBacklightControlInSettings},
        {});
    views::ViewsTestBase::SetUp();
    widget_ =
        CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
    widget_->Show();
    scoped_resetter_ = std::make_unique<
        InputDeviceSettingsController::ScopedResetterForTest>();
    controller_ = std::make_unique<FakeInputDeviceSettingsController>();
    provider_ = std::make_unique<InputDeviceSettingsProvider>();
    provider_->SetWidgetForTesting(widget_.get());
    keyboard_brightness_control_delegate_ =
        std::make_unique<FakeKeyboardBrightnessControlDelegate>();
    provider_->SetKeyboardBrightnessControlDelegateForTesting(
        keyboard_brightness_control_delegate_.get());
    power_manager_client_ =
        std::make_unique<chromeos::FakePowerManagerClient>();
    histogram_tester_ = std::make_unique<base::HistogramTester>();
  }

  void TearDown() override {
    provider_.reset();
    controller_.reset();
    keyboard_brightness_control_delegate_.reset();
    power_manager_client_.reset();
    scoped_resetter_.reset();
    widget_.reset();
    views::ViewsTestBase::TearDown();
    feature_list_.reset();
    histogram_tester_.reset();
  }

 protected:
  std::unique_ptr<FakeInputDeviceSettingsController> controller_;
  std::unique_ptr<InputDeviceSettingsProvider> provider_;
  std::unique_ptr<FakeKeyboardBrightnessControlDelegate>
      keyboard_brightness_control_delegate_;
  std::unique_ptr<chromeos::FakePowerManagerClient> power_manager_client_;
  std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
  std::unique_ptr<InputDeviceSettingsController::ScopedResetterForTest>
      scoped_resetter_;
  std::unique_ptr<views::Widget> widget_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

TEST_F(InputDeviceSettingsProviderTest, TestSetKeyboardSettings) {
  controller_->AddKeyboard(kKeyboard1.Clone());
  controller_->AddKeyboard(kKeyboard2.Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  EXPECT_CALL(*controller_, SetKeyboardSettings(kKeyboard1.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetKeyboardSettings(kKeyboard1.id, kKeyboard1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());

  EXPECT_CALL(*controller_, SetKeyboardSettings(kKeyboard2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetKeyboardSettings(kKeyboard2.id, kKeyboard1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());

  EXPECT_CALL(*controller_, SetKeyboardSettings(kKeyboard2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(false));
  provider_->SetKeyboardSettings(kKeyboard2.id, kKeyboard1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_keyboard_list_updated());
}

TEST_F(InputDeviceSettingsProviderTest, TestRestoreDefaultKeyboardRemappings) {
  controller_->AddKeyboard(kKeyboard1.Clone());
  provider_->RestoreDefaultKeyboardRemappings(kKeyboard1.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      1, controller_->num_times_restore_default_keyboard_remappings_called());

  controller_->AddKeyboard(kKeyboard2.Clone());
  provider_->RestoreDefaultKeyboardRemappings(kKeyboard2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      2, controller_->num_times_restore_default_keyboard_remappings_called());
}

TEST_F(InputDeviceSettingsProviderTest, TestSetPointingStickSettings) {
  controller_->AddPointingStick(kPointingStick1.Clone());
  controller_->AddPointingStick(kPointingStick2.Clone());

  FakePointingStickSettingsObserver fake_observer;
  provider_->ObservePointingStickSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  EXPECT_CALL(*controller_,
              SetPointingStickSettings(kPointingStick1.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetPointingStickSettings(kPointingStick1.id,
                                      kPointingStick1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());

  EXPECT_CALL(*controller_,
              SetPointingStickSettings(kPointingStick2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetPointingStickSettings(kPointingStick2.id,
                                      kPointingStick1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());

  EXPECT_CALL(*controller_,
              SetPointingStickSettings(kPointingStick2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(false));
  provider_->SetPointingStickSettings(kPointingStick2.id,
                                      kPointingStick1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_called());
}

TEST_F(InputDeviceSettingsProviderTest, TestSetMouseSettings) {
  controller_->AddMouse(kMouse1.Clone());
  controller_->AddMouse(kMouse2.Clone());

  FakeMouseSettingsObserver fake_observer;
  provider_->ObserveMouseSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  EXPECT_CALL(*controller_, SetMouseSettings(kMouse1.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetMouseSettings(kMouse1.id, kMouse1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_mouse_list_updated());

  EXPECT_CALL(*controller_, SetMouseSettings(kMouse2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetMouseSettings(kMouse2.id, kMouse1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_mouse_list_updated());

  EXPECT_CALL(*controller_, SetMouseSettings(kMouse2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(false));
  provider_->SetMouseSettings(kMouse2.id, kMouse1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_mouse_list_updated());
}

TEST_F(InputDeviceSettingsProviderTest, TestSetTouchpadSettings) {
  controller_->AddTouchpad(kTouchpad1.Clone());
  controller_->AddTouchpad(kTouchpad2.Clone());

  FakeTouchpadSettingsObserver fake_observer;
  provider_->ObserveTouchpadSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  EXPECT_CALL(*controller_, SetTouchpadSettings(kTouchpad1.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetTouchpadSettings(kTouchpad1.id, kTouchpad1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());

  EXPECT_CALL(*controller_, SetTouchpadSettings(kTouchpad2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetTouchpadSettings(kTouchpad2.id, kTouchpad1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());

  EXPECT_CALL(*controller_, SetTouchpadSettings(kTouchpad2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(false));
  provider_->SetTouchpadSettings(kTouchpad2.id, kTouchpad1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_called());
}

TEST_F(InputDeviceSettingsProviderTest, TestSetGraphicsTabletSettings) {
  controller_->AddGraphicsTablet(kGraphicsTablet1.Clone());
  controller_->AddGraphicsTablet(kGraphicsTablet2.Clone());

  FakeGraphicsTabletSettingsObserver fake_observer;
  provider_->ObserveGraphicsTabletSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  EXPECT_CALL(*controller_,
              SetGraphicsTabletSettings(kGraphicsTablet1.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetGraphicsTabletSettings(kGraphicsTablet1.id,
                                       kGraphicsTablet1.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_graphics_tablet_list_updated());

  EXPECT_CALL(*controller_,
              SetGraphicsTabletSettings(kGraphicsTablet2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(true));
  provider_->SetGraphicsTabletSettings(kGraphicsTablet2.id,
                                       kGraphicsTablet2.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_graphics_tablet_list_updated());

  EXPECT_CALL(*controller_,
              SetGraphicsTabletSettings(kGraphicsTablet2.id, testing::_))
      .Times(1)
      .WillOnce(testing::Return(false));
  provider_->SetGraphicsTabletSettings(kGraphicsTablet2.id,
                                       kGraphicsTablet2.settings->Clone());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_graphics_tablet_list_updated());
}

TEST_F(InputDeviceSettingsProviderTest, TestKeyboardSettingsObeserver) {
  std::vector<::ash::mojom::KeyboardPtr> expected_keyboards;
  expected_keyboards.push_back(kKeyboard1.Clone());
  controller_->AddKeyboard(kKeyboard1.Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual(expected_keyboards, fake_observer.keyboards());

  expected_keyboards.push_back(kKeyboard2.Clone());
  controller_->AddKeyboard(kKeyboard2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual(expected_keyboards, fake_observer.keyboards());

  expected_keyboards.pop_back();
  controller_->RemoveKeyboard(kKeyboard2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual(expected_keyboards, fake_observer.keyboards());
}

TEST_F(InputDeviceSettingsProviderTest,
       TestKeyboardSettingsObeserverPolicyUpdates) {
  controller_->AddKeyboard(kKeyboard1.Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());

  controller_->SetKeyboardPolicies(::ash::mojom::KeyboardPolicies::New());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(2, fake_observer.num_times_keyboard_policies_updated());
}

TEST_F(InputDeviceSettingsProviderTest, TestDuplicatesRemoved) {
  std::vector<::ash::mojom::KeyboardPtr> expected_keyboards;

  auto keyboard1 = kKeyboard1.Clone();
  keyboard1->device_key = "test-key1";
  expected_keyboards.push_back(keyboard1.Clone());
  controller_->AddKeyboard(keyboard1.Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual</*sorted=*/true>(expected_keyboards,
                                    fake_observer.keyboards());

  auto keyboard2 = kKeyboard2.Clone();
  keyboard2->device_key = "test-key1";
  controller_->AddKeyboard(keyboard2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual</*sorted=*/true>(expected_keyboards,
                                    fake_observer.keyboards());

  controller_->RemoveKeyboard(kKeyboard2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_keyboard_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_keyboard_policies_updated());
  ExpectListsEqual</*sorted=*/true>(expected_keyboards,
                                    fake_observer.keyboards());
}

TEST_F(InputDeviceSettingsProviderTest, TestSortingExternalFirst) {
  std::vector<::ash::mojom::KeyboardPtr> expected_keyboards;

  auto keyboard1 = kKeyboard1.Clone();
  auto keyboard2 = kKeyboard2.Clone();

  // Guarantee that keyboard 1 which is internal, has a higher id than keyboard
  // 2 to properly test that external devices always come first in the list.
  keyboard1->id = 2;
  keyboard2->id = 1;
  ASSERT_FALSE(keyboard1->is_external);
  ASSERT_TRUE(keyboard2->is_external);

  controller_->AddKeyboard(keyboard1->Clone());
  controller_->AddKeyboard(keyboard2->Clone());
  expected_keyboards.push_back(keyboard2->Clone());
  expected_keyboards.push_back(keyboard1->Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  ExpectListsEqual</*sorted=*/true>(expected_keyboards,
                                    fake_observer.keyboards());
}

TEST_F(InputDeviceSettingsProviderTest, TestSortingExternalFirstThenById) {
  std::vector<::ash::mojom::KeyboardPtr> expected_keyboards;

  controller_->AddKeyboard(kKeyboard1.Clone());
  ASSERT_FALSE(kKeyboard1.is_external);

  controller_->AddKeyboard(kKeyboard2.Clone());
  ASSERT_TRUE(kKeyboard2.is_external);

  controller_->AddKeyboard(kKeyboard3.Clone());
  ASSERT_TRUE(kKeyboard3.is_external);
  ASSERT_LT(kKeyboard2.id, kKeyboard3.id);

  expected_keyboards.push_back(kKeyboard3.Clone());
  expected_keyboards.push_back(kKeyboard2.Clone());
  expected_keyboards.push_back(kKeyboard1.Clone());

  FakeKeyboardSettingsObserver fake_observer;
  provider_->ObserveKeyboardSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  ExpectListsEqual</*sorted=*/true>(expected_keyboards,
                                    fake_observer.keyboards());
}

TEST_F(InputDeviceSettingsProviderTest, TestMouseSettingsObeserver) {
  std::vector<::ash::mojom::MousePtr> expected_mice;
  expected_mice.push_back(kMouse1.Clone());
  controller_->AddMouse(kMouse1.Clone());

  FakeMouseSettingsObserver fake_observer;
  provider_->ObserveMouseSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_mouse_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_mouse_policies_updated());
  ExpectListsEqual(expected_mice, fake_observer.mice());

  expected_mice.push_back(kMouse2.Clone());
  controller_->AddMouse(kMouse2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_mouse_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_mouse_policies_updated());
  ExpectListsEqual(expected_mice, fake_observer.mice());

  expected_mice.pop_back();
  controller_->RemoveMouse(kMouse2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_mouse_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_mouse_policies_updated());
  ExpectListsEqual(expected_mice, fake_observer.mice());
}

TEST_F(InputDeviceSettingsProviderTest,
       TestMouseSettingsObeserverPolicyUpdates) {
  controller_->AddMouse(kMouse1.Clone());

  FakeMouseSettingsObserver fake_observer;
  provider_->ObserveMouseSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_mouse_list_updated());
  EXPECT_EQ(1, fake_observer.num_times_mouse_policies_updated());

  controller_->SetMousePolicies(::ash::mojom::MousePolicies::New());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_mouse_list_updated());
  EXPECT_EQ(2, fake_observer.num_times_mouse_policies_updated());
}

TEST_F(InputDeviceSettingsProviderTest, TestTouchpadSettingsObeserver) {
  std::vector<::ash::mojom::TouchpadPtr> expected_touchpads;
  expected_touchpads.push_back(kTouchpad1.Clone());
  controller_->AddTouchpad(kTouchpad1.Clone());

  FakeTouchpadSettingsObserver fake_observer;
  provider_->ObserveTouchpadSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());
  ExpectListsEqual(expected_touchpads, fake_observer.touchpads());

  expected_touchpads.push_back(kTouchpad2.Clone());
  controller_->AddTouchpad(kTouchpad2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_called());
  ExpectListsEqual(expected_touchpads, fake_observer.touchpads());

  expected_touchpads.pop_back();
  controller_->RemoveTouchpad(kTouchpad2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_called());
  ExpectListsEqual(expected_touchpads, fake_observer.touchpads());
}

TEST_F(InputDeviceSettingsProviderTest, TestPointingStickSettingsObeserver) {
  std::vector<::ash::mojom::PointingStickPtr> expected_pointing_sticks;
  expected_pointing_sticks.push_back(kPointingStick1.Clone());
  controller_->AddPointingStick(kPointingStick1.Clone());

  FakePointingStickSettingsObserver fake_observer;
  provider_->ObservePointingStickSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_called());
  ExpectListsEqual(expected_pointing_sticks, fake_observer.pointing_sticks());

  expected_pointing_sticks.push_back(kPointingStick2.Clone());
  controller_->AddPointingStick(kPointingStick2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_called());
  ExpectListsEqual(expected_pointing_sticks, fake_observer.pointing_sticks());

  expected_pointing_sticks.pop_back();
  controller_->RemovePointingStick(kPointingStick2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_called());
  ExpectListsEqual(expected_pointing_sticks, fake_observer.pointing_sticks());
}

TEST_F(InputDeviceSettingsProviderTest, TestGraphicsTabletSettingsObeserver) {
  std::vector<::ash::mojom::GraphicsTabletPtr> expected_graphics_tablets;
  expected_graphics_tablets.push_back(kGraphicsTablet1.Clone());
  controller_->AddGraphicsTablet(kGraphicsTablet1.Clone());

  FakeGraphicsTabletSettingsObserver fake_observer;
  provider_->ObserveGraphicsTabletSettings(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1, fake_observer.num_times_graphics_tablet_list_updated());
  ExpectListsEqual(expected_graphics_tablets, fake_observer.graphics_tablets());

  expected_graphics_tablets.push_back(kGraphicsTablet2.Clone());
  controller_->AddGraphicsTablet(kGraphicsTablet2.Clone());

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(2, fake_observer.num_times_graphics_tablet_list_updated());
  ExpectListsEqual(expected_graphics_tablets, fake_observer.graphics_tablets());

  expected_graphics_tablets.pop_back();
  controller_->RemoveGraphicsTablet(kGraphicsTablet2.id);

  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(3, fake_observer.num_times_graphics_tablet_list_updated());
  ExpectListsEqual(expected_graphics_tablets, fake_observer.graphics_tablets());
}

TEST_F(InputDeviceSettingsProviderTest, ObservationMatchesWidget) {
  provider_->StartObserving(kMouse1.id);
  EXPECT_TRUE(controller_->observed_currently());

  widget_->Hide();
  EXPECT_FALSE(controller_->observed_currently());

  widget_->Show();
  EXPECT_TRUE(controller_->observed_currently());

  controller_->StopObservingButtons();
  EXPECT_FALSE(controller_->observed_currently());
}

TEST_F(InputDeviceSettingsProviderTest, ObservationStateRemembered) {
  provider_->StartObserving(kMouse1.id);
  EXPECT_TRUE(controller_->observed_currently());

  widget_->Hide();
  EXPECT_FALSE(controller_->observed_currently());

  provider_->StopObserving();
  EXPECT_FALSE(controller_->observed_currently());

  widget_->Show();
  EXPECT_FALSE(controller_->observed_currently());

  provider_->StartObserving(kMouse1.id);
  EXPECT_TRUE(controller_->observed_currently());
}

TEST_F(InputDeviceSettingsProviderTest, ObservationStateOnDestruction) {
  provider_->StartObserving(kMouse1.id);
  EXPECT_TRUE(controller_->observed_currently());

  widget_.reset();
  EXPECT_FALSE(controller_->observed_currently());
}

TEST_F(InputDeviceSettingsProviderTest, ButtonPressObserverTest) {
  FakeButtonPressObserver fake_observer;
  provider_->ObserveButtonPresses(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  ::ash::mojom::ButtonPtr expected_button =
      ::ash::mojom::Button::NewCustomizableButton(
          ::ash::mojom::CustomizableButton::kMiddle);
  provider_->OnCustomizableMouseButtonPressed(kMouse1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(*expected_button, fake_observer.last_pressed_button());

  expected_button = ::ash::mojom::Button::NewCustomizableButton(
      ::ash::mojom::CustomizableButton::kForward);
  provider_->OnCustomizablePenButtonPressed(kGraphicsTablet1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(*expected_button, fake_observer.last_pressed_button());

  expected_button = ::ash::mojom::Button::NewVkey(ui::VKEY_0);
  provider_->OnCustomizablePenButtonPressed(kGraphicsTablet1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(*expected_button, fake_observer.last_pressed_button());
}

TEST_F(InputDeviceSettingsProviderTest, KeyboardBrightnessObserverTest) {
  FakeKeyboardBrightnessObserver fake_observer;
  EXPECT_EQ(0, fake_observer.num_times_called());

  // Set initial brightness to 40.0.
  double initial_brightness = 40.0;
  keyboard_brightness_control_delegate_->HandleSetKeyboardBrightness(
      initial_brightness, /*gradual=*/false,
      KeyboardBrightnessChangeSource::kSettingsApp);
  EXPECT_EQ(KeyboardBrightnessChangeSource::kSettingsApp,
            keyboard_brightness_control_delegate_
                ->keyboard_brightness_change_source());

  provider_->ObserveKeyboardBrightness(
      fake_observer.receiver.BindNewPipeAndPassRemote());
  base::RunLoop().RunUntilIdle();

  // OnKeyboardBrightnessChange is called when observer is registered.
  EXPECT_EQ(1, fake_observer.num_times_called());

  double expected_brightness = 66.6;

  power_manager::BacklightBrightnessChange brightness_change;
  brightness_change.set_percent(expected_brightness);
  brightness_change.set_cause(
      power_manager::BacklightBrightnessChange_Cause_USER_REQUEST);
  provider_->KeyboardBrightnessChanged(brightness_change);
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(expected_brightness, fake_observer.keyboard_brightness());
  EXPECT_EQ(2, fake_observer.num_times_called());
}

TEST_F(InputDeviceSettingsProviderTest, LidStateObserverTest) {
  FakeLidStateObserver fake_observer;
  base::test::TestFuture<bool> future;

  // Attach a lid state observer.
  provider_->ObserveLidState(fake_observer.receiver.BindNewPipeAndPassRemote(),
                             future.GetCallback());
  base::RunLoop().RunUntilIdle();

  // Open the lid.
  provider_->LidEventReceived(chromeos::PowerManagerClient::LidState::OPEN,
                              /*timestamp=*/{});
  base::RunLoop().RunUntilIdle();
  ASSERT_TRUE(fake_observer.is_lid_open());
  EXPECT_EQ(1, fake_observer.num_lid_state_change_calls());

  // Close the lid.
  provider_->LidEventReceived(chromeos::PowerManagerClient::LidState::CLOSED,
                              /*timestamp=*/{});
  base::RunLoop().RunUntilIdle();
  ASSERT_FALSE(fake_observer.is_lid_open());
  EXPECT_EQ(2, fake_observer.num_lid_state_change_calls());
}

TEST_F(InputDeviceSettingsProviderTest,
       KeyboardAmbientLightSensorObserverTest) {
  provider_->SetKeyboardBrightnessControlDelegateForTesting(
      keyboard_brightness_control_delegate_.get());
  FakeKeyboardAmbientLightSensorObserver fake_observer;
  EXPECT_EQ(0, fake_observer.num_times_called());

  // Start observing the keyboard ambient light sensor
  provider_->ObserveKeyboardAmbientLightSensor(
      fake_observer.receiver.BindNewPipeAndPassRemote());
  base::RunLoop().RunUntilIdle();

  // OnKeyboardAmbientLightSensorEnabledChange is called to set initial value
  // when observer is registered.
  EXPECT_EQ(1, fake_observer.num_times_called());

  // Enable the keyboard ambient light sensor
  {
    bool keyboard_ambient_light_sensor_enabled = true;
    power_manager::AmbientLightSensorChange change;
    change.set_cause(
        power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
    change.set_sensor_enabled(keyboard_ambient_light_sensor_enabled);
    provider_->KeyboardAmbientLightSensorEnabledChanged(change);
    base::RunLoop().RunUntilIdle();
    EXPECT_EQ(keyboard_ambient_light_sensor_enabled,
              fake_observer.keyboard_ambient_light_sensor_enabled());
    EXPECT_EQ(2, fake_observer.num_times_called());
  }

  // Disable the keyboard ambient light sensor
  {
    bool keyboard_ambient_light_sensor_enabled = false;
    power_manager::AmbientLightSensorChange change;
    change.set_cause(
        power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
    change.set_sensor_enabled(keyboard_ambient_light_sensor_enabled);
    provider_->KeyboardAmbientLightSensorEnabledChanged(change);
    base::RunLoop().RunUntilIdle();
    EXPECT_EQ(keyboard_ambient_light_sensor_enabled,
              fake_observer.keyboard_ambient_light_sensor_enabled());
    EXPECT_EQ(3, fake_observer.num_times_called());
  }
}

TEST_F(InputDeviceSettingsProviderTest, SetKeyboardBrightness) {
  double adjustedBrightness = 60.9;
  provider_->SetKeyboardBrightness(adjustedBrightness);
  EXPECT_EQ(adjustedBrightness,
            keyboard_brightness_control_delegate_->keyboard_brightness());
  // When user change keyboard brightness from settings(using provider), the
  // change source should be kSettingsApp.
  EXPECT_EQ(KeyboardBrightnessChangeSource::kSettingsApp,
            keyboard_brightness_control_delegate_
                ->keyboard_brightness_change_source());

  adjustedBrightness = 20.3;
  provider_->SetKeyboardBrightness(adjustedBrightness);
  EXPECT_EQ(adjustedBrightness,
            keyboard_brightness_control_delegate_->keyboard_brightness());
}

TEST_F(InputDeviceSettingsProviderTest, SetKeyboardAmbientLightSensorEnabled) {
  // Verify initial state is enabled.
  EXPECT_TRUE(keyboard_brightness_control_delegate_
                  ->keyboard_ambient_light_sensor_enabled());

  // Disable the ambient light sensor.
  provider_->SetKeyboardAmbientLightSensorEnabled(false);
  EXPECT_FALSE(keyboard_brightness_control_delegate_
                   ->keyboard_ambient_light_sensor_enabled());

  // Re-enable the ambient light sensor and verify.
  provider_->SetKeyboardAmbientLightSensorEnabled(true);
  EXPECT_TRUE(keyboard_brightness_control_delegate_
                  ->keyboard_ambient_light_sensor_enabled());
}

TEST_F(InputDeviceSettingsProviderTest, ButtonPressObserverFollowsWindowFocus) {
  FakeButtonPressObserver fake_observer;
  provider_->ObserveButtonPresses(
      fake_observer.receiver.BindNewPipeAndPassRemote());

  widget_->Hide();

  ::ash::mojom::ButtonPtr expected_button =
      ::ash::mojom::Button::NewCustomizableButton(
          ::ash::mojom::CustomizableButton::kMiddle);

  provider_->OnCustomizableMouseButtonPressed(kMouse1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(fake_observer.has_last_pressed_button());

  provider_->OnCustomizablePenButtonPressed(kGraphicsTablet1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(fake_observer.has_last_pressed_button());

  provider_->OnCustomizableTabletButtonPressed(kGraphicsTablet1,
                                               *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(fake_observer.has_last_pressed_button());

  widget_->Show();
  provider_->OnCustomizableMouseButtonPressed(kMouse1, *expected_button);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(*expected_button, fake_observer.last_pressed_button());
}

TEST_F(InputDeviceSettingsProviderTest, HasKeyboardBacklight) {
  base::test::TestFuture<bool> future;

  power_manager_client_->set_has_keyboard_backlight(true);
  provider_->HasKeyboardBacklight(future.GetCallback());
  EXPECT_TRUE(future.Get<0>());

  future.Clear();
  power_manager_client_->set_has_keyboard_backlight(false);
  provider_->HasKeyboardBacklight(future.GetCallback());
  EXPECT_FALSE(future.Get<0>());
}

TEST_F(InputDeviceSettingsProviderTest, HasAmbientLightSensor) {
  base::test::TestFuture<bool> future;

  power_manager_client_->set_has_ambient_light_sensor(true);
  provider_->HasAmbientLightSensor(future.GetCallback());
  EXPECT_TRUE(future.Get<0>());

  future.Clear();
  power_manager_client_->set_has_ambient_light_sensor(false);
  provider_->HasAmbientLightSensor(future.GetCallback());
  EXPECT_FALSE(future.Get<0>());
}

TEST_F(InputDeviceSettingsProviderTest, RecordKeyboardColorLinkClicked) {
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.ColorLinkClicked", 0);
  provider_->RecordKeyboardColorLinkClicked();
  base::RunLoop().RunUntilIdle();
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.ColorLinkClicked", 1);
}

TEST_F(InputDeviceSettingsProviderTest,
       RecordKeyboardBrightnessChangeFromSlider) {
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.BrightnessSliderAdjusted", 0);
  provider_->RecordKeyboardBrightnessChangeFromSlider(40.0);
  base::RunLoop().RunUntilIdle();
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.BrightnessSliderAdjusted", 1);
}

TEST_F(InputDeviceSettingsProviderTest,
       RecordSetKeyboardAutoBrightnessEnabled) {
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed", 0);

  provider_->SetKeyboardAmbientLightSensorEnabled(true);
  base::RunLoop().RunUntilIdle();

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed", 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed",
      /*sample=*/true, /*expected_count=*/1);

  provider_->SetKeyboardAmbientLightSensorEnabled(false);
  base::RunLoop().RunUntilIdle();

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed", 2);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed",
      /*sample=*/true, /*expected_count=*/1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Settings.Device.Keyboard.AutoBrightnessEnabled.Changed",
      /*sample=*/false, /*expected_count=*/1);
}

TEST_F(InputDeviceSettingsProviderTest,
       RecordKeyboardAmbientLightSensorDisabledCause) {
  // No histograms should have been recorded yet.
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Settings.Keyboard.UserInitiated."
      "AmbientLightSensorDisabledCause",
      /*expected_count=*/0);

  // Verify histogram recording when ALS is disabled via settings app.
  {
    power_manager::AmbientLightSensorChange cause_settings_app;
    cause_settings_app.set_sensor_enabled(false);
    cause_settings_app.set_cause(
        power_manager::
            AmbientLightSensorChange_Cause_USER_REQUEST_SETTINGS_APP);
    provider_->KeyboardAmbientLightSensorEnabledChanged(cause_settings_app);
    histogram_tester_->ExpectUniqueSample(
        "ChromeOS.Settings.Keyboard.UserInitiated."
        "AmbientLightSensorDisabledCause",
        KeyboardAmbientLightSensorDisabledCause::kUserRequestSettingsApp, 1);
  }

  // Ensure enabling ALS does not emit histogram.
  {
    power_manager::AmbientLightSensorChange cause_settings_app;
    cause_settings_app.set_sensor_enabled(true);
    cause_settings_app.set_cause(
        power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
    provider_->KeyboardAmbientLightSensorEnabledChanged(cause_settings_app);
    histogram_tester_->ExpectTotalCount(
        "ChromeOS.Settings.Keyboard.UserInitiated."
        "AmbientLightSensorDisabledCause",
        /*expected_count=*/1);
  }

  // Test histogram update when ALS is disabled due to brightness change.
  {
    power_manager::AmbientLightSensorChange cause_user_request;
    cause_user_request.set_sensor_enabled(false);
    cause_user_request.set_cause(
        power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
    provider_->KeyboardAmbientLightSensorEnabledChanged(cause_user_request);
    histogram_tester_->ExpectBucketCount(
        "ChromeOS.Settings.Keyboard.UserInitiated."
        "AmbientLightSensorDisabledCause",
        KeyboardAmbientLightSensorDisabledCause::kBrightnessUserRequest, 1);
  }
}

}  // namespace ash::settings