// Copyright (c) 2013 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 <list> #include <string> #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/ui_events.h" #include "chrome/test/chromedriver/key_converter.h" #include "chrome/test/chromedriver/test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace { void CheckEvents(const base::string16& keys, KeyEvent expected_events[], bool release_modifiers, size_t expected_size, int expected_modifiers) { int modifiers = 0; std::list<KeyEvent> events; EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, release_modifiers, &modifiers, &events).code()); EXPECT_EQ(expected_size, events.size()); size_t i = 0; std::list<KeyEvent>::const_iterator it = events.begin(); while (i < expected_size && it != events.end()) { EXPECT_EQ(expected_events[i].type, it->type); EXPECT_EQ(expected_events[i].modifiers, it->modifiers); EXPECT_EQ(expected_events[i].modified_text, it->modified_text); EXPECT_EQ(expected_events[i].unmodified_text, it->unmodified_text); EXPECT_EQ(expected_events[i].key_code, it->key_code); ++i; ++it; } EXPECT_EQ(expected_modifiers, modifiers); } void CheckEventsReleaseModifiers(const base::string16& keys, KeyEvent expected_events[], size_t expected_size) { CheckEvents(keys, expected_events, true /* release_modifier */, expected_size, 0 /* expected_modifiers */); } void CheckEventsReleaseModifiers(const std::string& keys, KeyEvent expected_events[], size_t expected_size) { CheckEventsReleaseModifiers(base::UTF8ToUTF16(keys), expected_events, expected_size); } void CheckNonShiftChar(ui::KeyboardCode key_code, char character) { int modifiers = 0; std::string char_string; char_string.push_back(character); std::list<KeyEvent> events; EXPECT_EQ(kOk, ConvertKeysToKeyEvents(base::ASCIIToUTF16(char_string), true /* release_modifiers*/, &modifiers, &events).code()); ASSERT_EQ(3u, events.size()) << "Char: " << character; std::list<KeyEvent>::const_iterator it = events.begin(); EXPECT_EQ(key_code, it->key_code) << "Char: " << character; ++it; // Move to the second event. ASSERT_EQ(1u, it->modified_text.length()) << "Char: " << character; ASSERT_EQ(1u, it->unmodified_text.length()) << "Char: " << character; EXPECT_EQ(character, it->modified_text[0]) << "Char: " << character; EXPECT_EQ(character, it->unmodified_text[0]) << "Char: " << character; ++it; // Move to the third event. EXPECT_EQ(key_code, it->key_code) << "Char: " << character; } void CheckShiftChar(ui::KeyboardCode key_code, char character, char lower) { int modifiers = 0; std::string char_string; char_string.push_back(character); std::list<KeyEvent> events; EXPECT_EQ(kOk, ConvertKeysToKeyEvents(base::ASCIIToUTF16(char_string), true /* release_modifiers*/, &modifiers, &events).code()); ASSERT_EQ(5u, events.size()) << "Char: " << character; std::list<KeyEvent>::const_iterator it = events.begin(); EXPECT_EQ(ui::VKEY_SHIFT, it->key_code) << "Char: " << character; ++it; // Move to second event. EXPECT_EQ(key_code, it->key_code) << "Char: " << character; ++it; // Move to third event. ASSERT_EQ(1u, it->modified_text.length()) << "Char: " << character; ASSERT_EQ(1u, it->unmodified_text.length()) << "Char: " << character; EXPECT_EQ(character, it->modified_text[0]) << "Char: " << character; EXPECT_EQ(lower, it->unmodified_text[0]) << "Char: " << character; ++it; // Move to fourth event. EXPECT_EQ(key_code, it->key_code) << "Char: " << character; ++it; // Move to fifth event. EXPECT_EQ(ui::VKEY_SHIFT, it->key_code) << "Char: " << character; } } // namespace TEST(KeyConverter, SingleChar) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_H, 0), CreateCharEvent("h", "h", 0), CreateKeyUpEvent(ui::VKEY_H, 0)}; CheckEventsReleaseModifiers("h", event_array, arraysize(event_array)); } TEST(KeyConverter, SingleNumber) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_1, 0), CreateCharEvent("1", "1", 0), CreateKeyUpEvent(ui::VKEY_1, 0)}; CheckEventsReleaseModifiers("1", event_array, arraysize(event_array)); } TEST(KeyConverter, MultipleChars) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_H, 0), CreateCharEvent("h", "h", 0), CreateKeyUpEvent(ui::VKEY_H, 0), CreateKeyDownEvent(ui::VKEY_E, 0), CreateCharEvent("e", "e", 0), CreateKeyUpEvent(ui::VKEY_E, 0), CreateKeyDownEvent(ui::VKEY_Y, 0), CreateCharEvent("y", "y", 0), CreateKeyUpEvent(ui::VKEY_Y, 0)}; CheckEventsReleaseModifiers("hey", event_array, arraysize(event_array)); } TEST(KeyConverter, WebDriverSpecialChar) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SPACE, 0), CreateCharEvent(" ", " ", 0), CreateKeyUpEvent(ui::VKEY_SPACE, 0)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE00DU)); CheckEventsReleaseModifiers(keys, event_array, arraysize(event_array)); } TEST(KeyConverter, WebDriverSpecialNonCharKey) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_F1, 0), CreateKeyUpEvent(ui::VKEY_F1, 0)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE031U)); CheckEventsReleaseModifiers(keys, event_array, arraysize(event_array)); } TEST(KeyConverter, FrenchKeyOnEnglishLayout) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_UNKNOWN, 0), CreateCharEvent(base::WideToUTF8(L"\u00E9"), base::WideToUTF8(L"\u00E9"), 0), CreateKeyUpEvent(ui::VKEY_UNKNOWN, 0)}; CheckEventsReleaseModifiers(base::WideToUTF16(L"\u00E9"), event_array, arraysize(event_array)); } #if defined(OS_WIN) TEST(KeyConverter, NeedsCtrlAndAlt) { RestoreKeyboardLayoutOnDestruct restore; int ctrl_and_alt = kControlKeyModifierMask | kAltKeyModifierMask; KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_CONTROL, 0), CreateKeyDownEvent(ui::VKEY_MENU, 0), CreateKeyDownEvent(ui::VKEY_Q, ctrl_and_alt), CreateCharEvent("q", "@", ctrl_and_alt), CreateKeyUpEvent(ui::VKEY_Q, ctrl_and_alt), CreateKeyUpEvent(ui::VKEY_MENU, 0), CreateKeyUpEvent(ui::VKEY_CONTROL, 0)}; ASSERT_TRUE(SwitchKeyboardLayout("00000407")); CheckEventsReleaseModifiers("@", event_array, arraysize(event_array)); } #endif TEST(KeyConverter, UppercaseCharDoesShift) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, 0), CreateKeyDownEvent(ui::VKEY_A, kShiftKeyModifierMask), CreateCharEvent("a", "A", kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_A, kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_SHIFT, 0)}; CheckEventsReleaseModifiers("A", event_array, arraysize(event_array)); } TEST(KeyConverter, UppercaseSymbolCharDoesShift) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, 0), CreateKeyDownEvent(ui::VKEY_1, kShiftKeyModifierMask), CreateCharEvent("1", "!", kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_1, kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_SHIFT, 0)}; CheckEventsReleaseModifiers("!", event_array, arraysize(event_array)); } TEST(KeyConverter, UppercaseCharUsesShiftOnlyIfNecessary) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, kShiftKeyModifierMask), CreateKeyDownEvent(ui::VKEY_A, kShiftKeyModifierMask), CreateCharEvent("a", "A", kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_A, kShiftKeyModifierMask), CreateKeyDownEvent(ui::VKEY_B, kShiftKeyModifierMask), CreateCharEvent("b", "B", kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_B, kShiftKeyModifierMask), CreateKeyDownEvent(ui::VKEY_C, kShiftKeyModifierMask), CreateCharEvent("c", "C", kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_C, kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_SHIFT, 0)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE008U)); keys.append(base::UTF8ToUTF16("aBc")); CheckEventsReleaseModifiers(keys, event_array, arraysize(event_array)); } TEST(KeyConverter, ToggleModifiers) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, kShiftKeyModifierMask), CreateKeyUpEvent(ui::VKEY_SHIFT, 0), CreateKeyDownEvent(ui::VKEY_CONTROL, kControlKeyModifierMask), CreateKeyUpEvent(ui::VKEY_CONTROL, 0), CreateKeyDownEvent(ui::VKEY_MENU, kAltKeyModifierMask), CreateKeyUpEvent(ui::VKEY_MENU, 0), CreateKeyDownEvent(ui::VKEY_COMMAND, kMetaKeyModifierMask), CreateKeyUpEvent(ui::VKEY_COMMAND, 0)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE008U)); keys.push_back(static_cast<base::char16>(0xE008U)); keys.push_back(static_cast<base::char16>(0xE009U)); keys.push_back(static_cast<base::char16>(0xE009U)); keys.push_back(static_cast<base::char16>(0xE00AU)); keys.push_back(static_cast<base::char16>(0xE00AU)); keys.push_back(static_cast<base::char16>(0xE03DU)); keys.push_back(static_cast<base::char16>(0xE03DU)); CheckEventsReleaseModifiers(keys, event_array, arraysize(event_array)); } #if defined(OS_WIN) // https://code.google.com/p/chromedriver/issues/detail?id=546 #define MAYBE_AllShorthandKeys DISABLED_AllShorthandKeys #else #define MAYBE_AllShorthandKeys AllShorthandKeys #endif TEST(KeyConverter, MAYBE_AllShorthandKeys) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_RETURN, 0), CreateCharEvent("\r", "\r", 0), CreateKeyUpEvent(ui::VKEY_RETURN, 0), CreateKeyDownEvent(ui::VKEY_RETURN, 0), CreateCharEvent("\r", "\r", 0), CreateKeyUpEvent(ui::VKEY_RETURN, 0), CreateKeyDownEvent(ui::VKEY_TAB, 0), #if defined(USE_AURA) || defined(OS_LINUX) CreateCharEvent("\t", "\t", 0), #endif CreateKeyUpEvent(ui::VKEY_TAB, 0), CreateKeyDownEvent(ui::VKEY_BACK, 0), #if defined(USE_AURA) || defined(OS_LINUX) CreateCharEvent("\b", "\b", 0), #endif CreateKeyUpEvent(ui::VKEY_BACK, 0), CreateKeyDownEvent(ui::VKEY_SPACE, 0), CreateCharEvent(" ", " ", 0), CreateKeyUpEvent(ui::VKEY_SPACE, 0)}; CheckEventsReleaseModifiers("\n\r\n\t\b ", event_array,arraysize(event_array)); } #if defined(OS_LINUX) // Fails on bots: crbug.com/174962 #define MAYBE_AllEnglishKeyboardSymbols DISABLED_AllEnglishKeyboardSymbols #else #define MAYBE_AllEnglishKeyboardSymbols AllEnglishKeyboardSymbols #endif TEST(KeyConverter, MAYBE_AllEnglishKeyboardSymbols) { base::string16 keys; const ui::KeyboardCode kSymbolKeyCodes[] = { ui::VKEY_OEM_3, ui::VKEY_OEM_MINUS, ui::VKEY_OEM_PLUS, ui::VKEY_OEM_4, ui::VKEY_OEM_6, ui::VKEY_OEM_5, ui::VKEY_OEM_1, ui::VKEY_OEM_7, ui::VKEY_OEM_COMMA, ui::VKEY_OEM_PERIOD, ui::VKEY_OEM_2}; std::string kLowerSymbols = "`-=[]\\;',./"; std::string kUpperSymbols = "~_+{}|:\"<>?"; for (size_t i = 0; i < kLowerSymbols.length(); ++i) CheckNonShiftChar(kSymbolKeyCodes[i], kLowerSymbols[i]); for (size_t i = 0; i < kUpperSymbols.length(); ++i) CheckShiftChar(kSymbolKeyCodes[i], kUpperSymbols[i], kLowerSymbols[i]); } TEST(KeyConverter, AllEnglishKeyboardTextChars) { std::string kLowerChars = "0123456789abcdefghijklmnopqrstuvwxyz"; std::string kUpperChars = ")!@#$%^&*(ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (size_t i = 0; i < kLowerChars.length(); ++i) { int offset = 0; if (i < 10) offset = ui::VKEY_0; else offset = ui::VKEY_0 + 7; ui::KeyboardCode expected_code = static_cast<ui::KeyboardCode>(offset + i); CheckNonShiftChar(expected_code, kLowerChars[i]); } for (size_t i = 0; i < kUpperChars.length(); ++i) { int offset = 0; if (i < 10) offset = ui::VKEY_0; else offset = ui::VKEY_0 + 7; ui::KeyboardCode expected_code = static_cast<ui::KeyboardCode>(offset + i); CheckShiftChar(expected_code, kUpperChars[i], kLowerChars[i]); } } #if defined(OS_LINUX) || defined(OS_WIN) // https://code.google.com/p/chromedriver/issues/detail?id=240 // https://code.google.com/p/chromedriver/issues/detail?id=546 #define MAYBE_AllSpecialWebDriverKeysOnEnglishKeyboard \ DISABLED_AllSpecialWebDriverKeysOnEnglishKeyboard #else #define MAYBE_AllSpecialWebDriverKeysOnEnglishKeyboard \ AllSpecialWebDriverKeysOnEnglishKeyboard #endif TEST(KeyConverter, MAYBE_AllSpecialWebDriverKeysOnEnglishKeyboard) { const char kTextForKeys[] = { #if defined(USE_AURA) || defined(OS_LINUX) 0, 0, 0, '\b', '\t', 0, '\r', '\r', 0, 0, 0, 0, 0x1B, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7F, ';', '=', #else 0, 0, 0, 0, 0, 0, '\r', '\r', 0, 0, 0, 0, 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ';', '=', #endif '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '+', ',', '-', '.', '/'}; for (size_t i = 0; i <= 0x3D; ++i) { if (i > 0x29 && i < 0x31) continue; base::string16 keys; int modifiers = 0; keys.push_back(0xE000U + i); std::list<KeyEvent> events; if (i == 1) { EXPECT_NE(kOk, ConvertKeysToKeyEvents(keys, true /* release_modifiers*/, &modifiers, &events).code()) << "Index: " << i; EXPECT_EQ(0u, events.size()) << "Index: " << i; } else { EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, true /* release_modifiers */, &modifiers, &events).code()) << "Index: " << i; if (i == 0) { EXPECT_EQ(0u, events.size()) << "Index: " << i; } else if (i >= arraysize(kTextForKeys) || kTextForKeys[i] == 0) { EXPECT_EQ(2u, events.size()) << "Index: " << i; } else { ASSERT_EQ(3u, events.size()) << "Index: " << i; std::list<KeyEvent>::const_iterator it = events.begin(); ++it; // Move to the second event. ASSERT_EQ(1u, it->unmodified_text.length()) << "Index: " << i; EXPECT_EQ(kTextForKeys[i], it->unmodified_text[0]) << "Index: " << i; } } } } TEST(KeyConverter, ModifiersState) { int shift_key_modifier = kShiftKeyModifierMask; int control_key_modifier = shift_key_modifier | kControlKeyModifierMask; int alt_key_modifier = control_key_modifier | kAltKeyModifierMask; int meta_key_modifier = alt_key_modifier | kMetaKeyModifierMask; KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, shift_key_modifier), CreateKeyDownEvent(ui::VKEY_CONTROL, control_key_modifier), CreateKeyDownEvent(ui::VKEY_MENU, alt_key_modifier), CreateKeyDownEvent(ui::VKEY_COMMAND, meta_key_modifier)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE008U)); keys.push_back(static_cast<base::char16>(0xE009U)); keys.push_back(static_cast<base::char16>(0xE00AU)); keys.push_back(static_cast<base::char16>(0xE03DU)); CheckEvents(keys, event_array, false /* release_modifiers */, arraysize(event_array), meta_key_modifier); } TEST(KeyConverter, ReleaseModifiers) { KeyEvent event_array[] = { CreateKeyDownEvent(ui::VKEY_SHIFT, kShiftKeyModifierMask), CreateKeyDownEvent(ui::VKEY_CONTROL, kShiftKeyModifierMask | kControlKeyModifierMask), CreateKeyUpEvent(ui::VKEY_SHIFT, 0), CreateKeyUpEvent(ui::VKEY_CONTROL, 0)}; base::string16 keys; keys.push_back(static_cast<base::char16>(0xE008U)); keys.push_back(static_cast<base::char16>(0xE009U)); CheckEvents(keys, event_array, true /* release_modifiers */, arraysize(event_array), 0); }