blob: 34f13dc8b91afbb720861f28cb7dfa7ee4dcd385 [file] [log] [blame]
// Copyright (c) 2016 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 "ui/events/keycodes/platform_key_map_win.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/dom_us_layout_data.h"
#include "ui/events/test/keyboard_layout.h"
namespace ui {
namespace {
struct TestKey {
KeyboardCode key_code;
const char* normal;
const char* shift;
const char* capslock;
const char* altgr;
const char* shift_capslock;
const char* shift_altgr;
const char* altgr_capslock;
};
struct DomKeyAndFlags {
DomKey key;
int flags;
};
} // anonymous namespace
class PlatformKeyMapTest : public testing::Test {
public:
PlatformKeyMapTest() {}
~PlatformKeyMapTest() override {}
void CheckKeyboardCodeToKeyString(const char* label,
const PlatformKeyMap& keymap,
const TestKey& test_case) {
KeyboardCode key_code = test_case.key_code;
EXPECT_STREQ(test_case.normal,
KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_NONE))
.c_str())
<< label;
EXPECT_STREQ(test_case.shift, KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(
keymap, key_code, EF_SHIFT_DOWN))
.c_str())
<< label;
EXPECT_STREQ(test_case.capslock, KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(
keymap, key_code, EF_CAPS_LOCK_ON))
.c_str())
<< label;
EXPECT_STREQ(test_case.altgr, KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(
keymap, key_code, EF_ALTGR_DOWN))
.c_str())
<< label;
EXPECT_STREQ(test_case.shift_capslock,
KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(
keymap, key_code, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON))
.c_str())
<< label;
EXPECT_STREQ(test_case.shift_altgr,
KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(keymap, key_code,
EF_SHIFT_DOWN | EF_ALTGR_DOWN))
.c_str())
<< label;
EXPECT_STREQ(test_case.altgr_capslock,
KeycodeConverter::DomKeyToKeyString(
DomKeyFromKeyboardCodeImpl(
keymap, key_code, EF_ALTGR_DOWN | EF_CAPS_LOCK_ON))
.c_str())
<< label;
}
// Need this helper function to access private methods of |PlatformKeyMap|.
DomKey DomKeyFromKeyboardCodeImpl(const PlatformKeyMap& keymap,
KeyboardCode key_code,
int flags) {
return keymap.DomKeyFromKeyboardCodeImpl(key_code, &flags);
}
// Returns the DomKey and |flags| in a struct, for use in tests verifying
// that the API correctly modifies the |flags| in/out parameter.
DomKeyAndFlags DomKeyAndFlagsFromKeyboardCode(const PlatformKeyMap& keymap,
KeyboardCode key_code,
int flags) {
DomKeyAndFlags result = {DomKey(), flags};
result.key = keymap.DomKeyFromKeyboardCodeImpl(key_code, &result.flags);
return result;
}
private:
DISALLOW_COPY_AND_ASSIGN(PlatformKeyMapTest);
};
TEST_F(PlatformKeyMapTest, USLayout) {
PlatformKeyMap keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US));
const TestKey kUSLayoutTestCases[] = {
// n s c a sc sa ac
{VKEY_0, "0", ")", "0", "0", ")", ")", "0"},
{VKEY_1, "1", "!", "1", "1", "!", "!", "1"},
{VKEY_2, "2", "@", "2", "2", "@", "@", "2"},
{VKEY_3, "3", "#", "3", "3", "#", "#", "3"},
{VKEY_4, "4", "$", "4", "4", "$", "$", "4"},
{VKEY_5, "5", "%", "5", "5", "%", "%", "5"},
{VKEY_6, "6", "^", "6", "6", "^", "^", "6"},
{VKEY_7, "7", "&", "7", "7", "&", "&", "7"},
{VKEY_8, "8", "*", "8", "8", "*", "*", "8"},
{VKEY_9, "9", "(", "9", "9", "(", "(", "9"},
{VKEY_A, "a", "A", "A", "a", "a", "A", "A"},
{VKEY_B, "b", "B", "B", "b", "b", "B", "B"},
{VKEY_C, "c", "C", "C", "c", "c", "C", "C"},
{VKEY_D, "d", "D", "D", "d", "d", "D", "D"},
{VKEY_E, "e", "E", "E", "e", "e", "E", "E"},
{VKEY_F, "f", "F", "F", "f", "f", "F", "F"},
{VKEY_G, "g", "G", "G", "g", "g", "G", "G"},
{VKEY_H, "h", "H", "H", "h", "h", "H", "H"},
{VKEY_I, "i", "I", "I", "i", "i", "I", "I"},
{VKEY_J, "j", "J", "J", "j", "j", "J", "J"},
{VKEY_K, "k", "K", "K", "k", "k", "K", "K"},
{VKEY_L, "l", "L", "L", "l", "l", "L", "L"},
{VKEY_M, "m", "M", "M", "m", "m", "M", "M"},
{VKEY_N, "n", "N", "N", "n", "n", "N", "N"},
{VKEY_O, "o", "O", "O", "o", "o", "O", "O"},
{VKEY_P, "p", "P", "P", "p", "p", "P", "P"},
{VKEY_Q, "q", "Q", "Q", "q", "q", "Q", "Q"},
{VKEY_R, "r", "R", "R", "r", "r", "R", "R"},
{VKEY_S, "s", "S", "S", "s", "s", "S", "S"},
{VKEY_T, "t", "T", "T", "t", "t", "T", "T"},
{VKEY_U, "u", "U", "U", "u", "u", "U", "U"},
{VKEY_V, "v", "V", "V", "v", "v", "V", "V"},
{VKEY_W, "w", "W", "W", "w", "w", "W", "W"},
{VKEY_X, "x", "X", "X", "x", "x", "X", "X"},
{VKEY_Y, "y", "Y", "Y", "y", "y", "Y", "Y"},
{VKEY_Z, "z", "Z", "Z", "z", "z", "Z", "Z"},
};
for (const auto& test_case : kUSLayoutTestCases) {
CheckKeyboardCodeToKeyString("USLayout", keymap, test_case);
}
}
TEST_F(PlatformKeyMapTest, FRLayout) {
PlatformKeyMap keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_FRENCH));
const TestKey kFRLayoutTestCases[] = {
// n s c a sc sa ac
{VKEY_0, "à", "0", "0", "@", "à", "0", "@"},
{VKEY_1, "&", "1", "1", "&", "&", "1", "1"},
{VKEY_2, "é", "2", "2", "Dead", "é", "2", "Dead"},
{VKEY_3, "\"", "3", "3", "#", "\"", "3", "#"},
{VKEY_4, "\'", "4", "4", "{", "\'", "4", "{"},
{VKEY_5, "(", "5", "5", "[", "(", "5", "["},
{VKEY_6, "-", "6", "6", "|", "-", "6", "|"},
{VKEY_7, "è", "7", "7", "Dead", "è", "7", "Dead"},
{VKEY_8, "_", "8", "8", "\\", "_", "8", "\\"},
{VKEY_9, "ç", "9", "9", "^", "ç", "9", "^"},
{VKEY_A, "a", "A", "A", "a", "a", "A", "A"},
{VKEY_B, "b", "B", "B", "b", "b", "B", "B"},
{VKEY_C, "c", "C", "C", "c", "c", "C", "C"},
{VKEY_D, "d", "D", "D", "d", "d", "D", "D"},
{VKEY_E, "e", "E", "E", "€", "e", "E", "€"},
{VKEY_F, "f", "F", "F", "f", "f", "F", "F"},
{VKEY_G, "g", "G", "G", "g", "g", "G", "G"},
{VKEY_H, "h", "H", "H", "h", "h", "H", "H"},
{VKEY_I, "i", "I", "I", "i", "i", "I", "I"},
{VKEY_J, "j", "J", "J", "j", "j", "J", "J"},
{VKEY_K, "k", "K", "K", "k", "k", "K", "K"},
{VKEY_L, "l", "L", "L", "l", "l", "L", "L"},
{VKEY_M, "m", "M", "M", "m", "m", "M", "M"},
{VKEY_N, "n", "N", "N", "n", "n", "N", "N"},
{VKEY_O, "o", "O", "O", "o", "o", "O", "O"},
{VKEY_P, "p", "P", "P", "p", "p", "P", "P"},
{VKEY_Q, "q", "Q", "Q", "q", "q", "Q", "Q"},
{VKEY_R, "r", "R", "R", "r", "r", "R", "R"},
{VKEY_S, "s", "S", "S", "s", "s", "S", "S"},
{VKEY_T, "t", "T", "T", "t", "t", "T", "T"},
{VKEY_U, "u", "U", "U", "u", "u", "U", "U"},
{VKEY_V, "v", "V", "V", "v", "v", "V", "V"},
{VKEY_W, "w", "W", "W", "w", "w", "W", "W"},
{VKEY_X, "x", "X", "X", "x", "x", "X", "X"},
{VKEY_Y, "y", "Y", "Y", "y", "y", "Y", "Y"},
{VKEY_Z, "z", "Z", "Z", "z", "z", "Z", "Z"},
};
for (const auto& test_case : kFRLayoutTestCases) {
CheckKeyboardCodeToKeyString("FRLayout", keymap, test_case);
}
}
TEST_F(PlatformKeyMapTest, NumPad) {
PlatformKeyMap keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US));
const struct TestCase {
KeyboardCode key_code;
DomKey key;
} kNumPadTestCases[] = {
{VKEY_NUMPAD0, DomKey::FromCharacter('0')},
{VKEY_NUMPAD1, DomKey::FromCharacter('1')},
{VKEY_NUMPAD2, DomKey::FromCharacter('2')},
{VKEY_NUMPAD3, DomKey::FromCharacter('3')},
{VKEY_NUMPAD4, DomKey::FromCharacter('4')},
{VKEY_NUMPAD5, DomKey::FromCharacter('5')},
{VKEY_NUMPAD6, DomKey::FromCharacter('6')},
{VKEY_NUMPAD7, DomKey::FromCharacter('7')},
{VKEY_NUMPAD8, DomKey::FromCharacter('8')},
{VKEY_NUMPAD9, DomKey::FromCharacter('9')},
{VKEY_CLEAR, DomKey::CLEAR},
{VKEY_PRIOR, DomKey::PAGE_UP},
{VKEY_NEXT, DomKey::PAGE_DOWN},
{VKEY_END, DomKey::END},
{VKEY_HOME, DomKey::HOME},
{VKEY_LEFT, DomKey::ARROW_LEFT},
{VKEY_UP, DomKey::ARROW_UP},
{VKEY_RIGHT, DomKey::ARROW_RIGHT},
{VKEY_DOWN, DomKey::ARROW_DOWN},
{VKEY_INSERT, DomKey::INSERT},
{VKEY_DELETE, DomKey::DEL},
};
for (const auto& test_case : kNumPadTestCases) {
KeyboardCode key_code = test_case.key_code;
EXPECT_EQ(test_case.key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_NONE))
<< key_code;
EXPECT_EQ(test_case.key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_ALTGR_DOWN))
<< key_code;
EXPECT_EQ(test_case.key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_CONTROL_DOWN))
<< key_code;
EXPECT_EQ(test_case.key,
DomKeyFromKeyboardCodeImpl(keymap, key_code,
EF_ALTGR_DOWN | EF_CONTROL_DOWN))
<< key_code;
}
}
TEST_F(PlatformKeyMapTest, NonPrintableKey) {
HKL layout = GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US);
PlatformKeyMap keymap(layout);
for (const auto& test_case : kNonPrintableCodeMap) {
// Not available on |LAYOUT_US|.
if (test_case.dom_code == DomCode::PAUSE ||
test_case.dom_code == DomCode::LANG2 ||
test_case.dom_code == DomCode::NON_CONVERT)
continue;
int scan_code =
ui::KeycodeConverter::DomCodeToNativeKeycode(test_case.dom_code);
// TODO(input-dev): Some |scan_code| should map to different |key_code|
// based on modifiers.
KeyboardCode key_code = static_cast<KeyboardCode>(
::MapVirtualKeyEx(scan_code, MAPVK_VSC_TO_VK, layout));
if (key_code == VKEY_UNKNOWN)
continue;
EXPECT_EQ(test_case.dom_key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_NONE))
<< key_code << ", " << scan_code;
EXPECT_EQ(test_case.dom_key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_ALTGR_DOWN))
<< key_code << ", " << scan_code;
EXPECT_EQ(test_case.dom_key,
DomKeyFromKeyboardCodeImpl(keymap, key_code, EF_CONTROL_DOWN))
<< key_code << ", " << scan_code;
EXPECT_EQ(test_case.dom_key,
DomKeyFromKeyboardCodeImpl(keymap, key_code,
EF_ALTGR_DOWN | EF_CONTROL_DOWN))
<< key_code << ", " << scan_code;
}
}
TEST_F(PlatformKeyMapTest, KoreanSpecificKeys) {
const struct TestCase {
KeyboardCode key_code;
DomKey kr_key;
DomKey us_key;
} kKoreanTestCases[] = {
{VKEY_HANGUL, DomKey::HANGUL_MODE, DomKey::UNIDENTIFIED},
{VKEY_HANJA, DomKey::HANJA_MODE, DomKey::UNIDENTIFIED},
};
PlatformKeyMap us_keymap(
GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US));
PlatformKeyMap kr_keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_KOREAN));
for (const auto& test_case : kKoreanTestCases) {
EXPECT_EQ(test_case.us_key, DomKeyFromKeyboardCodeImpl(
us_keymap, test_case.key_code, EF_NONE))
<< test_case.key_code;
EXPECT_EQ(test_case.kr_key, DomKeyFromKeyboardCodeImpl(
kr_keymap, test_case.key_code, EF_NONE))
<< test_case.key_code;
}
}
TEST_F(PlatformKeyMapTest, JapaneseSpecificKeys) {
const struct TestCase {
KeyboardCode key_code;
DomKey jp_key;
DomKey us_key;
} kJapaneseTestCases[] = {
{VKEY_KANA, DomKey::KANA_MODE, DomKey::UNIDENTIFIED},
{VKEY_KANJI, DomKey::KANJI_MODE, DomKey::UNIDENTIFIED},
{VKEY_OEM_ATTN, DomKey::ALPHANUMERIC, DomKey::UNIDENTIFIED},
{VKEY_OEM_FINISH, DomKey::KATAKANA, DomKey::UNIDENTIFIED},
{VKEY_OEM_COPY, DomKey::HIRAGANA, DomKey::UNIDENTIFIED},
{VKEY_DBE_SBCSCHAR, DomKey::HANKAKU, DomKey::UNIDENTIFIED},
{VKEY_DBE_DBCSCHAR, DomKey::ZENKAKU, DomKey::UNIDENTIFIED},
{VKEY_OEM_BACKTAB, DomKey::ROMAJI, DomKey::UNIDENTIFIED},
{VKEY_ATTN, DomKey::KANA_MODE, DomKey::ATTN},
};
PlatformKeyMap us_keymap(
GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US));
PlatformKeyMap jp_keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_JAPANESE));
for (const auto& test_case : kJapaneseTestCases) {
EXPECT_EQ(test_case.us_key, DomKeyFromKeyboardCodeImpl(
us_keymap, test_case.key_code, EF_NONE))
<< test_case.key_code;
EXPECT_EQ(test_case.jp_key, DomKeyFromKeyboardCodeImpl(
jp_keymap, test_case.key_code, EF_NONE))
<< test_case.key_code;
}
}
TEST_F(PlatformKeyMapTest, AltGraphDomKey) {
PlatformKeyMap us_keymap(
GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_ENGLISH_US));
EXPECT_EQ(DomKey::ALT,
DomKeyFromKeyboardCodeImpl(us_keymap, VKEY_MENU, EF_ALTGR_DOWN));
EXPECT_EQ(DomKey::ALT,
DomKeyFromKeyboardCodeImpl(us_keymap, VKEY_MENU,
EF_ALTGR_DOWN | EF_IS_EXTENDED_KEY));
PlatformKeyMap fr_keymap(GetPlatformKeyboardLayout(KEYBOARD_LAYOUT_FRENCH));
EXPECT_EQ(DomKey::ALT,
DomKeyFromKeyboardCodeImpl(fr_keymap, VKEY_MENU, EF_ALTGR_DOWN));
EXPECT_EQ(DomKey::ALT_GRAPH,
DomKeyFromKeyboardCodeImpl(fr_keymap, VKEY_MENU,
EF_ALTGR_DOWN | EF_IS_EXTENDED_KEY));
}
namespace {
const struct AltGraphModifierTestCase {
// Test-case Virtual Keycode and modifier flags.
KeyboardCode key_code;
int flags;
// Whether or not this case generates an AltGraph-shifted key under FR-fr
// layout.
bool expect_alt_graph;
} kAltGraphModifierTestCases[] = {
{VKEY_C, EF_NONE, false},
{VKEY_C, EF_ALTGR_DOWN, false},
{VKEY_C, EF_CONTROL_DOWN | EF_ALT_DOWN, false},
{VKEY_C, EF_CONTROL_DOWN | EF_ALT_DOWN | EF_ALTGR_DOWN, false},
{VKEY_E, EF_NONE, false},
{VKEY_E, EF_ALTGR_DOWN, true},
{VKEY_E, EF_CONTROL_DOWN | EF_ALT_DOWN, true},
{VKEY_E, EF_CONTROL_DOWN | EF_ALT_DOWN | EF_ALTGR_DOWN, true},
};
class AltGraphModifierTest
: public PlatformKeyMapTest,
public testing::WithParamInterface<KeyboardLayout> {
public:
AltGraphModifierTest() : keymap_(GetPlatformKeyboardLayout(GetParam())) {}
protected:
PlatformKeyMap keymap_;
};
TEST_P(AltGraphModifierTest, AltGraphModifierBehaviour) {
// If the key generates a character under AltGraph then |result| should
// report AltGraph, but not Control or Alt.
for (const auto& test_case : kAltGraphModifierTestCases) {
DomKeyAndFlags result = DomKeyAndFlagsFromKeyboardCode(
keymap_, test_case.key_code, test_case.flags);
if (GetParam() == KEYBOARD_LAYOUT_FRENCH && test_case.expect_alt_graph) {
EXPECT_EQ(EF_ALTGR_DOWN, result.flags)
<< " for key_code=" << test_case.key_code
<< " flags=" << test_case.flags;
} else {
EXPECT_EQ(test_case.flags, result.flags)
<< " for key_code=" << test_case.key_code
<< " flags=" << test_case.flags;
}
}
}
INSTANTIATE_TEST_SUITE_P(VerifyAltGraph,
AltGraphModifierTest,
::testing::Values(KEYBOARD_LAYOUT_ENGLISH_US,
KEYBOARD_LAYOUT_FRENCH));
} // namespace
} // namespace ui