1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/input_method/xkeyboard.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <string>
10
11 #include <gtest/gtest.h>
12 #include <X11/Xlib.h>
13
14 #include "base/logging.h"
15
16 namespace chromeos {
17 namespace input_method {
18
19 namespace {
20
21 // Returns a ModifierMap object that contains the following mapping:
22 // - kSearchKey is mapped to |search|.
23 // - kLeftControl key is mapped to |control|.
24 // - kLeftAlt key is mapped to |alt|.
GetMap(ModifierKey search,ModifierKey control,ModifierKey alt)25 ModifierMap GetMap(ModifierKey search, ModifierKey control, ModifierKey alt) {
26 ModifierMap modifier_key;
27 // Use the Search key as |search|.
28 modifier_key.push_back(ModifierKeyPair(kSearchKey, search));
29 modifier_key.push_back(ModifierKeyPair(kLeftControlKey, control));
30 modifier_key.push_back(ModifierKeyPair(kLeftAltKey, alt));
31 return modifier_key;
32 }
33
34 // Checks |modifier_map| and returns true if the following conditions are met:
35 // - kSearchKey is mapped to |search|.
36 // - kLeftControl key is mapped to |control|.
37 // - kLeftAlt key is mapped to |alt|.
CheckMap(const ModifierMap & modifier_map,ModifierKey search,ModifierKey control,ModifierKey alt)38 bool CheckMap(const ModifierMap& modifier_map,
39 ModifierKey search, ModifierKey control, ModifierKey alt) {
40 ModifierMap::const_iterator begin = modifier_map.begin();
41 ModifierMap::const_iterator end = modifier_map.end();
42 if ((std::count(begin, end, ModifierKeyPair(kSearchKey, search)) == 1) &&
43 (std::count(begin, end,
44 ModifierKeyPair(kLeftControlKey, control)) == 1) &&
45 (std::count(begin, end, ModifierKeyPair(kLeftAltKey, alt)) == 1)) {
46 return true;
47 }
48 return false;
49 }
50
51 // Returns true if X display is available.
DisplayAvailable()52 bool DisplayAvailable() {
53 Display* display = XOpenDisplay(NULL);
54 if (!display) {
55 return false;
56 }
57 XCloseDisplay(display);
58 return true;
59 }
60
61 } // namespace
62
63 // Tests CreateFullXkbLayoutName() function.
TEST(XKeyboardTest,TestCreateFullXkbLayoutNameBasic)64 TEST(XKeyboardTest, TestCreateFullXkbLayoutNameBasic) {
65 // CreateFullXkbLayoutName should not accept an empty |layout_name|.
66 EXPECT_STREQ("", CreateFullXkbLayoutName(
67 "", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
68
69 // CreateFullXkbLayoutName should not accept an empty ModifierMap.
70 EXPECT_STREQ("", CreateFullXkbLayoutName("us", ModifierMap()).c_str());
71
72 // CreateFullXkbLayoutName should not accept an incomplete ModifierMap.
73 ModifierMap tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
74 tmp_map.pop_back();
75 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
76
77 // CreateFullXkbLayoutName should not accept redundant ModifierMaps.
78 tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
79 tmp_map.push_back(ModifierKeyPair(kSearchKey, kVoidKey)); // two search maps
80 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
81 tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
82 tmp_map.push_back(ModifierKeyPair(kLeftControlKey, kVoidKey)); // two ctrls
83 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
84 tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
85 tmp_map.push_back(ModifierKeyPair(kLeftAltKey, kVoidKey)); // two alts.
86 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
87
88 // CreateFullXkbLayoutName should not accept invalid ModifierMaps.
89 tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
90 tmp_map.push_back(ModifierKeyPair(kVoidKey, kSearchKey)); // can't remap void
91 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
92 tmp_map = GetMap(kVoidKey, kVoidKey, kVoidKey);
93 tmp_map.push_back(ModifierKeyPair(kCapsLockKey, kSearchKey)); // ditto
94 EXPECT_STREQ("", CreateFullXkbLayoutName("us", tmp_map).c_str());
95
96 // CreateFullXkbLayoutName can remap Search/Ctrl/Alt to CapsLock.
97 EXPECT_STREQ("us+chromeos(capslock_disabled_disabled)",
98 CreateFullXkbLayoutName(
99 "us",
100 GetMap(kCapsLockKey, kVoidKey, kVoidKey)).c_str());
101 EXPECT_STREQ("us+chromeos(disabled_capslock_disabled)",
102 CreateFullXkbLayoutName(
103 "us",
104 GetMap(kVoidKey, kCapsLockKey, kVoidKey)).c_str());
105 EXPECT_STREQ("us+chromeos(disabled_disabled_capslock)",
106 CreateFullXkbLayoutName(
107 "us",
108 GetMap(kVoidKey, kVoidKey, kCapsLockKey)).c_str());
109
110 // CreateFullXkbLayoutName should not accept non-alphanumeric characters
111 // except "()-_".
112 EXPECT_STREQ("", CreateFullXkbLayoutName(
113 "us!", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
114 EXPECT_STREQ("", CreateFullXkbLayoutName(
115 "us; /bin/sh", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
116 EXPECT_STREQ("ab-c_12+chromeos(disabled_disabled_disabled),us",
117 CreateFullXkbLayoutName(
118 "ab-c_12",
119 GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
120
121 // CreateFullXkbLayoutName should not accept upper-case ascii characters.
122 EXPECT_STREQ("", CreateFullXkbLayoutName(
123 "US", GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
124
125 // CreateFullXkbLayoutName should accept lower-case ascii characters.
126 for (int c = 'a'; c <= 'z'; ++c) {
127 EXPECT_STRNE("", CreateFullXkbLayoutName(
128 std::string(3, c),
129 GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
130 }
131
132 // CreateFullXkbLayoutName should accept numbers.
133 for (int c = '0'; c <= '9'; ++c) {
134 EXPECT_STRNE("", CreateFullXkbLayoutName(
135 std::string(3, c),
136 GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
137 }
138
139 // CreateFullXkbLayoutName should accept a layout with a variant name.
140 EXPECT_STREQ("us(dvorak)+chromeos(disabled_disabled_disabled)",
141 CreateFullXkbLayoutName(
142 "us(dvorak)",
143 GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
144 // TODO: Re-enable this when the stub is fixed to handle it.
145 // EXPECT_STREQ("gb(extd)+chromeos(disabled_disabled_disabled),us",
146 // CreateFullXkbLayoutName(
147 // "gb(extd)",
148 // GetMap(kVoidKey, kVoidKey, kVoidKey)).c_str());
149 EXPECT_STREQ("gb(extd)+", CreateFullXkbLayoutName(
150 "gb(extd)",
151 GetMap(kVoidKey, kVoidKey, kVoidKey)).substr(0, 9).c_str());
152 EXPECT_STREQ("jp+", CreateFullXkbLayoutName(
153 "jp", GetMap(kVoidKey, kVoidKey, kVoidKey)).substr(0, 3).c_str());
154
155 // When the layout name is not "us", the second layout should be added.
156 EXPECT_EQ(std::string::npos, CreateFullXkbLayoutName(
157 "us", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us"));
158 EXPECT_EQ(std::string::npos, CreateFullXkbLayoutName(
159 "us(dvorak)", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us"));
160 EXPECT_NE(std::string::npos, CreateFullXkbLayoutName(
161 "gb(extd)", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us"));
162 EXPECT_NE(std::string::npos, CreateFullXkbLayoutName(
163 "jp", GetMap(kVoidKey, kVoidKey, kVoidKey)).find(",us"));
164 }
165
166 // Tests if CreateFullXkbLayoutName and ExtractLayoutNameFromFullXkbLayoutName
167 // functions could handle all combinations of modifier remapping.
TEST(XKeyboardTest,TestCreateFullXkbLayoutNameModifierKeys)168 TEST(XKeyboardTest, TestCreateFullXkbLayoutNameModifierKeys) {
169 std::set<std::string> layouts;
170 for (int i = 0; i < static_cast<int>(kNumModifierKeys); ++i) {
171 for (int j = 0; j < static_cast<int>(kNumModifierKeys); ++j) {
172 for (int k = 0; k < static_cast<int>(kNumModifierKeys); ++k) {
173 const std::string layout = CreateFullXkbLayoutName(
174 "us", GetMap(ModifierKey(i), ModifierKey(j), ModifierKey(k)));
175 // CreateFullXkbLayoutName should succeed (i.e. should not return "".)
176 EXPECT_STREQ("us+", layout.substr(0, 3).c_str())
177 << "layout: " << layout;
178 // All 4*3*3 layouts should be different.
179 EXPECT_TRUE(layouts.insert(layout).second) << "layout: " << layout;
180 }
181 }
182 }
183 }
184
TEST(XKeyboardTest,TestSetCapsLockIsEnabled)185 TEST(XKeyboardTest, TestSetCapsLockIsEnabled) {
186 if (!DisplayAvailable()) {
187 return;
188 }
189 const bool initial_lock_state = CapsLockIsEnabled();
190 SetCapsLockEnabled(true);
191 EXPECT_TRUE(CapsLockIsEnabled());
192 SetCapsLockEnabled(false);
193 EXPECT_FALSE(CapsLockIsEnabled());
194 SetCapsLockEnabled(true);
195 EXPECT_TRUE(CapsLockIsEnabled());
196 SetCapsLockEnabled(false);
197 EXPECT_FALSE(CapsLockIsEnabled());
198 SetCapsLockEnabled(initial_lock_state);
199 }
200
TEST(XKeyboardTest,TestContainsModifierKeyAsReplacement)201 TEST(XKeyboardTest, TestContainsModifierKeyAsReplacement) {
202 EXPECT_FALSE(ContainsModifierKeyAsReplacement(
203 GetMap(kVoidKey, kVoidKey, kVoidKey), kCapsLockKey));
204 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
205 GetMap(kCapsLockKey, kVoidKey, kVoidKey), kCapsLockKey));
206 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
207 GetMap(kVoidKey, kCapsLockKey, kVoidKey), kCapsLockKey));
208 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
209 GetMap(kVoidKey, kVoidKey, kCapsLockKey), kCapsLockKey));
210 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
211 GetMap(kCapsLockKey, kCapsLockKey, kVoidKey), kCapsLockKey));
212 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
213 GetMap(kCapsLockKey, kCapsLockKey, kCapsLockKey), kCapsLockKey));
214 EXPECT_TRUE(ContainsModifierKeyAsReplacement(
215 GetMap(kSearchKey, kVoidKey, kVoidKey), kSearchKey));
216 }
217
218 } // namespace input_method
219 } // namespace chromeos
220