blob: 2d3b549e287ee2f5bcbf7a0982149389bf2ca8fa [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/events/accessibility_event_rewriter.h"
#include <memory>
#include <vector>
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/public/cpp/accessibility_event_rewriter_delegate.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/macros.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/chromeos/events/event_rewriter_chromeos.h"
#include "ui/chromeos/events/modifier_key.h"
#include "ui/chromeos/events/pref_names.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/test/test_event_rewriter.h"
#include "ui/events/types/event_type.h"
namespace ash {
namespace {
// A test implementation of the spoken feedback delegate interface.
// TODO(crbug/1116205): Merge ChromeVox and Switch Access test infrastructure
// below.
class ChromeVoxTestDelegate : public AccessibilityEventRewriterDelegate {
public:
ChromeVoxTestDelegate() = default;
ChromeVoxTestDelegate(const ChromeVoxTestDelegate&) = delete;
ChromeVoxTestDelegate& operator=(const ChromeVoxTestDelegate&) = delete;
~ChromeVoxTestDelegate() override = default;
// Count of events sent to the delegate.
size_t chromevox_recorded_event_count_ = 0;
// Count of captured events sent to the delegate.
size_t chromevox_captured_event_count_ = 0;
private:
// AccessibilityEventRewriterDelegate:
void DispatchKeyEventToChromeVox(std::unique_ptr<ui::Event> event,
bool capture) override {
chromevox_recorded_event_count_++;
if (capture)
chromevox_captured_event_count_++;
}
void DispatchMouseEventToChromeVox(
std::unique_ptr<ui::Event> event) override {
chromevox_recorded_event_count_++;
}
void SendSwitchAccessCommand(SwitchAccessCommand command) override {}
void SendPointScanPoint(const gfx::PointF& point) override {}
};
class ChromeVoxAccessibilityEventRewriterTest
: public ash::AshTestBase,
public ui::EventRewriterChromeOS::Delegate {
public:
ChromeVoxAccessibilityEventRewriterTest() {
event_rewriter_chromeos_ =
std::make_unique<ui::EventRewriterChromeOS>(this, nullptr, false);
}
ChromeVoxAccessibilityEventRewriterTest(
const ChromeVoxAccessibilityEventRewriterTest&) = delete;
ChromeVoxAccessibilityEventRewriterTest& operator=(
const ChromeVoxAccessibilityEventRewriterTest&) = delete;
void SetUp() override {
ash::AshTestBase::SetUp();
generator_ = AshTestBase::GetEventGenerator();
accessibility_event_rewriter_ =
std::make_unique<AccessibilityEventRewriter>(
event_rewriter_chromeos_.get(), &delegate_);
GetContext()->GetHost()->GetEventSource()->AddEventRewriter(
accessibility_event_rewriter_.get());
GetContext()->GetHost()->GetEventSource()->AddEventRewriter(
&event_recorder_);
}
void TearDown() override {
GetContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
&event_recorder_);
GetContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
accessibility_event_rewriter_.get());
accessibility_event_rewriter_.reset();
generator_ = nullptr;
ash::AshTestBase::TearDown();
}
size_t delegate_chromevox_recorded_event_count() {
return delegate_.chromevox_recorded_event_count_;
}
size_t delegate_chromevox_captured_event_count() {
return delegate_.chromevox_captured_event_count_;
}
void SetDelegateChromeVoxCaptureAllKeys(bool value) {
accessibility_event_rewriter_->set_chromevox_capture_all_keys(value);
}
void ExpectCounts(size_t expected_recorded_count,
size_t expected_delegate_count,
size_t expected_captured_count) {
EXPECT_EQ(expected_recorded_count,
static_cast<size_t>(event_recorder_.events_seen()));
EXPECT_EQ(expected_delegate_count,
delegate_chromevox_recorded_event_count());
EXPECT_EQ(expected_captured_count,
delegate_chromevox_captured_event_count());
}
void SetModifierRemapping(const std::string& pref_name,
ui::chromeos::ModifierKey value) {
modifier_remapping_[pref_name] = static_cast<int>(value);
}
std::set<int> GetSwitchAccessKeyCodesToCapture() {
return accessibility_event_rewriter_
->switch_access_key_codes_to_capture_for_test();
}
std::map<int, SwitchAccessCommand> GetSwitchAccessCommandForKeyCodeMap() {
return accessibility_event_rewriter_
->key_code_to_switch_access_command_map_for_test();
}
protected:
// A test accessibility event delegate; simulates ChromeVox and Switch Access.
ChromeVoxTestDelegate delegate_;
// Generates ui::Events from simulated user input.
ui::test::EventGenerator* generator_ = nullptr;
// Records events delivered to the next event rewriter after spoken feedback.
ui::test::TestEventRewriter event_recorder_;
std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter_;
std::unique_ptr<ui::EventRewriterChromeOS> event_rewriter_chromeos_;
private:
// ui::EventRewriterChromeOS::Delegate:
bool RewriteModifierKeys() override { return true; }
bool GetKeyboardRemappedPrefValue(const std::string& pref_name,
int* value) const override {
auto it = modifier_remapping_.find(pref_name);
if (it == modifier_remapping_.end())
return false;
*value = it->second;
return true;
}
bool TopRowKeysAreFunctionKeys() const override { return false; }
bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
int flags) const override {
return false;
}
bool IsSearchKeyAcceleratorReserved() const override { return false; }
std::map<std::string, int> modifier_remapping_;
};
// The delegate should not intercept events when spoken feedback is disabled.
TEST_F(ChromeVoxAccessibilityEventRewriterTest, EventsNotConsumedWhenDisabled) {
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
EXPECT_FALSE(controller->spoken_feedback().enabled());
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(1, event_recorder_.events_seen());
EXPECT_EQ(0U, delegate_chromevox_recorded_event_count());
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(2, event_recorder_.events_seen());
EXPECT_EQ(0U, delegate_chromevox_recorded_event_count());
generator_->ClickLeftButton();
EXPECT_EQ(4, event_recorder_.events_seen());
EXPECT_EQ(0U, delegate_chromevox_recorded_event_count());
generator_->GestureTapAt(gfx::Point());
EXPECT_EQ(6, event_recorder_.events_seen());
EXPECT_EQ(0U, delegate_chromevox_recorded_event_count());
}
// The delegate should intercept key events when spoken feedback is enabled.
TEST_F(ChromeVoxAccessibilityEventRewriterTest, KeyEventsConsumedWhenEnabled) {
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
EXPECT_TRUE(controller->spoken_feedback().enabled());
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(1, event_recorder_.events_seen());
EXPECT_EQ(1U, delegate_chromevox_recorded_event_count());
EXPECT_EQ(0U, delegate_chromevox_captured_event_count());
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(2, event_recorder_.events_seen());
EXPECT_EQ(2U, delegate_chromevox_recorded_event_count());
EXPECT_EQ(0U, delegate_chromevox_captured_event_count());
generator_->ClickLeftButton();
EXPECT_EQ(4, event_recorder_.events_seen());
EXPECT_EQ(2U, delegate_chromevox_recorded_event_count());
EXPECT_EQ(0U, delegate_chromevox_captured_event_count());
generator_->GestureTapAt(gfx::Point());
EXPECT_EQ(6, event_recorder_.events_seen());
EXPECT_EQ(2U, delegate_chromevox_recorded_event_count());
EXPECT_EQ(0U, delegate_chromevox_captured_event_count());
}
// Asynchronously unhandled events should be sent to subsequent rewriters.
TEST_F(ChromeVoxAccessibilityEventRewriterTest,
UnhandledEventsSentToOtherRewriters) {
// Before it can forward unhandled events, AccessibilityEventRewriter
// must have seen at least one event in the first place.
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(1, event_recorder_.events_seen());
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
EXPECT_EQ(2, event_recorder_.events_seen());
accessibility_event_rewriter_->OnUnhandledSpokenFeedbackEvent(
std::make_unique<ui::KeyEvent>(ui::ET_KEY_PRESSED, ui::VKEY_A,
ui::EF_NONE));
EXPECT_EQ(3, event_recorder_.events_seen());
accessibility_event_rewriter_->OnUnhandledSpokenFeedbackEvent(
std::make_unique<ui::KeyEvent>(ui::ET_KEY_RELEASED, ui::VKEY_A,
ui::EF_NONE));
EXPECT_EQ(4, event_recorder_.events_seen());
}
TEST_F(ChromeVoxAccessibilityEventRewriterTest,
KeysNotEatenWithChromeVoxDisabled) {
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
EXPECT_FALSE(controller->spoken_feedback().enabled());
// Send Search+Shift+Right.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
EXPECT_EQ(1, event_recorder_.events_seen());
generator_->PressKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(2, event_recorder_.events_seen());
// Mock successful commands lookup and dispatch; shouldn't matter either way.
generator_->PressKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(3, event_recorder_.events_seen());
// Released keys shouldn't get eaten.
generator_->ReleaseKey(ui::VKEY_RIGHT,
ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
generator_->ReleaseKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN);
generator_->ReleaseKey(ui::VKEY_LWIN, 0);
EXPECT_EQ(6, event_recorder_.events_seen());
// Try releasing more keys.
generator_->ReleaseKey(ui::VKEY_RIGHT, 0);
generator_->ReleaseKey(ui::VKEY_SHIFT, 0);
generator_->ReleaseKey(ui::VKEY_LWIN, 0);
EXPECT_EQ(9, event_recorder_.events_seen());
EXPECT_EQ(0U, delegate_chromevox_recorded_event_count());
}
TEST_F(ChromeVoxAccessibilityEventRewriterTest, KeyEventsCaptured) {
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
EXPECT_TRUE(controller->spoken_feedback().enabled());
// Initialize expected counts as variables for easier maintaiblity.
size_t recorded_count = 0;
size_t delegate_count = 0;
size_t captured_count = 0;
// Anything with Search gets captured.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// Tab never gets captured.
generator_->PressKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
// A client requested capture of all keys.
SetDelegateChromeVoxCaptureAllKeys(true);
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// Tab never gets captured even with explicit client request for all keys.
generator_->PressKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
// A client requested to not capture all keys.
SetDelegateChromeVoxCaptureAllKeys(false);
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
}
TEST_F(ChromeVoxAccessibilityEventRewriterTest,
KeyEventsCapturedWithModifierRemapping) {
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
EXPECT_TRUE(controller->spoken_feedback().enabled());
// Initialize expected counts as variables for easier maintaiblity.
size_t recorded_count = 0;
size_t delegate_count = 0;
size_t captured_count = 0;
// Map Control key to Search.
SetModifierRemapping(prefs::kLanguageRemapControlKeyTo,
ui::chromeos::ModifierKey::kSearchKey);
// Anything with Search gets captured.
generator_->PressKey(ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// EventRewriterChromeOS actually omits the modifier flag.
generator_->ReleaseKey(ui::VKEY_CONTROL, 0);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// Search itself should also work.
generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
generator_->ReleaseKey(ui::VKEY_LWIN, 0);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// Remapping should have no effect on all other expectations.
// Tab never gets captured.
generator_->PressKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
// A client requested capture of all keys.
SetDelegateChromeVoxCaptureAllKeys(true);
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(recorded_count, ++delegate_count, ++captured_count);
// Tab never gets captured even with explicit client request for all keys.
generator_->PressKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
// A client requested to not capture all keys.
SetDelegateChromeVoxCaptureAllKeys(false);
generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
ExpectCounts(++recorded_count, ++delegate_count, captured_count);
}
// Records all key events for testing.
class EventCapturer : public ui::EventHandler {
public:
EventCapturer() = default;
~EventCapturer() override = default;
void Reset() { last_key_event_.reset(); }
ui::KeyEvent* last_key_event() { return last_key_event_.get(); }
private:
void OnKeyEvent(ui::KeyEvent* event) override {
last_key_event_ = std::make_unique<ui::KeyEvent>(*event);
}
std::unique_ptr<ui::KeyEvent> last_key_event_;
DISALLOW_COPY_AND_ASSIGN(EventCapturer);
};
class SwitchAccessTestDelegate : public AccessibilityEventRewriterDelegate {
public:
SwitchAccessTestDelegate() = default;
~SwitchAccessTestDelegate() override = default;
SwitchAccessCommand last_command() { return commands_.back(); }
int command_count() { return commands_.size(); }
// AccessibilityEventRewriterDelegate:
void SendSwitchAccessCommand(SwitchAccessCommand command) override {
commands_.push_back(command);
}
void SendPointScanPoint(const gfx::PointF& point) override {}
void DispatchKeyEventToChromeVox(std::unique_ptr<ui::Event>, bool) override {}
void DispatchMouseEventToChromeVox(std::unique_ptr<ui::Event>) override {}
private:
std::vector<SwitchAccessCommand> commands_;
DISALLOW_COPY_AND_ASSIGN(SwitchAccessTestDelegate);
};
class SwitchAccessAccessibilityEventRewriterTest : public AshTestBase {
public:
SwitchAccessAccessibilityEventRewriterTest() {
event_rewriter_chromeos_ =
std::make_unique<ui::EventRewriterChromeOS>(nullptr, nullptr, false);
}
~SwitchAccessAccessibilityEventRewriterTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
// This test triggers a resize of WindowTreeHost, which will end up
// throttling events. set_throttle_input_on_resize_for_testing() disables
// this.
aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
delegate_ = std::make_unique<SwitchAccessTestDelegate>();
accessibility_event_rewriter_ =
std::make_unique<AccessibilityEventRewriter>(
event_rewriter_chromeos_.get(), delegate_.get());
generator_ = AshTestBase::GetEventGenerator();
GetContext()->AddPreTargetHandler(&event_capturer_);
GetContext()->GetHost()->GetEventSource()->AddEventRewriter(
accessibility_event_rewriter_.get());
controller_ = Shell::Get()->accessibility_controller();
controller_->SetAccessibilityEventRewriter(
accessibility_event_rewriter_.get());
controller_->switch_access().SetEnabled(true);
}
void TearDown() override {
GetContext()->RemovePreTargetHandler(&event_capturer_);
generator_ = nullptr;
controller_ = nullptr;
accessibility_event_rewriter_.reset();
AshTestBase::TearDown();
}
void SetKeyCodesForSwitchAccessCommand(std::set<int> key_codes,
SwitchAccessCommand command) {
AccessibilityEventRewriter* rewriter =
controller_->GetAccessibilityEventRewriterForTest();
rewriter->SetKeyCodesForSwitchAccessCommand(key_codes, command);
}
const std::set<int> GetKeyCodesToCapture() {
AccessibilityEventRewriter* rewriter =
controller_->GetAccessibilityEventRewriterForTest();
if (rewriter)
return rewriter->switch_access_key_codes_to_capture_for_test();
return std::set<int>();
}
const std::map<int, SwitchAccessCommand> GetCommandForKeyCodeMap() {
AccessibilityEventRewriter* rewriter =
controller_->GetAccessibilityEventRewriterForTest();
if (rewriter)
return rewriter->key_code_to_switch_access_command_map_for_test();
return std::map<int, SwitchAccessCommand>();
}
protected:
ui::test::EventGenerator* generator_ = nullptr;
EventCapturer event_capturer_;
AccessibilityControllerImpl* controller_ = nullptr;
std::unique_ptr<SwitchAccessTestDelegate> delegate_;
std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter_;
std::unique_ptr<ui::EventRewriterChromeOS> event_rewriter_chromeos_;
};
TEST_F(SwitchAccessAccessibilityEventRewriterTest, CaptureSpecifiedKeys) {
// Set keys for Switch Access to capture.
SetKeyCodesForSwitchAccessCommand({ui::VKEY_1, ui::VKEY_2},
SwitchAccessCommand::kSelect);
EXPECT_FALSE(event_capturer_.last_key_event());
generator_->PressKey(ui::VKEY_1, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE);
// The event was captured by AccessibilityEventRewriter.
EXPECT_FALSE(event_capturer_.last_key_event());
EXPECT_EQ(SwitchAccessCommand::kSelect, delegate_->last_command());
// Press the "2" key.
generator_->PressKey(ui::VKEY_2, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_2, ui::EF_NONE);
// We received a new event.
// The event was captured by AccessibilityEventRewriter.
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "3" key.
generator_->PressKey(ui::VKEY_3, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_3, ui::EF_NONE);
// The event was captured by AccessibilityEventRewriter.
EXPECT_TRUE(event_capturer_.last_key_event());
}
TEST_F(SwitchAccessAccessibilityEventRewriterTest,
KeysNoLongerCaptureAfterUpdate) {
// Set Switch Access to capture the keys {1, 2, 3}.
SetKeyCodesForSwitchAccessCommand({ui::VKEY_1, ui::VKEY_2, ui::VKEY_3},
SwitchAccessCommand::kSelect);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "1" key.
generator_->PressKey(ui::VKEY_1, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE);
// The event was captured by AccessibilityEventRewriter.
EXPECT_FALSE(event_capturer_.last_key_event());
EXPECT_EQ(SwitchAccessCommand::kSelect, delegate_->last_command());
// Update the Switch Access keys to capture {2, 3, 4}.
SetKeyCodesForSwitchAccessCommand({ui::VKEY_2, ui::VKEY_3, ui::VKEY_4},
SwitchAccessCommand::kSelect);
// Press the "1" key.
generator_->PressKey(ui::VKEY_1, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE);
// We received a new event.
// The event was NOT captured by AccessibilityEventRewriter.
EXPECT_TRUE(event_capturer_.last_key_event());
EXPECT_FALSE(event_capturer_.last_key_event()->handled());
// Press the "4" key.
generator_->PressKey(ui::VKEY_4, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_4, ui::EF_NONE);
// The event was captured by AccessibilityEventRewriter.
}
TEST_F(SwitchAccessAccessibilityEventRewriterTest,
SetKeyCodesForSwitchAccessCommand) {
AccessibilityEventRewriter* rewriter =
controller_->GetAccessibilityEventRewriterForTest();
EXPECT_NE(nullptr, rewriter);
// Both the key codes to capture and the command map should be empty.
EXPECT_EQ(0u, GetKeyCodesToCapture().size());
EXPECT_EQ(0u, GetCommandForKeyCodeMap().size());
// Set key codes for Select command.
std::set<int> new_key_codes;
new_key_codes.insert(48 /* '0' */);
new_key_codes.insert(83 /* 's' */);
rewriter->SetKeyCodesForSwitchAccessCommand(new_key_codes,
SwitchAccessCommand::kSelect);
// Check that values are added to both data structures.
std::set<int> kc_to_capture = GetKeyCodesToCapture();
EXPECT_EQ(2u, kc_to_capture.size());
EXPECT_EQ(1u, kc_to_capture.count(48));
EXPECT_EQ(1u, kc_to_capture.count(83));
std::map<int, SwitchAccessCommand> command_map = GetCommandForKeyCodeMap();
EXPECT_EQ(2u, command_map.size());
EXPECT_EQ(SwitchAccessCommand::kSelect, command_map.at(48));
EXPECT_EQ(SwitchAccessCommand::kSelect, command_map.at(83));
// Set key codes for the Next command.
new_key_codes.clear();
new_key_codes.insert(49 /* '1' */);
new_key_codes.insert(78 /* 'n' */);
rewriter->SetKeyCodesForSwitchAccessCommand(new_key_codes,
SwitchAccessCommand::kNext);
// Check that the new values are added and old values are not changed.
kc_to_capture = GetKeyCodesToCapture();
EXPECT_EQ(4u, kc_to_capture.size());
EXPECT_EQ(1u, kc_to_capture.count(49));
EXPECT_EQ(1u, kc_to_capture.count(78));
command_map = GetCommandForKeyCodeMap();
EXPECT_EQ(4u, command_map.size());
EXPECT_EQ(SwitchAccessCommand::kNext, command_map.at(49));
EXPECT_EQ(SwitchAccessCommand::kNext, command_map.at(78));
// Set key codes for the Previous command. Re-use a key code from above.
new_key_codes.clear();
new_key_codes.insert(49 /* '1' */);
new_key_codes.insert(80 /* 'p' */);
rewriter->SetKeyCodesForSwitchAccessCommand(new_key_codes,
SwitchAccessCommand::kPrevious);
// Check that '1' has been remapped to Previous.
kc_to_capture = GetKeyCodesToCapture();
EXPECT_EQ(5u, kc_to_capture.size());
EXPECT_EQ(1u, kc_to_capture.count(49));
EXPECT_EQ(1u, kc_to_capture.count(80));
command_map = GetCommandForKeyCodeMap();
EXPECT_EQ(5u, command_map.size());
EXPECT_EQ(SwitchAccessCommand::kPrevious, command_map.at(49));
EXPECT_EQ(SwitchAccessCommand::kPrevious, command_map.at(80));
EXPECT_EQ(SwitchAccessCommand::kNext, command_map.at(78));
// Set a new key code for the Select command.
new_key_codes.clear();
new_key_codes.insert(51 /* '3' */);
new_key_codes.insert(83 /* 's' */);
rewriter->SetKeyCodesForSwitchAccessCommand(new_key_codes,
SwitchAccessCommand::kSelect);
// Check that the previously set values for Select have been cleared.
kc_to_capture = GetKeyCodesToCapture();
EXPECT_EQ(5u, kc_to_capture.size());
EXPECT_EQ(0u, kc_to_capture.count(48));
EXPECT_EQ(1u, kc_to_capture.count(51));
EXPECT_EQ(1u, kc_to_capture.count(83));
command_map = GetCommandForKeyCodeMap();
EXPECT_EQ(5u, command_map.size());
EXPECT_EQ(SwitchAccessCommand::kSelect, command_map.at(51));
EXPECT_EQ(SwitchAccessCommand::kSelect, command_map.at(83));
EXPECT_EQ(command_map.end(), command_map.find(48));
}
TEST_F(SwitchAccessAccessibilityEventRewriterTest, SetKeyboardInputTypes) {
AccessibilityEventRewriter* rewriter =
controller_->GetAccessibilityEventRewriterForTest();
EXPECT_NE(nullptr, rewriter);
// Set Switch Access to capture these keys as the select command.
SetKeyCodesForSwitchAccessCommand(
{ui::VKEY_1, ui::VKEY_2, ui::VKEY_3, ui::VKEY_4},
SwitchAccessCommand::kSelect);
std::vector<ui::InputDevice> keyboards;
ui::DeviceDataManagerTestApi device_data_test_api;
keyboards.emplace_back(ui::InputDevice(1, ui::INPUT_DEVICE_INTERNAL, ""));
keyboards.emplace_back(ui::InputDevice(2, ui::INPUT_DEVICE_USB, ""));
keyboards.emplace_back(ui::InputDevice(3, ui::INPUT_DEVICE_BLUETOOTH, ""));
keyboards.emplace_back(ui::InputDevice(4, ui::INPUT_DEVICE_UNKNOWN, ""));
device_data_test_api.SetKeyboardDevices(keyboards);
// Press the "1" key with no source device id.
generator_->PressKey(ui::VKEY_1, ui::EF_NONE);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE);
// The event was captured by AccessibilityEventRewriter.
EXPECT_FALSE(event_capturer_.last_key_event());
EXPECT_EQ(SwitchAccessCommand::kSelect, delegate_->last_command());
// Press the "1" key from the internal keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_1, ui::EF_NONE, 1);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE, 1);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "2" key from the usb keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_2, ui::EF_NONE, 2);
generator_->ReleaseKey(ui::VKEY_2, ui::EF_NONE, 2);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "3" key from the bluetooth keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_3, ui::EF_NONE, 3);
generator_->ReleaseKey(ui::VKEY_3, ui::EF_NONE, 3);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "4" key from the unknown keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_4, ui::EF_NONE, 4);
generator_->ReleaseKey(ui::VKEY_4, ui::EF_NONE, 2);
EXPECT_FALSE(event_capturer_.last_key_event());
// Now, exclude some device types.
rewriter->SetKeyboardInputDeviceTypes(
{ui::INPUT_DEVICE_USB, ui::INPUT_DEVICE_BLUETOOTH});
// Press the "1" key from the internal keyboard which is not captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_1, ui::EF_NONE, 1);
generator_->ReleaseKey(ui::VKEY_1, ui::EF_NONE, 1);
EXPECT_TRUE(event_capturer_.last_key_event());
event_capturer_.Reset();
// Press the "2" key from the usb keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_2, ui::EF_NONE, 2);
generator_->ReleaseKey(ui::VKEY_2, ui::EF_NONE, 2);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "3" key from the bluetooth keyboard which is captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_3, ui::EF_NONE, 3);
generator_->ReleaseKey(ui::VKEY_3, ui::EF_NONE, 3);
EXPECT_FALSE(event_capturer_.last_key_event());
// Press the "4" key from the unknown keyboard which is not captured by
// AccessibilityEventRewriter.
generator_->PressKey(ui::VKEY_4, ui::EF_NONE, 4);
generator_->ReleaseKey(ui::VKEY_4, ui::EF_NONE, 2);
EXPECT_TRUE(event_capturer_.last_key_event());
}
} // namespace
} // namespace ash