1 // Copyright (c) 2012 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.
5 #include "chrome/browser/chromeos/input_method/input_method_util.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chromeos/ime/extension_ime_util.h"
13 #include "chromeos/ime/fake_input_method_delegate.h"
14 #include "chromeos/ime/input_method_manager.h"
15 #include "chromeos/ime/input_method_whitelist.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/base/l10n/l10n_util.h"
19 using base::ASCIIToUTF16;
23 extern const char* kExtensionImePrefix;
25 namespace input_method {
29 const char pinyin_ime_id[] = "zh-t-i0-pinyin";
30 const char zhuyin_ime_id[] = "zh-hant-t-i0-und";
32 class TestableInputMethodUtil : public InputMethodUtil {
34 explicit TestableInputMethodUtil(InputMethodDelegate* delegate,
35 scoped_ptr<InputMethodDescriptors> methods)
36 : InputMethodUtil(delegate) {
37 ResetInputMethods(*methods);
39 // Change access rights.
40 using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal;
41 using InputMethodUtil::GetKeyboardLayoutName;
46 class InputMethodUtilTest : public testing::Test {
49 : util_(&delegate_, whitelist_.GetSupportedInputMethods()) {
50 delegate_.set_get_localized_string_callback(
51 base::Bind(&l10n_util::GetStringUTF16));
52 delegate_.set_get_display_language_name_callback(
53 base::Bind(&InputMethodUtilTest::GetDisplayLanguageName));
56 virtual void SetUp() OVERRIDE {
57 InputMethodDescriptors input_methods;
59 std::vector<std::string> layouts;
60 std::vector<std::string> languages;
61 layouts.push_back("us");
62 languages.push_back("zh-CN");
64 InputMethodDescriptor pinyin_ime(Id(pinyin_ime_id),
65 "Pinyin input for testing",
72 input_methods.push_back(pinyin_ime);
75 languages.push_back("zh-TW");
76 InputMethodDescriptor zhuyin_ime(zhuyin_ime_id,
77 "Zhuyin input for testing",
84 input_methods.push_back(zhuyin_ime);
86 util_.InitXkbInputMethodsForTesting();
87 util_.AppendInputMethods(input_methods);
90 std::string Id(const std::string& id) {
91 return extension_ime_util::GetInputMethodIDByEngineID(id);
94 InputMethodDescriptor GetDesc(const std::string& id,
95 const std::string& raw_layout,
96 const std::string& language_code,
97 const std::string& indicator) {
98 std::vector<std::string> layouts;
99 layouts.push_back(raw_layout);
100 std::vector<std::string> languages;
101 languages.push_back(language_code);
102 return InputMethodDescriptor(Id(id),
104 indicator, // Short name used for indicator.
108 GURL(), // options page url
109 GURL()); // input view page url
112 static base::string16 GetDisplayLanguageName(const std::string& language_code) {
113 return l10n_util::GetDisplayNameForLocale(language_code, "en", true);
116 FakeInputMethodDelegate delegate_;
117 InputMethodWhitelist whitelist_;
118 TestableInputMethodUtil util_;
121 TEST_F(InputMethodUtilTest, GetInputMethodShortNameTest) {
122 // Test invalid cases. Two-letter language code should be returned.
124 InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx", "");
125 // Upper-case string of the unknown language code, "xx", should be returned.
126 EXPECT_EQ(ASCIIToUTF16("XX"), util_.GetInputMethodShortName(desc));
129 // Test special cases.
131 InputMethodDescriptor desc =
132 GetDesc("xkb:us:dvorak:eng", "us", "en-US", "DV");
133 EXPECT_EQ(ASCIIToUTF16("DV"), util_.GetInputMethodShortName(desc));
136 InputMethodDescriptor desc =
137 GetDesc("xkb:us:colemak:eng", "us", "en-US", "CO");
138 EXPECT_EQ(ASCIIToUTF16("CO"), util_.GetInputMethodShortName(desc));
141 InputMethodDescriptor desc =
142 GetDesc("xkb:us:altgr-intl:eng", "us", "en-US", "EXTD");
143 EXPECT_EQ(ASCIIToUTF16("EXTD"), util_.GetInputMethodShortName(desc));
146 InputMethodDescriptor desc =
147 GetDesc("xkb:us:intl:eng", "us", "en-US", "INTL");
148 EXPECT_EQ(ASCIIToUTF16("INTL"), util_.GetInputMethodShortName(desc));
151 InputMethodDescriptor desc =
152 GetDesc("xkb:de:neo:ger", "de(neo)", "de", "NEO");
153 EXPECT_EQ(ASCIIToUTF16("NEO"), util_.GetInputMethodShortName(desc));
156 InputMethodDescriptor desc =
157 GetDesc("xkb:es:cat:cat", "es(cat)", "ca", "CAS");
158 EXPECT_EQ(ASCIIToUTF16("CAS"), util_.GetInputMethodShortName(desc));
161 InputMethodDescriptor desc =
162 GetDesc(pinyin_ime_id, "us", "zh-CN", "");
163 EXPECT_EQ(base::UTF8ToUTF16("\xe6\x8b\xbc"),
164 util_.GetInputMethodShortName(desc));
167 InputMethodDescriptor desc = GetDesc(zhuyin_ime_id, "us", "zh-TW", "");
168 EXPECT_EQ(base::UTF8ToUTF16("\xE6\xB3\xA8"),
169 util_.GetInputMethodShortName(desc));
173 TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) {
175 // input methods with medium name equal to short name
176 const char * input_method_id[] = {
177 "xkb:us:altgr-intl:eng",
180 "xkb:us:colemak:eng",
185 const int len = ARRAYSIZE_UNSAFE(input_method_id);
186 for (int i=0; i<len; ++i) {
187 InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "", "");
188 base::string16 medium_name = util_.GetInputMethodMediumName(desc);
189 base::string16 short_name = util_.GetInputMethodShortName(desc);
190 EXPECT_EQ(medium_name,short_name);
194 // input methods with medium name not equal to short name
195 const char * input_method_id[] = {
199 const int len = ARRAYSIZE_UNSAFE(input_method_id);
200 for (int i=0; i<len; ++i) {
201 InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "", "");
202 base::string16 medium_name = util_.GetInputMethodMediumName(desc);
203 base::string16 short_name = util_.GetInputMethodShortName(desc);
204 EXPECT_NE(medium_name,short_name);
209 TEST_F(InputMethodUtilTest, GetInputMethodLongNameTest) {
210 // For most languages input method or keyboard layout name is returned.
211 // See below for exceptions.
213 InputMethodDescriptor desc = GetDesc("xkb:jp::jpn", "jp", "ja", "");
214 EXPECT_EQ(ASCIIToUTF16("Japanese"),
215 util_.GetInputMethodLongName(desc));
218 InputMethodDescriptor desc =
219 GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US", "");
220 EXPECT_EQ(ASCIIToUTF16("US Dvorak"),
221 util_.GetInputMethodLongName(desc));
224 InputMethodDescriptor desc =
225 GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US", "");
226 EXPECT_EQ(ASCIIToUTF16("UK Dvorak"),
227 util_.GetInputMethodLongName(desc));
230 // For Dutch, French, German and Hindi,
231 // "language - keyboard layout" pair is returned.
233 InputMethodDescriptor desc = GetDesc("xkb:be::nld", "be", "nl", "");
234 EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian"),
235 util_.GetInputMethodLongName(desc));
238 InputMethodDescriptor desc = GetDesc("xkb:fr::fra", "fr", "fr", "");
239 EXPECT_EQ(ASCIIToUTF16("French - French"),
240 util_.GetInputMethodLongName(desc));
243 InputMethodDescriptor desc = GetDesc("xkb:be::fra", "be", "fr", "");
244 EXPECT_EQ(ASCIIToUTF16("French - Belgian"),
245 util_.GetInputMethodLongName(desc));
248 InputMethodDescriptor desc = GetDesc("xkb:de::ger", "de", "de", "");
249 EXPECT_EQ(ASCIIToUTF16("German - German"),
250 util_.GetInputMethodLongName(desc));
253 InputMethodDescriptor desc = GetDesc("xkb:be::ger", "be", "de", "");
254 EXPECT_EQ(ASCIIToUTF16("German - Belgian"),
255 util_.GetInputMethodLongName(desc));
259 InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx", "");
260 // You can safely ignore the "Resouce ID is not found for: invalid-id"
262 EXPECT_EQ(ASCIIToUTF16("invalid-id"),
263 util_.GetInputMethodLongName(desc));
267 TEST_F(InputMethodUtilTest, TestIsValidInputMethodId) {
268 EXPECT_TRUE(util_.IsValidInputMethodId(Id("xkb:us:colemak:eng")));
269 EXPECT_TRUE(util_.IsValidInputMethodId(Id(pinyin_ime_id)));
270 EXPECT_FALSE(util_.IsValidInputMethodId("unsupported-input-method"));
273 TEST_F(InputMethodUtilTest, TestIsKeyboardLayout) {
274 EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
275 EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(Id(pinyin_ime_id)));
278 TEST_F(InputMethodUtilTest, TestGetKeyboardLayoutName) {
280 EXPECT_EQ("", util_.GetKeyboardLayoutName("UNSUPPORTED_ID"));
282 // Supported cases (samples).
283 EXPECT_EQ("us", util_.GetKeyboardLayoutName(Id(pinyin_ime_id)));
284 EXPECT_EQ("es", util_.GetKeyboardLayoutName(Id("xkb:es::spa")));
285 EXPECT_EQ("es(cat)", util_.GetKeyboardLayoutName(Id("xkb:es:cat:cat")));
286 EXPECT_EQ("gb(extd)", util_.GetKeyboardLayoutName(Id("xkb:gb:extd:eng")));
287 EXPECT_EQ("us", util_.GetKeyboardLayoutName(Id("xkb:us::eng")));
288 EXPECT_EQ("us(dvorak)", util_.GetKeyboardLayoutName(Id("xkb:us:dvorak:eng")));
289 EXPECT_EQ("us(colemak)",
290 util_.GetKeyboardLayoutName(Id("xkb:us:colemak:eng")));
291 EXPECT_EQ("de(neo)", util_.GetKeyboardLayoutName(Id("xkb:de:neo:ger")));
294 TEST_F(InputMethodUtilTest, TestGetInputMethodDisplayNameFromId) {
296 util_.GetInputMethodDisplayNameFromId("xkb:us::eng"));
297 EXPECT_EQ("", util_.GetInputMethodDisplayNameFromId("nonexistent"));
300 TEST_F(InputMethodUtilTest, TestGetInputMethodDescriptorFromId) {
301 EXPECT_EQ(NULL, util_.GetInputMethodDescriptorFromId("non_existent"));
303 const InputMethodDescriptor* descriptor =
304 util_.GetInputMethodDescriptorFromId(Id(pinyin_ime_id));
305 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
306 EXPECT_EQ(Id(pinyin_ime_id), descriptor->id());
307 EXPECT_EQ("us", descriptor->GetPreferredKeyboardLayout());
308 // This used to be "zh" but now we have "zh-CN" in input_methods.h,
309 // hence this should be zh-CN now.
310 ASSERT_TRUE(!descriptor->language_codes().empty());
311 EXPECT_EQ("zh-CN", descriptor->language_codes().at(0));
314 TEST_F(InputMethodUtilTest, TestGetInputMethodIdsForLanguageCode) {
315 std::multimap<std::string, std::string> language_code_to_ids_map;
316 language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
317 language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
318 language_code_to_ids_map.insert(std::make_pair("ja", "xkb:jp:jpn"));
319 language_code_to_ids_map.insert(std::make_pair("fr", "xkb:fr:fra"));
321 std::vector<std::string> result;
322 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
323 language_code_to_ids_map, "ja", kAllInputMethods, &result));
324 EXPECT_EQ(3U, result.size());
325 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
326 language_code_to_ids_map, "ja", kKeyboardLayoutsOnly, &result));
327 ASSERT_EQ(1U, result.size());
328 EXPECT_EQ("xkb:jp:jpn", result[0]);
330 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
331 language_code_to_ids_map, "fr", kAllInputMethods, &result));
332 ASSERT_EQ(1U, result.size());
333 EXPECT_EQ("xkb:fr:fra", result[0]);
334 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
335 language_code_to_ids_map, "fr", kKeyboardLayoutsOnly, &result));
336 ASSERT_EQ(1U, result.size());
337 EXPECT_EQ("xkb:fr:fra", result[0]);
339 EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
340 language_code_to_ids_map, "invalid_lang", kAllInputMethods, &result));
341 EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
342 language_code_to_ids_map, "invalid_lang", kKeyboardLayoutsOnly, &result));
345 // US keyboard + English US UI = US keyboard only.
346 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_EnUs) {
347 const InputMethodDescriptor* descriptor =
348 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
349 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
350 std::vector<std::string> input_method_ids;
351 util_.GetFirstLoginInputMethodIds("en-US", *descriptor, &input_method_ids);
352 ASSERT_EQ(1U, input_method_ids.size());
353 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
356 // US keyboard + Chinese UI = US keyboard + Pinyin IME.
357 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Zh) {
358 const InputMethodDescriptor* descriptor =
359 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
360 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
361 std::vector<std::string> input_method_ids;
362 util_.GetFirstLoginInputMethodIds("zh-CN", *descriptor, &input_method_ids);
363 ASSERT_EQ(2U, input_method_ids.size());
364 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
365 EXPECT_EQ(Id(pinyin_ime_id), input_method_ids[1]); // Pinyin for US keybaord.
368 // US keyboard + Russian UI = US keyboard + Russsian keyboard
369 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Ru) {
370 const InputMethodDescriptor* descriptor =
371 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
372 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
373 std::vector<std::string> input_method_ids;
374 util_.GetFirstLoginInputMethodIds("ru", *descriptor, &input_method_ids);
375 ASSERT_EQ(2U, input_method_ids.size());
376 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
377 EXPECT_EQ(Id("xkb:ru::rus"), input_method_ids[1]); // Russian keyboard.
380 // US keyboard + Traditional Chinese = US keyboard + chewing.
381 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_ZhTw) {
382 const InputMethodDescriptor* descriptor =
383 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
384 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
385 std::vector<std::string> input_method_ids;
386 util_.GetFirstLoginInputMethodIds("zh-TW", *descriptor, &input_method_ids);
387 ASSERT_EQ(2U, input_method_ids.size());
388 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
389 EXPECT_EQ(Id(zhuyin_ime_id), input_method_ids[1]); // Chewing.
392 // US keyboard + Thai = US keyboard + kesmanee.
393 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Th) {
394 const InputMethodDescriptor* descriptor =
395 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
396 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
397 std::vector<std::string> input_method_ids;
398 util_.GetFirstLoginInputMethodIds("th", *descriptor, &input_method_ids);
399 ASSERT_EQ(2U, input_method_ids.size());
400 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
401 EXPECT_EQ(Id("vkd_th"), input_method_ids[1]); // Kesmanee.
404 // US keyboard + Vietnamese = US keyboard + TCVN6064.
405 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Vi) {
406 const InputMethodDescriptor* descriptor =
407 util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard.
408 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
409 std::vector<std::string> input_method_ids;
410 util_.GetFirstLoginInputMethodIds("vi", *descriptor, &input_method_ids);
411 ASSERT_EQ(2U, input_method_ids.size());
412 EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]);
413 EXPECT_EQ(Id("vkd_vi_tcvn"), input_method_ids[1]); // TCVN6064.
416 TEST_F(InputMethodUtilTest, TestGetLanguageCodesFromInputMethodIds) {
417 std::vector<std::string> input_method_ids;
418 input_method_ids.push_back(Id("xkb:us::eng")); // English US.
419 input_method_ids.push_back(Id("xkb:us:dvorak:eng")); // English US Dvorak.
420 input_method_ids.push_back(Id(pinyin_ime_id)); // Pinyin
421 input_method_ids.push_back(Id("xkb:fr::fra")); // French France.
422 std::vector<std::string> language_codes;
423 util_.GetLanguageCodesFromInputMethodIds(input_method_ids, &language_codes);
424 ASSERT_EQ(3U, language_codes.size());
425 EXPECT_EQ("en", language_codes[0]);
426 EXPECT_EQ("zh-CN", language_codes[1]);
427 EXPECT_EQ("fr", language_codes[2]);
430 // Test all supported descriptors to detect a typo in input_methods.txt.
431 TEST_F(InputMethodUtilTest, TestIBusInputMethodText) {
432 const std::map<std::string, InputMethodDescriptor>& id_to_descriptor =
433 util_.GetIdToDesciptorMapForTesting();
434 for (std::map<std::string, InputMethodDescriptor>::const_iterator it =
435 id_to_descriptor.begin(); it != id_to_descriptor.end(); ++it) {
436 const std::string language_code = it->second.language_codes().at(0);
437 const base::string16 display_name =
438 l10n_util::GetDisplayNameForLocale(language_code, "en", false);
439 // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
440 // allowed. See the text file for details.
441 EXPECT_TRUE(language_code == "fil" || language_code.length() == 2 ||
442 (language_code.length() == 5 && language_code[2] == '-'))
443 << "Invalid language code " << language_code;
444 EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code))
445 << "Invalid language code " << language_code;
446 EXPECT_FALSE(display_name.empty())
447 << "Invalid language code " << language_code;
448 // On error, GetDisplayNameForLocale() returns the |language_code| as-is.
449 EXPECT_NE(language_code, base::UTF16ToUTF8(display_name))
450 << "Invalid language code " << language_code;
454 } // namespace input_method
455 } // namespace chromeos