blob: 22a6ead7730a6c07d1add073e95b8541ccbadd85 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/input_device_settings_controller.h"
#include <cstdint>
#include <memory>
#include <optional>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/events/event_rewriter_controller_impl.h"
#include "ash/public/cpp/ash_prefs.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
#include "ash/system/input_device_settings/input_device_settings_defaults.h"
#include "ash/system/input_device_settings/input_device_settings_metadata.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/pref_handlers/graphics_tablet_pref_handler_impl.h"
#include "ash/system/input_device_settings/pref_handlers/keyboard_pref_handler.h"
#include "ash/system/input_device_settings/pref_handlers/mouse_pref_handler_impl.h"
#include "ash/system/input_device_settings/pref_handlers/pointing_stick_pref_handler_impl.h"
#include "ash/system/input_device_settings/pref_handlers/touchpad_pref_handler_impl.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/ranges/algorithm.h"
#include "base/ranges/functional.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/known_user.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/udev_linux/fake_udev_loader.h"
#include "mojo/public/cpp/bindings/clone_traits.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/ash/keyboard_capability.h"
#include "ui/events/ash/mojom/extended_fkeys_modifier.mojom-shared.h"
#include "ui/events/ash/mojom/simulate_right_click_modifier.mojom-shared.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/keyboard_device.h"
#include "ui/events/devices/touchpad_device.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
namespace ash {
using DeviceId = InputDeviceSettingsController::DeviceId;
namespace {
const ui::KeyboardDevice kSampleKeyboardInternal(5,
ui::INPUT_DEVICE_INTERNAL,
"kSampleKeyboardInternal",
"",
base::FilePath("path5"),
0x1111,
0x1111,
0);
const ui::KeyboardDevice kSampleKeyboardInternal2(4,
ui::INPUT_DEVICE_INTERNAL,
"kSampleKeyboardInternal2",
"",
base::FilePath("path4"),
0x1111,
0x1111,
0);
const ui::KeyboardDevice kSampleKeyboardBluetooth(10,
ui::INPUT_DEVICE_BLUETOOTH,
"kSampleKeyboardBluetooth");
const ui::KeyboardDevice kSampleKeyboardUsb(15,
ui::INPUT_DEVICE_USB,
"kSampleKeyboardUsb",
"",
base::FilePath("path15"),
0x1111,
0x2222,
0);
const ui::KeyboardDevice kSampleKeyboardUsb2(20,
ui::INPUT_DEVICE_USB,
"kSampleKeyboardUsb2",
"",
base::FilePath("path20"),
0x1111,
0x3333,
0);
const ui::TouchpadDevice kSampleTouchpadInternal(1,
ui::INPUT_DEVICE_INTERNAL,
"kSampleTouchpadInternal",
"",
base::FilePath(),
0x1111,
0x4444,
0);
const ui::TouchpadDevice kSampleHapticTouchpadInternal(
25,
ui::INPUT_DEVICE_INTERNAL,
"kSampleHapticTouchpadInternal",
"",
base::FilePath(),
0x1111,
0x4444,
0,
true);
const ui::TouchpadDevice kSampleTouchpadExternal(26,
ui::INPUT_DEVICE_USB,
"kSampleTouchpadExternal",
"",
base::FilePath(),
0x1111,
0x5555,
0);
const ui::InputDevice kSamplePointingStickInternal(
2,
ui::INPUT_DEVICE_INTERNAL,
"kSamplePointingStickInternal");
const ui::InputDevice kSamplePointingStickExternal(
3,
ui::INPUT_DEVICE_USB,
"kSamplePointingStickExternal");
const ui::InputDevice kSampleMouseUsb(3,
ui::INPUT_DEVICE_USB,
"kSampleMouseUsb",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0x0001,
/*product=*/0x0002,
/*version=*/0x0003);
const ui::InputDevice kSampleGraphicsTablet(4,
ui::INPUT_DEVICE_USB,
"kSampleGraphicsTablet",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0x0004,
/*product=*/0x0005,
/*version=*/0x0006);
const ui::InputDevice kSampleUncustomizableMouse(5,
ui::INPUT_DEVICE_USB,
"kSampleUncustomizableMouse",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0xffff,
/*product=*/0xffff,
/*version=*/0x0009);
const ui::InputDevice kSampleCustomizableMouse(6,
ui::INPUT_DEVICE_USB,
"kSampleCustomizableMouse",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0xffff,
/*product=*/0xfffe,
/*version=*/0x0009);
const ui::InputDevice kSampleKeyboardMouseCombo(7,
ui::INPUT_DEVICE_USB,
"kSampleKeyboardMouseCombo",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0x046d,
/*product=*/0xc548,
/*version=*/0x0009);
const ui::InputDevice kSampleUnCustomizableGraphicsTablet(
27,
ui::INPUT_DEVICE_USB,
"kSampleGraphicsTablet",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0xeeee,
/*product=*/0xeeee,
/*version=*/0x0006);
const ui::InputDevice kSamplekWacomOnePenTabletS(28,
ui::INPUT_DEVICE_USB,
"kSamplekWacomOnePenTabletS",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0x0531,
/*product=*/0x0100,
/*version=*/0x0006);
const ui::KeyboardDevice kSampleKeychronKeyboard(29,
ui::INPUT_DEVICE_USB,
"kSampleKeychronKeyboard",
/*phys=*/"",
/*sys_path=*/base::FilePath(),
/*vendor=*/0x3434,
/*product=*/0x0311,
/*version=*/0);
const ui::KeyboardDevice kSampleSplitModifierKeyboard(
21,
ui::INPUT_DEVICE_INTERNAL,
"kSampleSplitModifierKeyboard",
/*has_assistant_key=*/true,
/*has_function_key=*/true);
constexpr char kUserEmail1[] = "example1@abc.com";
constexpr char kUserEmail2[] = "joy@abc.com";
constexpr char kUserEmail3[] = "joy1@abc.com";
const AccountId account_id_1 =
AccountId::FromUserEmailGaiaId(kUserEmail1, kUserEmail1);
const AccountId account_id_2 =
AccountId::FromUserEmailGaiaId(kUserEmail2, kUserEmail2);
const AccountId account_id_3 =
AccountId::FromUserEmailGaiaId(kUserEmail3, kUserEmail3);
constexpr char kKbdTopRowPropertyName[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
constexpr char kKbdTopRowLayout1Tag[] = "1";
constexpr char kKbdTopRowLayout2Tag[] = "2";
constexpr char kBluetoothDeviceName[] = "Bluetooth Device";
constexpr char kBluetoothDevicePublicAddress[] = "01:23:45:67:89:AB";
class FakeDeviceManager {
public:
FakeDeviceManager() = default;
FakeDeviceManager(const FakeDeviceManager&) = delete;
FakeDeviceManager& operator=(const FakeDeviceManager&) = delete;
~FakeDeviceManager() = default;
// Add a fake keyboard to DeviceDataManagerTestApi and provide layout info to
// fake udev.
void AddFakeKeyboard(const ui::KeyboardDevice& fake_keyboard,
const std::string& layout) {
fake_keyboard_devices_.push_back(fake_keyboard);
std::map<std::string, std::string> sysfs_properties;
std::map<std::string, std::string> sysfs_attributes;
sysfs_properties[kKbdTopRowPropertyName] = layout;
fake_udev_.AddFakeDevice(fake_keyboard.name, fake_keyboard.sys_path.value(),
/*subsystem=*/"input", /*devnode=*/std::nullopt,
/*devtype=*/std::nullopt,
std::move(sysfs_attributes),
std::move(sysfs_properties));
ui::DeviceDataManagerTestApi().SetKeyboardDevices(fake_keyboard_devices_);
}
// Add a fake graphics tablet to DeviceDataManagerTestApi and provide layout
// info to fake udev.
void AddFakeGraphicsTablet(const ui::InputDevice& fake_graphics_tablet) {
fake_graphics_tablet_devices_.push_back(fake_graphics_tablet);
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
fake_graphics_tablet_devices_);
}
// Add a fake mouse to DeviceDataManagerTestApi and provide layout
// info to fake udev.
void AddFakeMouse(const ui::InputDevice& fake_mouse) {
fake_mouse_devices_.push_back(fake_mouse);
ui::DeviceDataManagerTestApi().SetMouseDevices(fake_mouse_devices_);
}
void RemoveAllDevices() {
fake_udev_.Reset();
fake_keyboard_devices_.clear();
fake_graphics_tablet_devices_.clear();
fake_mouse_devices_.clear();
ui::DeviceDataManagerTestApi().SetKeyboardDevices(fake_keyboard_devices_);
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
fake_graphics_tablet_devices_);
ui::DeviceDataManagerTestApi().SetMouseDevices(fake_mouse_devices_);
}
private:
testing::FakeUdevLoader fake_udev_;
std::vector<ui::KeyboardDevice> fake_keyboard_devices_;
std::vector<ui::InputDevice> fake_graphics_tablet_devices_;
std::vector<ui::InputDevice> fake_mouse_devices_;
};
mojom::KeyboardSettingsPtr CreateNewKeyboardSettings() {
mojom::KeyboardSettingsPtr settings = mojom::KeyboardSettings::New();
settings->six_pack_key_remappings = mojom::SixPackKeyInfo::New();
return settings;
}
} // namespace
class FakeKeyboardPrefHandler : public KeyboardPrefHandler {
public:
void InitializeKeyboardSettings(
PrefService* pref_service,
const mojom::KeyboardPolicies& keyboard_policies,
mojom::Keyboard* keyboard) override {
keyboard->settings = CreateNewKeyboardSettings();
num_keyboard_settings_initialized_++;
}
void InitializeLoginScreenKeyboardSettings(
PrefService* local_state,
const AccountId& account_id,
const mojom::KeyboardPolicies& keyboard_policies,
mojom::Keyboard* keyboard) override {
num_login_screen_keyboard_settings_initialized_++;
}
void UpdateKeyboardSettings(PrefService* pref_service,
const mojom::KeyboardPolicies& keyboard_policies,
const mojom::Keyboard& keyboard) override {
num_keyboard_settings_updated_++;
}
void UpdateLoginScreenKeyboardSettings(
PrefService* local_state,
const AccountId& account_id,
const mojom::KeyboardPolicies& keyboard_policies,
const mojom::Keyboard& keyboard) override {
num_login_screen_keyboard_settings_updated_++;
}
void InitializeWithDefaultKeyboardSettings(
const mojom::KeyboardPolicies& keyboard_policies,
mojom::Keyboard* keyboard) override {
keyboard->settings = CreateNewKeyboardSettings();
num_initialize_default_keyboard_settings_calls_++;
}
void UpdateDefaultChromeOSKeyboardSettings(
PrefService* pref_service,
const mojom::KeyboardPolicies& keyboard_policies,
const mojom::Keyboard& keyboard) override {}
void UpdateDefaultNonChromeOSKeyboardSettings(
PrefService* pref_service,
const mojom::KeyboardPolicies& keyboard_policies,
const mojom::Keyboard& keyboard) override {}
void UpdateDefaultSplitModifierKeyboardSettings(
PrefService* pref_service,
const mojom::KeyboardPolicies& keyboard_policies,
const mojom::Keyboard& keyboard) override {}
uint32_t num_keyboard_settings_initialized() {
return num_keyboard_settings_initialized_;
}
uint32_t num_keyboard_settings_updated() {
return num_keyboard_settings_updated_;
}
uint32_t num_login_screen_keyboard_settings_initialized() {
return num_login_screen_keyboard_settings_initialized_;
}
uint32_t num_login_screen_keyboard_settings_updated() {
return num_login_screen_keyboard_settings_updated_;
}
uint32_t num_initialize_default_keyboard_settings_calls() {
return num_initialize_default_keyboard_settings_calls_;
}
void reset_num_keyboard_settings_initialized() {
num_keyboard_settings_initialized_ = 0;
}
private:
uint32_t num_keyboard_settings_initialized_ = 0;
uint32_t num_keyboard_settings_updated_ = 0;
uint32_t num_login_screen_keyboard_settings_initialized_ = 0;
uint32_t num_login_screen_keyboard_settings_updated_ = 0;
uint32_t num_initialize_default_keyboard_settings_calls_ = 0;
};
class FakeInputDeviceSettingsControllerObserver
: public InputDeviceSettingsController::Observer {
public:
void OnKeyboardConnected(const mojom::Keyboard& keyboard) override {
num_keyboards_connected_++;
}
void OnKeyboardDisconnected(const mojom::Keyboard& keyboard) override {
num_keyboards_connected_--;
}
void OnKeyboardSettingsUpdated(const mojom::Keyboard& keyboard) override {
num_keyboards_settings_updated_++;
}
void OnTouchpadSettingsUpdated(const mojom::Touchpad& touchpad) override {
num_touchpad_settings_updated_++;
}
void OnPointingStickSettingsUpdated(
const mojom::PointingStick& pointing_stick) override {
num_pointing_stick_settings_updated_++;
}
void OnGraphicsTabletConnected(
const mojom::GraphicsTablet& graphics_tablet) override {
num_graphics_tablets_connected_++;
}
void OnGraphicsTabletDisconnected(
const mojom::GraphicsTablet& graphics_tablet) override {
num_graphics_tablets_connected_--;
}
void OnGraphicsTabletSettingsUpdated(
const mojom::GraphicsTablet& graphics_tablet) override {
num_graphics_tablets_settings_updated_++;
}
void OnMouseSettingsUpdated(const mojom::Mouse& mouse) override {
num_mouse_settings_updated_++;
}
void OnCustomizableMouseButtonPressed(const mojom::Mouse& mouse,
const mojom::Button& button) override {
num_mouse_buttons_pressed_++;
}
void OnCustomizableTabletButtonPressed(const mojom::GraphicsTablet& mouse,
const mojom::Button& button) override {
num_tablet_buttons_pressed_++;
}
void OnCustomizablePenButtonPressed(const mojom::GraphicsTablet& mouse,
const mojom::Button& button) override {
num_pen_buttons_pressed_++;
}
void OnCustomizableMouseObservingStarted(const mojom::Mouse& mouse) override {
num_mouse_observing_started_++;
}
void OnCustomizableMouseObservingStopped() override {
num_mouse_observing_stopped_++;
}
uint32_t num_keyboards_connected() { return num_keyboards_connected_; }
uint32_t num_graphics_tablets_connected() {
return num_graphics_tablets_connected_;
}
uint32_t num_keyboards_settings_updated() {
return num_keyboards_settings_updated_;
}
uint32_t num_touchpad_settings_updated() {
return num_touchpad_settings_updated_;
}
uint32_t num_pointing_stick_settings_updated() {
return num_pointing_stick_settings_updated_;
}
uint32_t num_graphics_tablets_settings_updated() {
return num_graphics_tablets_settings_updated_;
}
uint32_t num_mouse_settings_updated() { return num_mouse_settings_updated_; }
uint32_t num_mouse_buttons_pressed() { return num_mouse_buttons_pressed_; }
uint32_t num_pen_buttons_pressed() { return num_pen_buttons_pressed_; }
uint32_t num_tablet_buttons_pressed() { return num_tablet_buttons_pressed_; }
uint32_t num_mouse_observing_started() {
return num_mouse_observing_started_;
}
uint32_t num_mouse_observing_stopped() {
return num_mouse_observing_stopped_;
}
private:
uint32_t num_keyboards_connected_ = 0;
uint32_t num_graphics_tablets_connected_ = 0;
uint32_t num_keyboards_settings_updated_ = 0;
uint32_t num_touchpad_settings_updated_ = 0;
uint32_t num_pointing_stick_settings_updated_;
uint32_t num_graphics_tablets_settings_updated_ = 0;
uint32_t num_mouse_settings_updated_ = 0;
uint32_t num_mouse_buttons_pressed_ = 0;
uint32_t num_tablet_buttons_pressed_ = 0;
uint32_t num_pen_buttons_pressed_ = 0;
uint32_t num_mouse_observing_started_ = 0;
uint32_t num_mouse_observing_stopped_ = 0;
};
class InputDeviceSettingsControllerTest : public NoSessionAshTestBase {
public:
InputDeviceSettingsControllerTest() = default;
InputDeviceSettingsControllerTest(const InputDeviceSettingsControllerTest&) =
delete;
InputDeviceSettingsControllerTest& operator=(
const InputDeviceSettingsControllerTest&) = delete;
~InputDeviceSettingsControllerTest() override = default;
// testing::Test:
void SetUp() override {
mock_adapter_ =
base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
ON_CALL(*mock_adapter_, IsPowered).WillByDefault(testing::Return(true));
ON_CALL(*mock_adapter_, IsPresent).WillByDefault(testing::Return(true));
task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
scoped_feature_list_.InitWithFeatures(
{features::kPeripheralCustomization,
features::kInputDeviceSettingsSplit,
features::kAltClickAndSixPackCustomization,
features::kPeripheralNotification, features::kWelcomeExperience,
::features::kSupportF11AndF12KeyShortcuts},
{});
NoSessionAshTestBase::SetUp();
Shell::Get()->event_rewriter_controller()->Initialize(nullptr, nullptr);
fake_device_manager_ = std::make_unique<FakeDeviceManager>();
// Resetter must be created before the controller is initialized.
scoped_resetter_ = std::make_unique<
InputDeviceSettingsController::ScopedResetterForTest>();
observer_ = std::make_unique<FakeInputDeviceSettingsControllerObserver>();
std::unique_ptr<FakeKeyboardPrefHandler> keyboard_pref_handler =
std::make_unique<FakeKeyboardPrefHandler>();
keyboard_pref_handler_ = keyboard_pref_handler.get();
controller_ = std::make_unique<InputDeviceSettingsControllerImpl>(
local_state(), std::move(keyboard_pref_handler),
std::make_unique<TouchpadPrefHandlerImpl>(),
std::make_unique<MousePrefHandlerImpl>(),
std::make_unique<PointingStickPrefHandlerImpl>(),
std::make_unique<GraphicsTabletPrefHandlerImpl>(), task_runner_);
controller_->AddObserver(observer_.get());
sample_keyboards_ = {kSampleKeyboardUsb, kSampleKeyboardInternal,
kSampleKeyboardBluetooth};
TestSessionControllerClient* session_controller =
GetSessionControllerClient();
session_controller->Reset();
auto user_1_prefs = std::make_unique<TestingPrefServiceSimple>();
RegisterUserProfilePrefs(user_1_prefs->registry(), /*country=*/"",
/*for_test=*/true);
auto user_2_prefs = std::make_unique<TestingPrefServiceSimple>();
RegisterUserProfilePrefs(user_2_prefs->registry(), /*country=*/"",
/*for_test=*/true);
if (should_sign_in_) {
session_controller->AddUserSession(kUserEmail1,
user_manager::UserType::kRegular,
/*provide_pref_service=*/false);
session_controller->SetUserPrefService(account_id_1,
std::move(user_1_prefs));
session_controller->AddUserSession(kUserEmail2,
user_manager::UserType::kRegular,
/*provide_pref_service=*/false);
session_controller->SetUserPrefService(account_id_2,
std::move(user_2_prefs));
session_controller->AddUserSession(kUserEmail3,
user_manager::UserType::kRegular,
/*provide_pref_service=*/false);
session_controller->SwitchActiveUser(account_id_1);
session_controller->SetSessionState(
session_manager::SessionState::ACTIVE);
}
// Reset the `num_keyboard_settings_initialized_` to account for the
// `InitializeKeyboardSettings` call made after test setup where we
// simualate
// a user logging in.
keyboard_pref_handler_->reset_num_keyboard_settings_initialized();
}
void TearDown() override {
observer_.reset();
controller_.reset();
keyboard_pref_handler_ = nullptr;
// Scoped Resetter must be deleted before the test base is teared down.
scoped_resetter_.reset();
NoSessionAshTestBase::TearDown();
task_runner_.reset();
}
void SetActiveUser(const AccountId& account_id) {
TestSessionControllerClient* session_controller =
GetSessionControllerClient();
session_controller->SwitchActiveUser(account_id);
session_controller->SetSessionState(session_manager::SessionState::ACTIVE);
}
std::unique_ptr<device::MockBluetoothDevice> SetupMockBluetoothDevice(
uint32_t vendor_id,
uint32_t product_id,
const char* device_name,
const char* device_address) {
std::unique_ptr<device::MockBluetoothDevice> mock_device =
std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
mock_adapter_.get(), /*bluetooth_class=*/0, device_name,
device_address,
/*initially_paired=*/true, /*connected=*/true);
mock_device->SetBatteryInfo(device::BluetoothDevice::BatteryInfo(
device::BluetoothDevice::BatteryType::kDefault, 66));
ON_CALL(*mock_device, GetDeviceType)
.WillByDefault(testing::Return(device::BluetoothDeviceType::KEYBOARD));
ON_CALL(*mock_device, GetVendorID)
.WillByDefault(testing::Return(vendor_id));
ON_CALL(*mock_device, GetProductID)
.WillByDefault(testing::Return(product_id));
return mock_device;
}
protected:
std::unique_ptr<InputDeviceSettingsControllerImpl> controller_;
std::unique_ptr<FakeDeviceManager> fake_device_manager_;
std::vector<ui::InputDevice> sample_keyboards_;
std::unique_ptr<FakeInputDeviceSettingsControllerObserver> observer_;
base::test::ScopedFeatureList scoped_feature_list_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
std::unique_ptr<InputDeviceSettingsController::ScopedResetterForTest>
scoped_resetter_;
raw_ptr<FakeKeyboardPrefHandler, DanglingUntriaged> keyboard_pref_handler_ =
nullptr;
// Used by other instances of the InputDeviceSettingsControllerTest to control
// whether or not to sign in within the SetUp() function. Configured to sign
// in by default.
bool should_sign_in_ = true;
scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
};
TEST_F(InputDeviceSettingsControllerTest, KeyboardAddingOne) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardAddingMultiple) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kSampleKeyboardUsb, kSampleKeyboardInternal, kSampleKeyboardBluetooth});
EXPECT_EQ(observer_->num_keyboards_connected(), 3u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 3u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardAddingThenRemovingOne) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({});
EXPECT_EQ(observer_->num_keyboards_connected(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardAddingThenRemovingMultiple) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kSampleKeyboardUsb, kSampleKeyboardInternal, kSampleKeyboardBluetooth});
EXPECT_EQ(observer_->num_keyboards_connected(), 3u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 3u);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({});
EXPECT_EQ(observer_->num_keyboards_connected(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 3u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardAddingAndRemoving) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardInternal});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 2u);
}
TEST_F(InputDeviceSettingsControllerTest,
DeletesPrefsWhenInputDeviceSettingsSplitFlagDisabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(features::kInputDeviceSettingsSplit);
std::unique_ptr<TestingPrefServiceSimple> pref_service =
std::make_unique<TestingPrefServiceSimple>();
ash::RegisterUserProfilePrefs(pref_service->registry(), /*country=*/"",
/*for_test=*/true);
base::Value::Dict test_pref_value;
test_pref_value.Set("Fake Key", base::Value::Dict());
pref_service->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
test_pref_value.Clone());
pref_service->SetDict(prefs::kMouseDeviceSettingsDictPref,
test_pref_value.Clone());
pref_service->SetDict(prefs::kPointingStickDeviceSettingsDictPref,
test_pref_value.Clone());
pref_service->SetDict(prefs::kTouchpadDeviceSettingsDictPref,
test_pref_value.Clone());
GetSessionControllerClient()->SetUserPrefService(account_id_3,
std::move(pref_service));
SetActiveUser(account_id_3);
PrefService* active_pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
EXPECT_EQ(base::Value::Dict(), active_pref_service->GetDict(
prefs::kKeyboardDeviceSettingsDictPref));
EXPECT_EQ(base::Value::Dict(),
active_pref_service->GetDict(prefs::kMouseDeviceSettingsDictPref));
EXPECT_EQ(base::Value::Dict(),
active_pref_service->GetDict(
prefs::kPointingStickDeviceSettingsDictPref));
EXPECT_EQ(base::Value::Dict(), active_pref_service->GetDict(
prefs::kTouchpadDeviceSettingsDictPref));
}
TEST_F(InputDeviceSettingsControllerTest,
DeletesPrefsWhenPeripheralCustomizationFlagDisabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(features::kPeripheralCustomization);
std::unique_ptr<TestingPrefServiceSimple> pref_service =
std::make_unique<TestingPrefServiceSimple>();
ash::RegisterUserProfilePrefs(pref_service->registry(), /*country=*/"",
/*for_test=*/true);
base::Value::Dict test_pref_value;
test_pref_value.Set("Fake Key", base::Value::Dict());
pref_service->SetDict(prefs::kGraphicsTabletPenButtonRemappingsDictPref,
test_pref_value.Clone());
pref_service->SetDict(prefs::kGraphicsTabletTabletButtonRemappingsDictPref,
test_pref_value.Clone());
pref_service->SetDict(prefs::kMouseButtonRemappingsDictPref,
test_pref_value.Clone());
GetSessionControllerClient()->SetUserPrefService(account_id_3,
std::move(pref_service));
SetActiveUser(account_id_3);
PrefService* active_pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
EXPECT_EQ(base::Value::Dict(),
active_pref_service->GetDict(
prefs::kGraphicsTabletPenButtonRemappingsDictPref));
EXPECT_EQ(base::Value::Dict(),
active_pref_service->GetDict(
prefs::kGraphicsTabletTabletButtonRemappingsDictPref));
EXPECT_EQ(base::Value::Dict(), active_pref_service->GetDict(
prefs::kMouseButtonRemappingsDictPref));
}
TEST_F(InputDeviceSettingsControllerTest,
DeletesSimulateRightClickPrefsWhenAltFlagDisabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::kAltClickAndSixPackCustomization);
auto user_prefs = std::make_unique<TestingPrefServiceSimple>();
RegisterUserProfilePrefs(user_prefs->registry(), /*country=*/"",
/*for_test=*/true);
base::Value::Dict test_pref_value;
base::Value::Dict six_pack_remappings_dict;
six_pack_remappings_dict.Set(
prefs::kTouchpadSettingSimulateRightClick,
static_cast<int>(ui::mojom::SimulateRightClickModifier::kAlt));
test_pref_value.Set("key", std::move(six_pack_remappings_dict));
user_prefs->SetDict(prefs::kTouchpadDeviceSettingsDictPref,
test_pref_value.Clone());
GetSessionControllerClient()->SetUserPrefService(account_id_3,
std::move(user_prefs));
SetActiveUser(account_id_3);
PrefService* active_pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
base::Value::Dict devices_dict =
active_pref_service->GetDict(prefs::kTouchpadDeviceSettingsDictPref)
.Clone();
base::Value::Dict* existing_settings_dict = devices_dict.FindDict("key");
EXPECT_EQ(base::Value::Dict(), *existing_settings_dict);
}
TEST_F(InputDeviceSettingsControllerTest,
DeletesSixPackKeyPrefsWhenAltFlagDisabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::kAltClickAndSixPackCustomization);
auto user_prefs = std::make_unique<TestingPrefServiceSimple>();
RegisterUserProfilePrefs(user_prefs->registry(), /*country=*/"",
/*for_test=*/true);
base::Value::Dict test_pref_value;
base::Value::Dict six_pack_remappings_dict;
base::Value::Dict settings_dict;
six_pack_remappings_dict.Set(
prefs::kSixPackKeyPageUp,
static_cast<int>(ui::mojom::SimulateRightClickModifier::kAlt));
settings_dict.Set(prefs::kKeyboardSettingSixPackKeyRemappings,
std::move(six_pack_remappings_dict));
test_pref_value.Set("key", std::move(settings_dict));
user_prefs->SetDict(prefs::kKeyboardDeviceSettingsDictPref,
test_pref_value.Clone());
GetSessionControllerClient()->SetUserPrefService(account_id_3,
std::move(user_prefs));
SetActiveUser(account_id_3);
PrefService* active_pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
base::Value::Dict devices_dict =
active_pref_service->GetDict(prefs::kKeyboardDeviceSettingsDictPref)
.Clone();
base::Value::Dict* existing_settings_dict = devices_dict.FindDict("key");
EXPECT_EQ(base::Value::Dict(), *existing_settings_dict);
}
TEST_F(InputDeviceSettingsControllerTest,
InitializeSettingsWhenUserSessionChanges) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
SimulateUserLogin(account_id_2);
task_runner_->RunUntilIdle();
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 2u);
SimulateUserLogin(account_id_1);
task_runner_->RunUntilIdle();
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 3u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsUpdated) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
auto settings = CreateNewKeyboardSettings();
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id,
std::move(settings));
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
}
TEST_F(InputDeviceSettingsControllerTest, PrefsInitializedBasedOnLoginState) {
ClearLogin();
// Reset the `active_pref_service_` to account for the
// `InitializeKeyboardSettings` call made after test setup where we simualate
// a user logging in.
controller_->OnActiveUserPrefServiceChanged(
Shell::Get()->session_controller()->GetPrimaryUserPrefService());
controller_->OnLoginScreenFocusedPodChanged(account_id_1);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(
keyboard_pref_handler_->num_login_screen_keyboard_settings_initialized(),
1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 0u);
controller_->OnLoginScreenFocusedPodChanged(account_id_2);
EXPECT_EQ(
keyboard_pref_handler_->num_login_screen_keyboard_settings_initialized(),
2u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 0u);
SimulateUserLogin(account_id_1);
task_runner_->RunUntilIdle();
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
EXPECT_EQ(
keyboard_pref_handler_->num_login_screen_keyboard_settings_initialized(),
2u);
}
TEST_F(InputDeviceSettingsControllerTest, UpdateLoginScreenSettings) {
controller_->OnLoginScreenFocusedPodChanged(account_id_1);
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id,
CreateNewKeyboardSettings());
task_runner_->RunUntilIdle();
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
// Expect multiple calls to persist settings to the login screen prefs due to:
// updates to the following: active pref service, keyboard settings,
// list of keyboards.
EXPECT_EQ(
keyboard_pref_handler_->num_login_screen_keyboard_settings_updated(), 3u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsAreValid) {
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardInternal,
kKbdTopRowLayout1Tag);
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
const mojom::KeyboardSettingsPtr settings = CreateNewKeyboardSettings();
settings->suppress_meta_fkey_rewrites = !kDefaultSuppressMetaFKeyRewrites;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
// The keyboard does not have an assistant key so therefore, it cannot be
// remapped.
settings->suppress_meta_fkey_rewrites = kDefaultSuppressMetaFKeyRewrites;
settings->modifier_remappings[ui::mojom::ModifierKey::kAssistant] =
ui::mojom::ModifierKey::kAlt;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
const mojom::KeyboardSettingsPtr new_settings = CreateNewKeyboardSettings();
// Function key is not a modifier key so alt key can't be remapped to function
// key.
new_settings->modifier_remappings[ui::mojom::ModifierKey::kAlt] =
ui::mojom::ModifierKey::kFunction;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
new_settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
}
TEST_F(InputDeviceSettingsControllerTest, FkeySettingsAreValid) {
ui::KeyboardCapability::KeyboardInfo keyboard_info;
keyboard_info.device_type =
ui::KeyboardCapability::DeviceType::kDeviceExternalGenericKeyboard;
Shell::Get()->keyboard_capability()->SetKeyboardInfoForTesting(
kSampleKeyboardUsb, std::move(keyboard_info));
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardUsb,
kKbdTopRowLayout1Tag);
const mojom::KeyboardSettingsPtr usb_kb_settings =
mojom::KeyboardSettings::New();
usb_kb_settings->f11 = ui::mojom::ExtendedFkeysModifier::kAlt;
usb_kb_settings->f12 = ui::mojom::ExtendedFkeysModifier::kAlt;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id,
usb_kb_settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
}
TEST_F(InputDeviceSettingsControllerTest,
UpdateSplitModifierKeyboardDefaultSettings) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kModifierSplit);
auto ignore_modifier_split_secret_key =
ash::switches::SetIgnoreModifierSplitSecretKeyForTest();
fake_device_manager_->AddFakeKeyboard(kSampleSplitModifierKeyboard,
kKbdTopRowLayout1Tag);
const mojom::KeyboardSettingsPtr usb_kb_settings =
mojom::KeyboardSettings::New();
usb_kb_settings->modifier_remappings[ui::mojom::ModifierKey::kFunction] =
ui::mojom::ModifierKey::kAlt;
controller_->SetKeyboardSettings((DeviceId)kSampleSplitModifierKeyboard.id,
usb_kb_settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
fake_device_manager_->RemoveAllDevices();
}
TEST_F(InputDeviceSettingsControllerTest, GraphicsTabletSettingsAreValid) {
fake_device_manager_->AddFakeGraphicsTablet(kSampleGraphicsTablet);
EXPECT_EQ(observer_->num_graphics_tablets_connected(), 1u);
mojom::ButtonPtr button =
mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kBack);
controller_->OnGraphicsTabletButtonPressed(kSampleGraphicsTablet.id, *button);
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 1u);
// Create a valid settings.
mojom::ButtonRemapping button_remapping1(
/*name=*/"test",
/*button=*/
button->Clone(),
/*remapping_action=*/
mojom::RemappingAction::NewAcceleratorAction(
ash::AcceleratorAction::kBrightnessDown));
std::vector<mojom::ButtonRemappingPtr> tablet_button_remappings;
std::vector<mojom::ButtonRemappingPtr> pen_button_remappings;
pen_button_remappings.push_back(button_remapping1.Clone());
mojom::GraphicsTabletSettingsPtr settings =
mojom::GraphicsTabletSettings::New(mojo::Clone(tablet_button_remappings),
mojo::Clone(pen_button_remappings));
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
settings.Clone());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 2u);
// Invalid empty settings.
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
mojom::GraphicsTabletSettings::New());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 2u);
// Invalid settings with a button remapping name more than 32 characters.
settings->pen_button_remappings[0]->name =
"This is a really long name to test the name max length";
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
settings.Clone());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 2u);
// Valid settings with a button remapping name fewer than 32 characters.
settings->pen_button_remappings[0]->name = "Short valid name";
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
settings.Clone());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 3u);
// Valid settings with a button remapping name with exactly 32 characters.
settings->pen_button_remappings[0]->name = std::string(32, 'a');
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
settings.Clone());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 4u);
// Valid settings with a button remapping name with exactly 33 characters.
settings->pen_button_remappings[0]->name = std::string(33, 'a');
controller_->SetGraphicsTabletSettings(kSampleGraphicsTablet.id,
settings.Clone());
EXPECT_EQ(observer_->num_graphics_tablets_settings_updated(), 4u);
}
TEST_F(InputDeviceSettingsControllerTest,
RecordSetKeyboardSettingsValidMetric) {
base::HistogramTester histogram_tester;
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardInternal,
kKbdTopRowLayout1Tag);
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
CreateNewKeyboardSettings());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Keyboard.SetSettingsSucceeded", true,
/*expected_count=*/1u);
// Set keyboard with invalid settings.
const mojom::KeyboardSettingsPtr settings = CreateNewKeyboardSettings();
settings->suppress_meta_fkey_rewrites = !kDefaultSuppressMetaFKeyRewrites;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Keyboard.SetSettingsSucceeded", false,
/*expected_count=*/1u);
}
TEST_F(InputDeviceSettingsControllerTest,
RecordSetTouchpadSettingsValidMetric) {
base::HistogramTester histogram_tester;
ui::DeviceDataManagerTestApi().SetTouchpadDevices({kSampleTouchpadInternal});
// Set non-haptic touchpad with valid settings.
auto settings = mojom::TouchpadSettings::New();
settings->haptic_enabled = kDefaultHapticFeedbackEnabled;
settings->haptic_sensitivity = kDefaultHapticSensitivity;
controller_->SetTouchpadSettings((DeviceId)kSampleTouchpadInternal.id,
settings->Clone());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Touchpad.SetSettingsSucceeded", true,
/*expected_count=*/1u);
// Set non-haptic touchpad with invalid id.
controller_->SetTouchpadSettings(/*id=*/4, mojom::TouchpadSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Touchpad.SetSettingsSucceeded", false,
/*expected_count=*/1u);
// Set non-haptic touchpad with invalid settings.
controller_->SetTouchpadSettings((DeviceId)kSampleTouchpadInternal.id,
mojom::TouchpadSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Touchpad.SetSettingsSucceeded", false,
/*expected_count=*/2u);
// Set haptic touchpad with valid settings.
ui::DeviceDataManagerTestApi().SetTouchpadDevices(
{kSampleHapticTouchpadInternal});
controller_->SetTouchpadSettings((DeviceId)kSampleHapticTouchpadInternal.id,
mojom::TouchpadSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Touchpad.SetSettingsSucceeded", true,
/*expected_count=*/2u);
}
TEST_F(InputDeviceSettingsControllerTest,
RecordSetPointingStickSettingsValidMetric) {
base::HistogramTester histogram_tester;
ui::DeviceDataManagerTestApi().SetPointingStickDevices(
{kSamplePointingStickInternal});
controller_->SetPointingStickSettings(
(DeviceId)kSamplePointingStickInternal.id,
mojom::PointingStickSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.PointingStick.SetSettingsSucceeded", true,
/*expected_count=*/1u);
// Set pointing stick with invalid id.
controller_->SetPointingStickSettings(/*id=*/4,
mojom::PointingStickSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.PointingStick.SetSettingsSucceeded", false,
/*expected_count=*/1u);
}
TEST_F(InputDeviceSettingsControllerTest, RecordSetMouseSettingsValidMetric) {
base::HistogramTester histogram_tester;
ui::DeviceDataManagerTestApi().SetMouseDevices({kSampleMouseUsb});
controller_->SetMouseSettings((DeviceId)kSampleMouseUsb.id,
mojom::MouseSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Mouse.SetSettingsSucceeded", true,
/*expected_count=*/1u);
// Set mouse with invalid id.
controller_->SetMouseSettings(/*id=*/4, mojom::MouseSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Mouse.SetSettingsSucceeded", false,
/*expected_count=*/1u);
// Set mouse with valid id and valid settings.
controller_->SetMouseSettings(/*id=*/3, mojom::MouseSettings::New());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Mouse.SetSettingsSucceeded", true,
/*expected_count=*/2u);
// Create invalid settings.
mojom::ButtonRemapping button_remapping(
/*name=*/"test",
/*button=*/
mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kBack),
/*remapping_action=*/
mojom::RemappingAction::NewAcceleratorAction(
ash::AcceleratorAction::kBrightnessDown));
std::vector<mojom::ButtonRemappingPtr> button_remappings;
button_remappings.push_back(button_remapping.Clone());
mojom::MouseSettingsPtr settings = mojom::MouseSettings::New(
/*swap_right=*/kDefaultSwapRight, /*sensitivity=*/kDefaultSensitivity,
/*reverse_scrolling=*/kDefaultReverseScrolling,
/*acceleration_enabled=*/kDefaultAccelerationEnabled,
/*scroll_sensitivity=*/kDefaultSensitivity,
/*scroll_acceleration=*/kDefaultScrollAccelerationEnabled,
/*button_remappings=*/mojo::Clone(button_remappings));
// Set mouse with valid id and invalid settings.
controller_->SetMouseSettings(
/*id=*/3, settings->Clone());
histogram_tester.ExpectBucketCount(
"ChromeOS.Settings.Device.Mouse.SetSettingsSucceeded", false,
/*expected_count=*/2u);
}
// Tests that given an invalid id, keyboard settings are not updated and
// observers are not notified.
TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsUpdatedInvalidId) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardUsb});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id + 1,
CreateNewKeyboardSettings());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
}
TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsUpdateMultiple) {
// The SetKeyboardSettings call should update both keyboards since they have
// the same |device_key|.
ui::KeyboardDevice sample_usb_keyboard_copy = kSampleKeyboardUsb;
sample_usb_keyboard_copy.id = kSampleKeyboardUsb2.id;
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kSampleKeyboardUsb, sample_usb_keyboard_copy});
EXPECT_EQ(observer_->num_keyboards_connected(), 2u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 2u);
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id,
CreateNewKeyboardSettings());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 2u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
}
TEST_F(InputDeviceSettingsControllerTest, RecordsMetricsSettings) {
base::HistogramTester histogram_tester;
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardUsb,
kKbdTopRowLayout1Tag);
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardUsb2,
kKbdTopRowLayout1Tag);
// Initially expect no user preferences recorded.
// Two input device settings controllers publish this metric at the same time,
// so we expect 2 times the number of devices for the initial count.
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Keyboard.ExternalChromeOS.TopRowAreFKeys."
"Initial",
/*expected_count=*/4u);
SimulateUserLogin(account_id_2);
task_runner_->RunUntilIdle();
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Keyboard.ExternalChromeOS.TopRowAreFKeys."
"Initial",
/*expected_count=*/6u);
// Test Metrics Updates when setKeyboardSettings is called.
auto updated_settings = CreateNewKeyboardSettings();
updated_settings.get()->top_row_are_fkeys = true;
controller_->SetKeyboardSettings(kSampleKeyboardUsb.id,
std::move(updated_settings));
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Keyboard.ExternalChromeOS.TopRowAreFKeys."
"Changed",
/*expected_count=*/1u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Keyboard.ExternalChromeOS."
"BlockMetaFKeyRewrites.Changed",
/*expected_count=*/0);
// Test Metrics Updates when setTouchpadSettings is called.
controller_->OnTouchpadListUpdated({kSampleTouchpadInternal}, {});
auto settings = mojom::TouchpadSettings::New();
settings->haptic_enabled = kDefaultHapticFeedbackEnabled;
settings->haptic_sensitivity = kDefaultHapticSensitivity;
controller_->SetTouchpadSettings(kSampleTouchpadInternal.id,
settings.Clone());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Touchpad.Internal.AccelerationEnabled.Changed",
/*expected_count=*/1u);
}
TEST_F(InputDeviceSettingsControllerTest, RecordsMetadataMetrics) {
base::HistogramTester histogram_tester;
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kSampleKeyboardInternal, kSampleKeychronKeyboard});
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
{kSampleGraphicsTablet, kSampleUnCustomizableGraphicsTablet,
kSamplekWacomOnePenTabletS});
ui::DeviceDataManagerTestApi().SetMouseDevices(
{kSampleMouseUsb, kSampleCustomizableMouse, kSampleUncustomizableMouse});
// Two input device settings controllers publish this metric at the same time,
// so we expect 2 times the number of metadata tiers for all devices.
histogram_tester.ExpectBucketCount(
"ChromeOS.Inputs.GraphicsTablet.MetadataTier", MetadataTier::kNoMetadata,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount(
"ChromeOS.Inputs.GraphicsTablet.MetadataTier", MetadataTier::kNoMetadata,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount(
"ChromeOS.Inputs.GraphicsTablet.MetadataTier",
MetadataTier::kHasButtonConfig,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount("ChromeOS.Inputs.Mouse.MetadataTier",
MetadataTier::kNoMetadata,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount("ChromeOS.Inputs.Mouse.MetadataTier",
MetadataTier::kNoMetadata,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount("ChromeOS.Inputs.Mouse.MetadataTier",
MetadataTier::kHasButtonConfig,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount("ChromeOS.Inputs.Keyboard.MetadataTier",
MetadataTier::kNoMetadata,
/*expected_count=*/2u);
histogram_tester.ExpectBucketCount("ChromeOS.Inputs.Keyboard.MetadataTier",
MetadataTier::kNoMetadata,
/*expected_count=*/2u);
}
TEST_F(InputDeviceSettingsControllerTest, GetGeneralizedTopRowAreFKeys) {
// If there no keyboards, return false.
EXPECT_EQ(false, controller_->GetGeneralizedTopRowAreFKeys());
// If there is only internal keyboard, return its top_row_are_fkeys value.
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardInternal});
auto internal_keyboard_settings = CreateNewKeyboardSettings();
;
internal_keyboard_settings->top_row_are_fkeys = true;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
internal_keyboard_settings.Clone());
EXPECT_EQ(true, controller_->GetGeneralizedTopRowAreFKeys());
// If there are multiple external keyboards, return the top_row_are_fkeys
// value of the external keyboard which has the largest device id.
ui::DeviceDataManagerTestApi().SetKeyboardDevices(
{kSampleKeyboardInternal, kSampleKeyboardUsb, kSampleKeyboardUsb2});
auto settings = CreateNewKeyboardSettings();
;
settings->top_row_are_fkeys = true;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id,
settings.Clone());
EXPECT_EQ(false, controller_->GetGeneralizedTopRowAreFKeys());
}
TEST_F(InputDeviceSettingsControllerTest,
KeyboardSettingsAreValidWithEnterprisePolicy) {
// Test when top_row_are_fkeys_policy doesn't exist.
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardInternal});
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
const mojom::KeyboardSettingsPtr settings = CreateNewKeyboardSettings();
settings->top_row_are_fkeys = !kDefaultTopRowAreFKeys;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
// Test when policy status is kRecommended.
std::unique_ptr<TestingPrefServiceSimple> pref_service =
std::make_unique<TestingPrefServiceSimple>();
ash::RegisterUserProfilePrefs(pref_service->registry(), /*country=*/"",
/*for_test=*/true);
controller_->OnActiveUserPrefServiceChanged(pref_service.get());
pref_service->SetRecommendedPref(prefs::kSendFunctionKeys,
base::Value(kDefaultTopRowAreFKeys));
settings->top_row_are_fkeys = !kDefaultTopRowAreFKeys;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 2u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 2u);
// Test when policy status is kManaged and the settings are valid.
pref_service->SetManagedPref(prefs::kSendFunctionKeys,
base::Value(kDefaultTopRowAreFKeys));
settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 3u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 3u);
// Test when policy status is kManaged and the settings are invalid.
pref_service->SetManagedPref(prefs::kSendFunctionKeys,
base::Value(!kDefaultTopRowAreFKeys));
settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings.Clone());
EXPECT_EQ(observer_->num_keyboards_settings_updated(), 3u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 3u);
controller_->OnActiveUserPrefServiceChanged(
Shell::Get()->session_controller()->GetActivePrefService());
}
TEST_F(InputDeviceSettingsControllerTest, RestoreDefaultKeyboardRemappings) {
base::HistogramTester histogram_tester;
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardInternal});
const mojom::KeyboardSettingsPtr settings = CreateNewKeyboardSettings();
settings->top_row_are_fkeys = kDefaultTopRowAreFKeys;
settings->modifier_remappings[ui::mojom::ModifierKey::kMeta] =
ui::mojom::ModifierKey::kAlt;
controller_->SetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id,
settings->Clone());
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
EXPECT_EQ(
controller_->GetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id)
->modifier_remappings.size(),
1u);
controller_->RestoreDefaultKeyboardRemappings(
(DeviceId)kSampleKeyboardInternal.id);
EXPECT_EQ(
controller_->GetKeyboardSettings((DeviceId)kSampleKeyboardInternal.id)
->modifier_remappings.size(),
0u);
histogram_tester.ExpectUniqueSample(
"ChromeOS.Settings.Device.Keyboard.Internal.Modifiers.NumberOfKeysReset",
/*sample=*/1u, /*expected_bucket_count=*/1u);
}
TEST_F(InputDeviceSettingsControllerTest, MouseButtonPressed) {
ui::DeviceDataManagerTestApi().SetMouseDevices({kSampleMouseUsb});
base::HistogramTester histogram_tester;
controller_->OnMouseButtonPressed(kSampleMouseUsb.id,
*mojom::Button::NewCustomizableButton(
mojom::CustomizableButton::kMiddle));
EXPECT_EQ(1u, observer_->num_mouse_settings_updated());
EXPECT_EQ(1u, observer_->num_mouse_buttons_pressed());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Mouse.ButtonRemapping.Registered."
"CustomizableButton",
/*expected_count=*/1u);
controller_->OnMouseButtonPressed(
kSampleMouseUsb.id,
*mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kSide));
EXPECT_EQ(2u, observer_->num_mouse_settings_updated());
EXPECT_EQ(2u, observer_->num_mouse_buttons_pressed());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Mouse.ButtonRemapping.Registered."
"CustomizableButton",
/*expected_count=*/2u);
controller_->OnMouseButtonPressed(
kSampleMouseUsb.id,
*mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kExtra));
EXPECT_EQ(3u, observer_->num_mouse_settings_updated());
EXPECT_EQ(3u, observer_->num_mouse_buttons_pressed());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Mouse.ButtonRemapping.Registered."
"CustomizableButton",
/*expected_count=*/3u);
auto* settings = controller_->GetMouseSettings(kSampleMouseUsb.id);
ASSERT_TRUE(settings);
ASSERT_EQ(3u, settings->button_remappings.size());
EXPECT_EQ(
*mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kMiddle),
*settings->button_remappings[0]->button);
EXPECT_EQ("Middle Button", settings->button_remappings[0]->name);
EXPECT_EQ(nullptr, settings->button_remappings[0]->remapping_action.get());
EXPECT_EQ(
*mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kSide),
*settings->button_remappings[1]->button);
EXPECT_EQ("Other Button 1", settings->button_remappings[1]->name);
EXPECT_EQ(nullptr, settings->button_remappings[1]->remapping_action.get());
EXPECT_EQ(
*mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kExtra),
*settings->button_remappings[2]->button);
EXPECT_EQ("Other Button 2", settings->button_remappings[2]->name);
EXPECT_EQ(nullptr, settings->button_remappings[2]->remapping_action.get());
// Press a same button again, the button register metrics should not emit.
controller_->OnMouseButtonPressed(kSampleMouseUsb.id,
*mojom::Button::NewCustomizableButton(
mojom::CustomizableButton::kMiddle));
EXPECT_EQ(3u, observer_->num_mouse_settings_updated());
EXPECT_EQ(4u, observer_->num_mouse_buttons_pressed());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.Mouse.ButtonRemapping.Registered."
"CustomizableButton",
/*expected_count=*/3u);
}
TEST_F(InputDeviceSettingsControllerTest, GraphicsTabletButtonPressed) {
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
{kSampleGraphicsTablet});
base::HistogramTester histogram_tester;
mojom::ButtonPtr pen_button =
mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kForward);
mojom::ButtonPtr pen_middle_button =
mojom::Button::NewCustomizableButton(mojom::CustomizableButton::kMiddle);
mojom::ButtonPtr tablet_button = mojom::Button::NewVkey(ui::VKEY_A);
controller_->OnGraphicsTabletButtonPressed(kSampleGraphicsTablet.id,
*pen_button);
controller_->OnGraphicsTabletButtonPressed(kSampleGraphicsTablet.id,
*pen_middle_button);
controller_->OnGraphicsTabletButtonPressed(kSampleGraphicsTablet.id,
*tablet_button);
EXPECT_EQ(3u, observer_->num_graphics_tablets_settings_updated());
EXPECT_EQ(1u, observer_->num_tablet_buttons_pressed());
EXPECT_EQ(2u, observer_->num_pen_buttons_pressed());
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.GraphicsTablet.ButtonRemapping.Registered."
"Vkey",
/*expected_count=*/1u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.GraphicsTabletPen.ButtonRemapping.Registered."
"CustomizableButton",
/*expected_count=*/2u);
auto* settings =
controller_->GetGraphicsTabletSettings(kSampleGraphicsTablet.id);
ASSERT_TRUE(settings);
ASSERT_EQ(2u, settings->pen_button_remappings.size());
EXPECT_EQ(*pen_button, *settings->pen_button_remappings[0]->button);
EXPECT_EQ("Other Button 1", settings->pen_button_remappings[0]->name);
EXPECT_EQ(nullptr,
settings->pen_button_remappings[0]->remapping_action.get());
EXPECT_EQ(*pen_middle_button, *settings->pen_button_remappings[1]->button);
EXPECT_EQ("Other Button 2", settings->pen_button_remappings[1]->name);
EXPECT_EQ(nullptr,
settings->pen_button_remappings[1]->remapping_action.get());
ASSERT_EQ(1u, settings->tablet_button_remappings.size());
EXPECT_EQ(*tablet_button, *settings->tablet_button_remappings[0]->button);
EXPECT_EQ("Other Button 1", settings->tablet_button_remappings[0]->name);
EXPECT_EQ(nullptr,
settings->tablet_button_remappings[0]->remapping_action.get());
}
TEST_F(InputDeviceSettingsControllerTest, ObservingButtons) {
ui::DeviceDataManagerTestApi().SetMouseDevices({kSampleMouseUsb});
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
{kSampleGraphicsTablet});
auto* rewriter = Shell::Get()
->event_rewriter_controller()
->peripheral_customization_event_rewriter();
controller_->StartObservingButtons(kSampleMouseUsb.id);
ASSERT_EQ(1u, rewriter->mice_to_observe().size());
EXPECT_TRUE(rewriter->mice_to_observe().contains(kSampleMouseUsb.id));
EXPECT_EQ(1u, observer_->num_mouse_observing_started());
controller_->StopObservingButtons();
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
EXPECT_EQ(1u, observer_->num_mouse_observing_stopped());
controller_->StartObservingButtons(kSampleGraphicsTablet.id);
ASSERT_EQ(1u, rewriter->graphics_tablets_to_observe().size());
EXPECT_TRUE(rewriter->graphics_tablets_to_observe().contains(
kSampleGraphicsTablet.id));
controller_->StopObservingButtons();
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
controller_->StartObservingButtons(kSampleTouchpadInternal.id);
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
}
TEST_F(InputDeviceSettingsControllerTest, ObservingMouseButtons) {
ui::DeviceDataManagerTestApi().SetMouseDevices({kSampleUncustomizableMouse,
kSampleCustomizableMouse,
kSampleKeyboardMouseCombo});
auto* rewriter = Shell::Get()
->event_rewriter_controller()
->peripheral_customization_event_rewriter();
controller_->StartObservingButtons(kSampleUncustomizableMouse.id);
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
EXPECT_FALSE(
rewriter->mice_to_observe().contains(kSampleUncustomizableMouse.id));
controller_->StartObservingButtons(kSampleCustomizableMouse.id);
ASSERT_EQ(1u, rewriter->mice_to_observe().size());
EXPECT_TRUE(
rewriter->mice_to_observe().contains(kSampleCustomizableMouse.id));
EXPECT_EQ(
rewriter->mice_to_observe().find(kSampleCustomizableMouse.id)->second,
mojom::CustomizationRestriction::kAllowCustomizations);
controller_->StartObservingButtons(kSampleKeyboardMouseCombo.id);
ASSERT_EQ(2u, rewriter->mice_to_observe().size());
EXPECT_TRUE(
rewriter->mice_to_observe().contains(kSampleCustomizableMouse.id));
EXPECT_EQ(
rewriter->mice_to_observe().find(kSampleKeyboardMouseCombo.id)->second,
mojom::CustomizationRestriction::kDisableKeyEventRewrites);
}
TEST_F(InputDeviceSettingsControllerTest, ObservingButtonsDuplicateIds) {
auto mouse1 = kSampleMouseUsb;
auto mouse2 = kSampleMouseUsb;
mouse2.id = mouse2.id + 100;
auto graphics_tablet1 = kSampleGraphicsTablet;
auto graphics_tablet2 = kSampleGraphicsTablet;
graphics_tablet2.id = graphics_tablet2.id + 100;
ui::DeviceDataManagerTestApi().SetMouseDevices({mouse1});
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices({graphics_tablet1});
ui::DeviceDataManagerTestApi().SetUncategorizedDevices(
{mouse2, graphics_tablet2});
auto* rewriter = Shell::Get()
->event_rewriter_controller()
->peripheral_customization_event_rewriter();
controller_->StartObservingButtons(mouse1.id);
ASSERT_EQ(2u, rewriter->mice_to_observe().size());
EXPECT_TRUE(rewriter->mice_to_observe().contains(mouse1.id));
EXPECT_TRUE(rewriter->mice_to_observe().contains(mouse2.id));
controller_->StopObservingButtons();
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
controller_->StartObservingButtons(kSampleGraphicsTablet.id);
ASSERT_EQ(2u, rewriter->graphics_tablets_to_observe().size());
EXPECT_TRUE(
rewriter->graphics_tablets_to_observe().contains(graphics_tablet1.id));
EXPECT_TRUE(
rewriter->graphics_tablets_to_observe().contains(graphics_tablet2.id));
controller_->StopObservingButtons();
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
controller_->StartObservingButtons(kSampleTouchpadInternal.id);
ASSERT_EQ(0u, rewriter->mice_to_observe().size());
ASSERT_EQ(0u, rewriter->graphics_tablets_to_observe().size());
}
TEST_F(InputDeviceSettingsControllerTest, GetSettingsDuplicateIds) {
auto mouse1 = kSampleMouseUsb;
auto mouse2 = kSampleMouseUsb;
mouse2.id = mouse2.id + 100;
auto graphics_tablet1 = kSampleGraphicsTablet;
auto graphics_tablet2 = kSampleGraphicsTablet;
graphics_tablet2.id = graphics_tablet2.id + 100;
ui::DeviceDataManagerTestApi().SetMouseDevices({mouse1});
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices({graphics_tablet1});
ui::DeviceDataManagerTestApi().SetUncategorizedDevices(
{mouse2, graphics_tablet2});
EXPECT_EQ(controller_->GetMouseSettings(mouse1.id),
controller_->GetMouseSettings(mouse2.id));
EXPECT_EQ(controller_->GetGraphicsTabletSettings(graphics_tablet1.id),
controller_->GetGraphicsTabletSettings(graphics_tablet2.id));
}
TEST_F(InputDeviceSettingsControllerTest,
KeyboardReceivesCorrectTopRowActionKeys) {
// `kKbdTopRowLayout1Tag` maps to the original Chrome OS Layout:
// Browser Back, Browser Forward, Refresh, Full Screen, Overview,
// Brightness Down, Brightness Up, Mute, Volume Down, Volume Up.
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardInternal,
kKbdTopRowLayout1Tag);
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
auto keyboards = controller_->GetConnectedKeyboards();
EXPECT_EQ(keyboards.size(), 1u);
auto keyboard = mojo::Clone(keyboards[0]);
EXPECT_EQ(keyboard->top_row_action_keys.size(), 10u);
EXPECT_EQ(keyboard->top_row_action_keys[0], mojom::TopRowActionKey::kBack);
EXPECT_EQ(keyboard->top_row_action_keys[1], mojom::TopRowActionKey::kForward);
EXPECT_EQ(keyboard->top_row_action_keys[2], mojom::TopRowActionKey::kRefresh);
EXPECT_EQ(keyboard->top_row_action_keys[3],
mojom::TopRowActionKey::kFullscreen);
EXPECT_EQ(keyboard->top_row_action_keys[4],
mojom::TopRowActionKey::kOverview);
EXPECT_EQ(keyboard->top_row_action_keys[5],
mojom::TopRowActionKey::kScreenBrightnessDown);
EXPECT_EQ(keyboard->top_row_action_keys[6],
mojom::TopRowActionKey::kScreenBrightnessUp);
EXPECT_EQ(keyboard->top_row_action_keys[7],
mojom::TopRowActionKey::kVolumeMute);
EXPECT_EQ(keyboard->top_row_action_keys[8],
mojom::TopRowActionKey::kVolumeDown);
EXPECT_EQ(keyboard->top_row_action_keys[9],
mojom::TopRowActionKey::kVolumeUp);
fake_device_manager_->RemoveAllDevices();
// `kKbdTopRowLayout2Tag` represents the 2017 keyboard layout:
// Browser Forward is gone and Play/Pause key is added between
// Brightness Up and Mute.
fake_device_manager_->AddFakeKeyboard(kSampleKeyboardInternal2,
kKbdTopRowLayout2Tag);
EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
keyboards = controller_->GetConnectedKeyboards();
EXPECT_EQ(keyboards.size(), 1u);
keyboard = mojo::Clone(keyboards[0]);
EXPECT_EQ(keyboard->top_row_action_keys.size(), 10u);
EXPECT_EQ(keyboard->top_row_action_keys[0], mojom::TopRowActionKey::kBack);
EXPECT_EQ(keyboard->top_row_action_keys[1], mojom::TopRowActionKey::kRefresh);
EXPECT_EQ(keyboard->top_row_action_keys[2],
mojom::TopRowActionKey::kFullscreen);
EXPECT_EQ(keyboard->top_row_action_keys[3],
mojom::TopRowActionKey::kOverview);
EXPECT_EQ(keyboard->top_row_action_keys[4],
mojom::TopRowActionKey::kScreenBrightnessDown);
EXPECT_EQ(keyboard->top_row_action_keys[5],
mojom::TopRowActionKey::kScreenBrightnessUp);
EXPECT_EQ(keyboard->top_row_action_keys[6],
mojom::TopRowActionKey::kPlayPause);
EXPECT_EQ(keyboard->top_row_action_keys[7],
mojom::TopRowActionKey::kVolumeMute);
EXPECT_EQ(keyboard->top_row_action_keys[8],
mojom::TopRowActionKey::kVolumeDown);
EXPECT_EQ(keyboard->top_row_action_keys[9],
mojom::TopRowActionKey::kVolumeUp);
}
TEST_F(InputDeviceSettingsControllerTest, InternalTouchpadUpdatedWithPrefs) {
ui::DeviceDataManagerTestApi().SetTouchpadDevices(
{kSampleTouchpadInternal, kSampleTouchpadExternal});
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
base::Value::Dict updated_dict;
updated_dict.Set("test_key", 1);
pref_service->SetDict(prefs::kTouchpadInternalSettings, updated_dict.Clone());
EXPECT_EQ(2u, observer_->num_touchpad_settings_updated());
// If there is no internal touchpad, expect no settings updated call.
ui::DeviceDataManagerTestApi().SetTouchpadDevices({kSampleTouchpadExternal});
updated_dict.Set("test_key", 2);
pref_service->SetDict(prefs::kTouchpadInternalSettings, updated_dict.Clone());
EXPECT_EQ(2u, observer_->num_touchpad_settings_updated());
ui::DeviceDataManagerTestApi().SetTouchpadDevices({kSampleTouchpadInternal});
updated_dict.Set("test_key", 3);
pref_service->SetDict(prefs::kTouchpadInternalSettings, updated_dict.Clone());
EXPECT_EQ(4u, observer_->num_touchpad_settings_updated());
}
TEST_F(InputDeviceSettingsControllerTest,
InternalPointingStickUpdatedWithPrefs) {
ui::DeviceDataManagerTestApi().SetPointingStickDevices(
{kSamplePointingStickInternal, kSamplePointingStickExternal});
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
base::Value::Dict updated_dict;
updated_dict.Set("test_key", 1);
pref_service->SetDict(prefs::kPointingStickInternalSettings,
updated_dict.Clone());
EXPECT_EQ(1u, observer_->num_pointing_stick_settings_updated());
// If there is no internal touchpad, expect no settings updated call.
ui::DeviceDataManagerTestApi().SetPointingStickDevices(
{kSamplePointingStickExternal});
updated_dict.Set("test_key", 2);
pref_service->SetDict(prefs::kPointingStickInternalSettings,
updated_dict.Clone());
EXPECT_EQ(1u, observer_->num_pointing_stick_settings_updated());
ui::DeviceDataManagerTestApi().SetPointingStickDevices(
{kSamplePointingStickInternal});
updated_dict.Set("test_key", 3);
pref_service->SetDict(prefs::kPointingStickInternalSettings,
updated_dict.Clone());
EXPECT_EQ(2u, observer_->num_pointing_stick_settings_updated());
}
class InputDeviceSettingsControllerNoSignInTest
: public InputDeviceSettingsControllerTest {
public:
InputDeviceSettingsControllerNoSignInTest() { should_sign_in_ = false; }
};
TEST_F(InputDeviceSettingsControllerNoSignInTest,
NoCrashOnPodSelectionRaceConditionMouse) {
ui::DeviceDataManagerTestApi().SetMouseDevices({kSampleMouseUsb});
auto* mouse = controller_->GetMouse(kSampleMouseUsb.id);
ASSERT_TRUE(mouse);
ASSERT_TRUE(mouse->settings);
}
TEST_F(InputDeviceSettingsControllerNoSignInTest,
NoCrashOnPodSelectionRaceConditionKeyboard) {
ui::DeviceDataManagerTestApi().SetKeyboardDevices({kSampleKeyboardInternal});
auto* keyboard = controller_->GetKeyboard(kSampleKeyboardInternal.id);
ASSERT_TRUE(keyboard);
ASSERT_TRUE(keyboard->settings);
}
TEST_F(InputDeviceSettingsControllerNoSignInTest,
NoCrashOnPodSelectionRaceConditionTouchpad) {
ui::DeviceDataManagerTestApi().SetTouchpadDevices({kSampleTouchpadInternal});
auto* touchpad = controller_->GetTouchpad(kSampleTouchpadInternal.id);
ASSERT_TRUE(touchpad);
ASSERT_TRUE(touchpad->settings);
}
TEST_F(InputDeviceSettingsControllerNoSignInTest,
NoCrashOnPodSelectionRaceConditionPointingStick) {
ui::DeviceDataManagerTestApi().SetPointingStickDevices(
{kSamplePointingStickExternal});
auto* pointing_stick =
controller_->GetPointingStick(kSamplePointingStickExternal.id);
ASSERT_TRUE(pointing_stick);
ASSERT_TRUE(pointing_stick->settings);
}
TEST_F(InputDeviceSettingsControllerNoSignInTest,
NoCrashOnPodSelectionRaceConditionGraphicsTablet) {
ui::DeviceDataManagerTestApi().SetGraphicsTabletDevices(
{kSampleGraphicsTablet});
auto* graphics_tablet =
controller_->GetGraphicsTablet(kSampleGraphicsTablet.id);
ASSERT_TRUE(graphics_tablet);
ASSERT_TRUE(graphics_tablet->settings);
}
TEST_F(InputDeviceSettingsControllerTest, BatteryInfoAddedForBluetoothDevices) {
uint32_t test_vendor_id = 0x1111;
uint32_t test_product_id = 0x1112;
auto mock_device = SetupMockBluetoothDevice(test_vendor_id, test_product_id,
kBluetoothDeviceName,
kBluetoothDevicePublicAddress);
mock_device->SetBatteryInfo(device::BluetoothDevice::BatteryInfo(
device::BluetoothDevice::BatteryType::kDefault, 66,
device::BluetoothDevice::BatteryInfo::ChargeState::kDischarging));
std::vector<raw_ptr<const device::BluetoothDevice, VectorExperimental>>
devices;
devices.push_back(mock_device.get());
ON_CALL(*mock_adapter_, GetDevices).WillByDefault(testing::Return(devices));
ui::KeyboardDevice bluetooth_keyboard = kSampleKeyboardBluetooth;
bluetooth_keyboard.product_id = test_product_id;
bluetooth_keyboard.vendor_id = test_vendor_id;
ui::DeviceDataManagerTestApi().SetKeyboardDevices({bluetooth_keyboard});
auto* keyboard = controller_->GetKeyboard(bluetooth_keyboard.id);
ASSERT_TRUE(keyboard);
ASSERT_TRUE(keyboard->battery_info);
ASSERT_EQ(mojom::ChargeState::kDischarging,
keyboard->battery_info->charge_state);
ASSERT_EQ(66, keyboard->battery_info->battery_percentage);
}
TEST_F(InputDeviceSettingsControllerTest, BatteryInfoUpdates) {
// Create mock BT device for testing.
uint32_t test_vendor_id = 0x1111;
uint32_t test_product_id = 0x1112;
auto mock_device = SetupMockBluetoothDevice(test_vendor_id, test_product_id,
kBluetoothDeviceName,
kBluetoothDevicePublicAddress);
std::vector<raw_ptr<const device::BluetoothDevice, VectorExperimental>>
devices;
devices.push_back(mock_device.get());
ON_CALL(*mock_adapter_, GetDevices).WillByDefault(testing::Return(devices));
// Set initial battery info for BT device.
mock_device->SetBatteryInfo(device::BluetoothDevice::BatteryInfo(
device::BluetoothDevice::BatteryType::kDefault, 66));
ui::KeyboardDevice bluetooth_keyboard = kSampleKeyboardBluetooth;
bluetooth_keyboard.product_id = test_product_id;
bluetooth_keyboard.vendor_id = test_vendor_id;
ui::DeviceDataManagerTestApi().SetKeyboardDevices({bluetooth_keyboard});
// Keyboard populated with initial battery info.
auto* keyboard = controller_->GetKeyboard(bluetooth_keyboard.id);
ASSERT_EQ(66, keyboard->battery_info->battery_percentage);
auto bt_address_map =
controller_->GetBluetoothAddressToDeviceIdMapForTesting();
// BT address map should contain an entry for the connected keyboard.
ASSERT_EQ(1u, bt_address_map.size());
// Battery percentage change should trigger a call to `DeviceBatteryChanged`.
mock_device->SetBatteryInfo(device::BluetoothDevice::BatteryInfo(
device::BluetoothDevice::BatteryType::kDefault, 65));
keyboard = controller_->GetKeyboard(bluetooth_keyboard.id);
ASSERT_EQ(65, keyboard->battery_info->battery_percentage);
// Disconnecting the bluetooth device should remove the corresponding
// entry from the bluetooth address map.
ui::DeviceDataManagerTestApi().SetKeyboardDevices({});
bt_address_map = controller_->GetBluetoothAddressToDeviceIdMapForTesting();
ASSERT_EQ(0u, bt_address_map.size());
}
} // namespace ash