blob: 3e7d60cecd1eb95ae1b7e742c18fe535b81bca8a [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/accelerators/accelerator_launcher_state_machine.h"
#include <variant>
#include "ash/test/ash_test_base.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/devices/stylus_state.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/types/event_type.h"
#include "ui/ozone/public/stub_input_controller.h"
namespace ash::accelerators {
namespace {
using EventTypeVariant = std::variant<ui::MouseEvent, ui::KeyEvent, bool>;
using LauncherState = AcceleratorLauncherStateMachine::LauncherState;
const bool kKeysPressed = true;
const bool kNoKeysPressed = false;
class MockInputController : public ui::StubInputController {
public:
MOCK_METHOD(bool, AreAnyKeysPressed, (), (override));
};
ui::KeyEvent KeyPress(ui::KeyboardCode key_code) {
return ui::KeyEvent(ui::EventType::kKeyPressed, key_code, ui::EF_NONE);
}
ui::KeyEvent KeyRelease(ui::KeyboardCode key_code) {
return ui::KeyEvent(ui::EventType::kKeyReleased, key_code, ui::EF_NONE);
}
ui::Event& GetEventFromVariant(EventTypeVariant& event) {
if (std::holds_alternative<ui::MouseEvent>(event)) {
return std::get<ui::MouseEvent>(event);
} else {
return std::get<ui::KeyEvent>(event);
}
}
} // namespace
class AcceleratorLauncherStateMachineTest
: public AshTestBase,
public testing::WithParamInterface<
std::tuple<std::vector<EventTypeVariant>, LauncherState>> {
public:
void SetUp() override {
AshTestBase::SetUp();
std::tie(events_, expected_state_) = GetParam();
input_controller_ = std::make_unique<MockInputController>();
launcher_state_machine_ = std::make_unique<AcceleratorLauncherStateMachine>(
input_controller_.get());
}
void TearDown() override {
launcher_state_machine_.reset();
AshTestBase::TearDown();
}
protected:
std::unique_ptr<AcceleratorLauncherStateMachine> launcher_state_machine_;
std::unique_ptr<MockInputController> input_controller_;
std::vector<EventTypeVariant> events_;
LauncherState expected_state_;
};
INSTANTIATE_TEST_SUITE_P(
Works,
AcceleratorLauncherStateMachineTest,
testing::ValuesIn(std::vector<
std::tuple<std::vector<EventTypeVariant>, LauncherState>>{
// Pressing and releasing Meta key allows launcher to trigger.
{{kKeysPressed, KeyPress(ui::VKEY_LWIN), kNoKeysPressed,
KeyRelease(ui::VKEY_LWIN)},
LauncherState::kTrigger},
// Pressing Shift -> Meta allows launcher to open.
{{kKeysPressed, KeyPress(ui::VKEY_SHIFT), KeyPress(ui::VKEY_LWIN),
KeyRelease(ui::VKEY_LWIN)},
LauncherState::kTrigger},
// Getting into a suppressed state and then releasing all keys resets
// and allows you to trigger the launcher via Meta press and release.
{{kKeysPressed, KeyPress(ui::VKEY_LWIN), KeyPress(ui::VKEY_A),
KeyRelease(ui::VKEY_A), kNoKeysPressed, KeyRelease(ui::VKEY_LWIN),
kKeysPressed, KeyPress(ui::VKEY_LWIN), kNoKeysPressed,
KeyRelease(ui::VKEY_LWIN)},
LauncherState::kTrigger},
// Meta -> Shift -> Shift release -> Meta release does not trigger and
// instead puts us back at the start.
{{kKeysPressed, KeyPress(ui::VKEY_LWIN), KeyPress(ui::VKEY_SHIFT),
KeyRelease(ui::VKEY_SHIFT), kNoKeysPressed,
KeyRelease(ui::VKEY_LWIN)},
LauncherState::kStart},
// Shift -> Meta leaves us in kPrimed.
{{kKeysPressed, KeyPress(ui::VKEY_SHIFT), KeyPress(ui::VKEY_LWIN)},
LauncherState::kPrimed},
// Pressing any key besides shift or meta leaves us in kSuppress.
{{kKeysPressed, KeyPress(ui::VKEY_A)}, LauncherState::kSuppress},
// Pressing any key besides shift or meta and releasing resets us back
// to kStart.
{{kKeysPressed, KeyPress(ui::VKEY_A), kNoKeysPressed,
KeyRelease(ui::VKEY_A)},
LauncherState::kStart},
// Pressing A -> Press and release Meta leaves us still in kSuppress and
// does not allow the launcher to open.
{{kKeysPressed, KeyPress(ui::VKEY_A), KeyPress(ui::VKEY_LWIN),
KeyRelease(ui::VKEY_LWIN)},
LauncherState::kSuppress},
// Press A -> Press B -> Release A leaves us still in kSuppress as B is
// still being held down.
{{kKeysPressed, KeyPress(ui::VKEY_A), KeyPress(ui::VKEY_B),
KeyRelease(ui::VKEY_A)},
LauncherState::kSuppress},
}));
TEST_P(AcceleratorLauncherStateMachineTest, StateTest) {
for (auto& event : events_) {
if (std::holds_alternative<bool>(event)) {
ON_CALL(*input_controller_, AreAnyKeysPressed())
.WillByDefault(testing::Return(std::get<bool>(event)));
continue;
}
launcher_state_machine_->OnEvent(&GetEventFromVariant(event));
}
EXPECT_EQ(expected_state_, launcher_state_machine_->current_state());
}
} // namespace ash::accelerators