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/fake_input_method_delegate.h"
13 #include "chromeos/ime/input_method_manager.h"
14 #include "chromeos/ime/input_method_whitelist.h"
15 #include "grit/generated_resources.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/base/l10n/l10n_util.h"
21 extern const char* kExtensionImePrefix;
23 namespace input_method {
27 const char pinyin_ime_id[] =
28 "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin";
29 const char zhuyin_ime_id[] =
30 "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und";
32 class TestableInputMethodUtil : public InputMethodUtil {
34 explicit TestableInputMethodUtil(InputMethodDelegate* delegate,
35 scoped_ptr<InputMethodDescriptors> methods)
36 : InputMethodUtil(delegate, methods.Pass()) {
38 // Change access rights.
39 using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal;
40 using InputMethodUtil::GetKeyboardLayoutName;
41 using InputMethodUtil::ReloadInternalMaps;
42 using InputMethodUtil::supported_input_methods_;
47 class InputMethodUtilTest : public testing::Test {
50 : util_(&delegate_, whitelist_.GetSupportedInputMethods()) {
51 delegate_.set_get_localized_string_callback(
52 base::Bind(&l10n_util::GetStringUTF16));
53 delegate_.set_get_display_language_name_callback(
54 base::Bind(&InputMethodUtilTest::GetDisplayLanguageName));
57 virtual void SetUp() OVERRIDE {
58 InputMethodDescriptors input_methods;
60 std::vector<std::string> layouts;
61 std::vector<std::string> languages;
62 layouts.push_back("us");
63 languages.push_back("zh-CN");
65 InputMethodDescriptor pinyin_ime(pinyin_ime_id,
66 "Pinyin input for testing",
71 input_methods.push_back(pinyin_ime);
74 languages.push_back("zh-TW");
75 InputMethodDescriptor zhuyin_ime(zhuyin_ime_id,
76 "Zhuyin input for testing",
81 input_methods.push_back(zhuyin_ime);
83 util_.SetComponentExtensions(input_methods);
86 InputMethodDescriptor GetDesc(const std::string& id,
87 const std::string& raw_layout,
88 const std::string& language_code) {
89 std::vector<std::string> layouts;
90 layouts.push_back(raw_layout);
91 std::vector<std::string> languages;
92 languages.push_back(language_code);
93 return InputMethodDescriptor(id,
98 GURL()); // options page url
101 static string16 GetDisplayLanguageName(const std::string& language_code) {
102 return l10n_util::GetDisplayNameForLocale(language_code, "en", true);
105 FakeInputMethodDelegate delegate_;
106 InputMethodWhitelist whitelist_;
107 TestableInputMethodUtil util_;
110 TEST_F(InputMethodUtilTest, GetInputMethodShortNameTest) {
111 // Test normal cases. Two-letter language code should be returned.
113 InputMethodDescriptor desc = GetDesc("m17n:fa:isiri", // input method id
114 "us", // keyboard layout name
115 "fa"); // language name
116 EXPECT_EQ(ASCIIToUTF16("FA"), util_.GetInputMethodShortName(desc));
119 InputMethodDescriptor desc = GetDesc("mozc-hangul", "us", "ko");
120 EXPECT_EQ(UTF8ToUTF16("\xed\x95\x9c"),
121 util_.GetInputMethodShortName(desc));
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 = GetDesc("xkb:us:dvorak:eng", "us", "en-US");
132 EXPECT_EQ(ASCIIToUTF16("DV"), util_.GetInputMethodShortName(desc));
135 InputMethodDescriptor desc = GetDesc("xkb:us:colemak:eng", "us", "en-US");
136 EXPECT_EQ(ASCIIToUTF16("CO"), util_.GetInputMethodShortName(desc));
139 InputMethodDescriptor desc =
140 GetDesc("xkb:us:altgr-intl:eng", "us", "en-US");
141 EXPECT_EQ(ASCIIToUTF16("EXTD"), util_.GetInputMethodShortName(desc));
144 InputMethodDescriptor desc = GetDesc("xkb:us:intl:eng", "us", "en-US");
145 EXPECT_EQ(ASCIIToUTF16("INTL"), util_.GetInputMethodShortName(desc));
148 InputMethodDescriptor desc = GetDesc("xkb:de:neo:ger", "de(neo)", "de");
149 EXPECT_EQ(ASCIIToUTF16("NEO"), util_.GetInputMethodShortName(desc));
152 InputMethodDescriptor desc = GetDesc("xkb:es:cat:cat", "es(cat)", "ca");
153 EXPECT_EQ(ASCIIToUTF16("CAS"), util_.GetInputMethodShortName(desc));
156 InputMethodDescriptor desc = GetDesc(pinyin_ime_id, "us", "zh-CN");
157 EXPECT_EQ(UTF8ToUTF16("\xe6\x8b\xbc"),
158 util_.GetInputMethodShortName(desc));
161 InputMethodDescriptor desc = GetDesc(zhuyin_ime_id, "us", "zh-TW");
162 EXPECT_EQ(UTF8ToUTF16("\xE6\xB3\xA8"),
163 util_.GetInputMethodShortName(desc));
167 TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) {
169 // input methods with medium name equal to short name
170 const char * input_method_id[] = {
171 "xkb:us:altgr-intl:eng",
174 "xkb:us:colemak:eng",
180 const int len = ARRAYSIZE_UNSAFE(input_method_id);
181 for (int i=0; i<len; ++i) {
182 InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "");
183 string16 medium_name = util_.GetInputMethodMediumName(desc);
184 string16 short_name = util_.GetInputMethodShortName(desc);
185 EXPECT_EQ(medium_name,short_name);
189 // input methods with medium name not equal to short name
190 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 string16 medium_name = util_.GetInputMethodMediumName(desc);
203 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("m17n:fa:isiri", "us", "fa");
214 EXPECT_EQ(ASCIIToUTF16("Persian input method (ISIRI 2901 layout)"),
215 util_.GetInputMethodLongName(desc));
218 InputMethodDescriptor desc = GetDesc("mozc-hangul", "us", "ko");
219 EXPECT_EQ(ASCIIToUTF16("Korean input method"),
220 util_.GetInputMethodLongName(desc));
223 InputMethodDescriptor desc = GetDesc("m17n:vi:tcvn", "us", "vi");
224 EXPECT_EQ(ASCIIToUTF16("Vietnamese input method (TCVN6064)"),
225 util_.GetInputMethodLongName(desc));
228 InputMethodDescriptor desc = GetDesc("xkb:jp::jpn", "jp", "ja");
229 EXPECT_EQ(ASCIIToUTF16("Japanese keyboard"),
230 util_.GetInputMethodLongName(desc));
233 InputMethodDescriptor desc =
234 GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US");
235 EXPECT_EQ(ASCIIToUTF16("US Dvorak keyboard"),
236 util_.GetInputMethodLongName(desc));
239 InputMethodDescriptor desc =
240 GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US");
241 EXPECT_EQ(ASCIIToUTF16("UK Dvorak keyboard"),
242 util_.GetInputMethodLongName(desc));
245 // For Arabic, Dutch, French, German and Hindi,
246 // "language - keyboard layout" pair is returned.
248 InputMethodDescriptor desc = GetDesc("m17n:ar:kbd", "us", "ar");
249 EXPECT_EQ(ASCIIToUTF16("Arabic - Standard input method"),
250 util_.GetInputMethodLongName(desc));
253 InputMethodDescriptor desc = GetDesc("xkb:be::nld", "be", "nl");
254 EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian keyboard"),
255 util_.GetInputMethodLongName(desc));
258 InputMethodDescriptor desc = GetDesc("xkb:fr::fra", "fr", "fr");
259 EXPECT_EQ(ASCIIToUTF16("French - French keyboard"),
260 util_.GetInputMethodLongName(desc));
263 InputMethodDescriptor desc = GetDesc("xkb:be::fra", "be", "fr");
264 EXPECT_EQ(ASCIIToUTF16("French - Belgian keyboard"),
265 util_.GetInputMethodLongName(desc));
268 InputMethodDescriptor desc = GetDesc("xkb:de::ger", "de", "de");
269 EXPECT_EQ(ASCIIToUTF16("German - German keyboard"),
270 util_.GetInputMethodLongName(desc));
273 InputMethodDescriptor desc = GetDesc("xkb:be::ger", "be", "de");
274 EXPECT_EQ(ASCIIToUTF16("German - Belgian keyboard"),
275 util_.GetInputMethodLongName(desc));
278 InputMethodDescriptor desc = GetDesc("m17n:hi:itrans", "us", "hi");
279 EXPECT_EQ(ASCIIToUTF16("Hindi - Standard input method"),
280 util_.GetInputMethodLongName(desc));
284 InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx");
285 // You can safely ignore the "Resouce ID is not found for: invalid-id"
287 EXPECT_EQ(ASCIIToUTF16("invalid-id"),
288 util_.GetInputMethodLongName(desc));
292 TEST_F(InputMethodUtilTest, TestIsValidInputMethodId) {
293 EXPECT_TRUE(util_.IsValidInputMethodId("xkb:us:colemak:eng"));
294 EXPECT_TRUE(util_.IsValidInputMethodId(pinyin_ime_id));
295 EXPECT_FALSE(util_.IsValidInputMethodId("unsupported-input-method"));
298 TEST_F(InputMethodUtilTest, TestIsKeyboardLayout) {
299 EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
300 EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(pinyin_ime_id));
303 TEST_F(InputMethodUtilTest, TestGetKeyboardLayoutName) {
305 EXPECT_EQ("", util_.GetKeyboardLayoutName("UNSUPPORTED_ID"));
307 // Supported cases (samples).
308 EXPECT_EQ("us", util_.GetKeyboardLayoutName(pinyin_ime_id));
309 EXPECT_EQ("es", util_.GetKeyboardLayoutName("xkb:es::spa"));
310 EXPECT_EQ("es(cat)", util_.GetKeyboardLayoutName("xkb:es:cat:cat"));
311 EXPECT_EQ("gb(extd)", util_.GetKeyboardLayoutName("xkb:gb:extd:eng"));
312 EXPECT_EQ("us", util_.GetKeyboardLayoutName("xkb:us::eng"));
313 EXPECT_EQ("us(dvorak)", util_.GetKeyboardLayoutName("xkb:us:dvorak:eng"));
314 EXPECT_EQ("us(colemak)", util_.GetKeyboardLayoutName("xkb:us:colemak:eng"));
315 EXPECT_EQ("de(neo)", util_.GetKeyboardLayoutName("xkb:de:neo:ger"));
318 TEST_F(InputMethodUtilTest, TestGetLanguageCodeFromInputMethodId) {
319 // Make sure that the -CN is added properly.
320 EXPECT_EQ("zh-CN", util_.GetLanguageCodeFromInputMethodId(pinyin_ime_id));
323 TEST_F(InputMethodUtilTest, TestGetInputMethodDisplayNameFromId) {
324 EXPECT_EQ("US keyboard",
325 util_.GetInputMethodDisplayNameFromId("xkb:us::eng"));
326 EXPECT_EQ("", util_.GetInputMethodDisplayNameFromId("nonexistent"));
329 TEST_F(InputMethodUtilTest, TestGetInputMethodDescriptorFromId) {
330 EXPECT_EQ(NULL, util_.GetInputMethodDescriptorFromId("non_existent"));
332 const InputMethodDescriptor* descriptor =
333 util_.GetInputMethodDescriptorFromId(pinyin_ime_id);
334 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
335 EXPECT_EQ(pinyin_ime_id, descriptor->id());
336 EXPECT_EQ("us", descriptor->GetPreferredKeyboardLayout());
337 // This used to be "zh" but now we have "zh-CN" in input_methods.h,
338 // hence this should be zh-CN now.
339 ASSERT_TRUE(!descriptor->language_codes().empty());
340 EXPECT_EQ("zh-CN", descriptor->language_codes().at(0));
343 TEST_F(InputMethodUtilTest, TestGetInputMethodIdsForLanguageCode) {
344 std::multimap<std::string, std::string> language_code_to_ids_map;
345 language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
346 language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
347 language_code_to_ids_map.insert(std::make_pair("ja", "xkb:jp:jpn"));
348 language_code_to_ids_map.insert(std::make_pair("fr", "xkb:fr:fra"));
350 std::vector<std::string> result;
351 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
352 language_code_to_ids_map, "ja", kAllInputMethods, &result));
353 EXPECT_EQ(3U, result.size());
354 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
355 language_code_to_ids_map, "ja", kKeyboardLayoutsOnly, &result));
356 ASSERT_EQ(1U, result.size());
357 EXPECT_EQ("xkb:jp:jpn", result[0]);
359 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
360 language_code_to_ids_map, "fr", kAllInputMethods, &result));
361 ASSERT_EQ(1U, result.size());
362 EXPECT_EQ("xkb:fr:fra", result[0]);
363 EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
364 language_code_to_ids_map, "fr", kKeyboardLayoutsOnly, &result));
365 ASSERT_EQ(1U, result.size());
366 EXPECT_EQ("xkb:fr:fra", result[0]);
368 EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
369 language_code_to_ids_map, "invalid_lang", kAllInputMethods, &result));
370 EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
371 language_code_to_ids_map, "invalid_lang", kKeyboardLayoutsOnly, &result));
374 // US keyboard + English US UI = US keyboard only.
375 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_EnUs) {
376 const InputMethodDescriptor* descriptor =
377 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
378 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
379 std::vector<std::string> input_method_ids;
380 util_.GetFirstLoginInputMethodIds("en-US", *descriptor, &input_method_ids);
381 ASSERT_EQ(1U, input_method_ids.size());
382 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
385 // US keyboard + Chinese UI = US keyboard + Pinyin IME.
386 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Zh) {
387 const InputMethodDescriptor* descriptor =
388 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
389 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
390 std::vector<std::string> input_method_ids;
391 util_.GetFirstLoginInputMethodIds("zh-CN", *descriptor, &input_method_ids);
392 ASSERT_EQ(2U, input_method_ids.size());
393 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
394 EXPECT_EQ(pinyin_ime_id, input_method_ids[1]); // Pinyin for US keybaord.
397 // US keyboard + Russian UI = US keyboard + Russsian keyboard
398 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Ru) {
399 const InputMethodDescriptor* descriptor =
400 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
401 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
402 std::vector<std::string> input_method_ids;
403 util_.GetFirstLoginInputMethodIds("ru", *descriptor, &input_method_ids);
404 ASSERT_EQ(2U, input_method_ids.size());
405 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
406 EXPECT_EQ("xkb:ru::rus", input_method_ids[1]); // Russian keyboard.
409 // US keyboard + Traditional Chinese = US keyboard + chewing.
410 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_ZhTw) {
411 const InputMethodDescriptor* descriptor =
412 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
413 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
414 std::vector<std::string> input_method_ids;
415 util_.GetFirstLoginInputMethodIds("zh-TW", *descriptor, &input_method_ids);
416 ASSERT_EQ(2U, input_method_ids.size());
417 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
418 EXPECT_EQ(zhuyin_ime_id, input_method_ids[1]); // Chewing.
421 // US keyboard + Thai = US keyboard + kesmanee.
422 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Th) {
423 const InputMethodDescriptor* descriptor =
424 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
425 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
426 std::vector<std::string> input_method_ids;
427 util_.GetFirstLoginInputMethodIds("th", *descriptor, &input_method_ids);
428 ASSERT_EQ(2U, input_method_ids.size());
429 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
430 EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th",
431 input_method_ids[1]); // Kesmanee.
434 // US keyboard + Vietnamese = US keyboard + TCVN6064.
435 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Vi) {
436 const InputMethodDescriptor* descriptor =
437 util_.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
438 ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile.
439 std::vector<std::string> input_method_ids;
440 util_.GetFirstLoginInputMethodIds("vi", *descriptor, &input_method_ids);
441 ASSERT_EQ(2U, input_method_ids.size());
442 EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
443 EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn",
444 input_method_ids[1]); // TCVN6064.
447 TEST_F(InputMethodUtilTest, TestGetLanguageCodesFromInputMethodIds) {
448 std::vector<std::string> input_method_ids;
449 input_method_ids.push_back("xkb:us::eng"); // English US.
450 input_method_ids.push_back("xkb:us:dvorak:eng"); // English US Dvorak.
451 input_method_ids.push_back(pinyin_ime_id); // Pinyin
452 input_method_ids.push_back("xkb:fr::fra"); // French France.
453 std::vector<std::string> language_codes;
454 util_.GetLanguageCodesFromInputMethodIds(input_method_ids, &language_codes);
455 ASSERT_EQ(3U, language_codes.size());
456 EXPECT_EQ("en-US", language_codes[0]);
457 EXPECT_EQ("zh-CN", language_codes[1]);
458 EXPECT_EQ("fr", language_codes[2]);
461 // Test all supported descriptors to detect a typo in ibus_input_methods.txt.
462 TEST_F(InputMethodUtilTest, TestIBusInputMethodText) {
463 for (size_t i = 0; i < util_.supported_input_methods_->size(); ++i) {
464 const std::string language_code =
465 util_.supported_input_methods_->at(i).language_codes().at(0);
466 const string16 display_name =
467 l10n_util::GetDisplayNameForLocale(language_code, "en", false);
468 // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
469 // allowed. See the text file for details.
470 EXPECT_TRUE(language_code.length() == 2 ||
471 (language_code.length() == 5 && language_code[2] == '-'))
472 << "Invalid language code " << language_code;
473 EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code))
474 << "Invalid language code " << language_code;
475 EXPECT_FALSE(display_name.empty())
476 << "Invalid language code " << language_code;
477 // On error, GetDisplayNameForLocale() returns the |language_code| as-is.
478 EXPECT_NE(language_code, UTF16ToUTF8(display_name))
479 << "Invalid language code " << language_code;
483 } // namespace input_method
484 } // namespace chromeos