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"
19 using base::ASCIIToUTF16
;
23 extern const char* kExtensionImePrefix
;
25 namespace input_method
{
29 const char pinyin_ime_id
[] =
30 "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin";
31 const char zhuyin_ime_id
[] =
32 "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und";
34 class TestableInputMethodUtil
: public InputMethodUtil
{
36 explicit TestableInputMethodUtil(InputMethodDelegate
* delegate
,
37 scoped_ptr
<InputMethodDescriptors
> methods
)
38 : InputMethodUtil(delegate
, methods
.Pass()) {
40 // Change access rights.
41 using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal
;
42 using InputMethodUtil::GetKeyboardLayoutName
;
43 using InputMethodUtil::ReloadInternalMaps
;
44 using InputMethodUtil::supported_input_methods_
;
49 class InputMethodUtilTest
: public testing::Test
{
52 : util_(&delegate_
, whitelist_
.GetSupportedInputMethods()) {
53 delegate_
.set_get_localized_string_callback(
54 base::Bind(&l10n_util::GetStringUTF16
));
55 delegate_
.set_get_display_language_name_callback(
56 base::Bind(&InputMethodUtilTest::GetDisplayLanguageName
));
59 virtual void SetUp() OVERRIDE
{
60 InputMethodDescriptors input_methods
;
62 std::vector
<std::string
> layouts
;
63 std::vector
<std::string
> languages
;
64 layouts
.push_back("us");
65 languages
.push_back("zh-CN");
67 InputMethodDescriptor
pinyin_ime(pinyin_ime_id
,
68 "Pinyin input for testing",
74 input_methods
.push_back(pinyin_ime
);
77 languages
.push_back("zh-TW");
78 InputMethodDescriptor
zhuyin_ime(zhuyin_ime_id
,
79 "Zhuyin input for testing",
85 input_methods
.push_back(zhuyin_ime
);
87 util_
.SetComponentExtensions(input_methods
);
90 InputMethodDescriptor
GetDesc(const std::string
& id
,
91 const std::string
& raw_layout
,
92 const std::string
& language_code
) {
93 std::vector
<std::string
> layouts
;
94 layouts
.push_back(raw_layout
);
95 std::vector
<std::string
> languages
;
96 languages
.push_back(language_code
);
97 return InputMethodDescriptor(id
,
102 GURL(), // options page url
103 GURL()); // input view page url
106 static base::string16
GetDisplayLanguageName(const std::string
& language_code
) {
107 return l10n_util::GetDisplayNameForLocale(language_code
, "en", true);
110 FakeInputMethodDelegate delegate_
;
111 InputMethodWhitelist whitelist_
;
112 TestableInputMethodUtil util_
;
115 TEST_F(InputMethodUtilTest
, GetInputMethodShortNameTest
) {
116 // Test normal cases. Two-letter language code should be returned.
118 InputMethodDescriptor desc
= GetDesc("m17n:fa:isiri", // input method id
119 "us", // keyboard layout name
120 "fa"); // language name
121 EXPECT_EQ(ASCIIToUTF16("FA"), util_
.GetInputMethodShortName(desc
));
124 InputMethodDescriptor desc
= GetDesc("mozc-hangul", "us", "ko");
125 EXPECT_EQ(base::UTF8ToUTF16("\xed\x95\x9c"),
126 util_
.GetInputMethodShortName(desc
));
129 InputMethodDescriptor desc
= GetDesc("invalid-id", "us", "xx");
130 // Upper-case string of the unknown language code, "xx", should be returned.
131 EXPECT_EQ(ASCIIToUTF16("XX"), util_
.GetInputMethodShortName(desc
));
134 // Test special cases.
136 InputMethodDescriptor desc
= GetDesc("xkb:us:dvorak:eng", "us", "en-US");
137 EXPECT_EQ(ASCIIToUTF16("DV"), util_
.GetInputMethodShortName(desc
));
140 InputMethodDescriptor desc
= GetDesc("xkb:us:colemak:eng", "us", "en-US");
141 EXPECT_EQ(ASCIIToUTF16("CO"), util_
.GetInputMethodShortName(desc
));
144 InputMethodDescriptor desc
=
145 GetDesc("xkb:us:altgr-intl:eng", "us", "en-US");
146 EXPECT_EQ(ASCIIToUTF16("EXTD"), util_
.GetInputMethodShortName(desc
));
149 InputMethodDescriptor desc
= GetDesc("xkb:us:intl:eng", "us", "en-US");
150 EXPECT_EQ(ASCIIToUTF16("INTL"), util_
.GetInputMethodShortName(desc
));
153 InputMethodDescriptor desc
= GetDesc("xkb:de:neo:ger", "de(neo)", "de");
154 EXPECT_EQ(ASCIIToUTF16("NEO"), util_
.GetInputMethodShortName(desc
));
157 InputMethodDescriptor desc
= GetDesc("xkb:es:cat:cat", "es(cat)", "ca");
158 EXPECT_EQ(ASCIIToUTF16("CAS"), util_
.GetInputMethodShortName(desc
));
161 InputMethodDescriptor desc
= GetDesc(pinyin_ime_id
, "us", "zh-CN");
162 EXPECT_EQ(base::UTF8ToUTF16("\xe6\x8b\xbc"),
163 util_
.GetInputMethodShortName(desc
));
166 InputMethodDescriptor desc
= GetDesc(zhuyin_ime_id
, "us", "zh-TW");
167 EXPECT_EQ(base::UTF8ToUTF16("\xE6\xB3\xA8"),
168 util_
.GetInputMethodShortName(desc
));
172 TEST_F(InputMethodUtilTest
, GetInputMethodMediumNameTest
) {
174 // input methods with medium name equal to short name
175 const char * input_method_id
[] = {
176 "xkb:us:altgr-intl:eng",
179 "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
[] = {
204 const int len
= ARRAYSIZE_UNSAFE(input_method_id
);
205 for (int i
=0; i
<len
; ++i
) {
206 InputMethodDescriptor desc
= GetDesc(input_method_id
[i
], "", "");
207 base::string16 medium_name
= util_
.GetInputMethodMediumName(desc
);
208 base::string16 short_name
= util_
.GetInputMethodShortName(desc
);
209 EXPECT_NE(medium_name
,short_name
);
214 TEST_F(InputMethodUtilTest
, GetInputMethodLongNameTest
) {
215 // For most languages input method or keyboard layout name is returned.
216 // See below for exceptions.
218 InputMethodDescriptor desc
= GetDesc("m17n:fa:isiri", "us", "fa");
219 EXPECT_EQ(ASCIIToUTF16("Persian input method (ISIRI 2901 layout)"),
220 util_
.GetInputMethodLongName(desc
));
223 InputMethodDescriptor desc
= GetDesc("mozc-hangul", "us", "ko");
224 EXPECT_EQ(ASCIIToUTF16("Korean input method"),
225 util_
.GetInputMethodLongName(desc
));
228 InputMethodDescriptor desc
= GetDesc("m17n:vi:tcvn", "us", "vi");
229 EXPECT_EQ(ASCIIToUTF16("Vietnamese input method (TCVN6064)"),
230 util_
.GetInputMethodLongName(desc
));
233 InputMethodDescriptor desc
= GetDesc("xkb:jp::jpn", "jp", "ja");
234 EXPECT_EQ(ASCIIToUTF16("Japanese keyboard"),
235 util_
.GetInputMethodLongName(desc
));
238 InputMethodDescriptor desc
=
239 GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US");
240 EXPECT_EQ(ASCIIToUTF16("US Dvorak keyboard"),
241 util_
.GetInputMethodLongName(desc
));
244 InputMethodDescriptor desc
=
245 GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US");
246 EXPECT_EQ(ASCIIToUTF16("UK Dvorak keyboard"),
247 util_
.GetInputMethodLongName(desc
));
250 // For Arabic, Dutch, French, German and Hindi,
251 // "language - keyboard layout" pair is returned.
253 InputMethodDescriptor desc
= GetDesc("m17n:ar:kbd", "us", "ar");
254 EXPECT_EQ(ASCIIToUTF16("Arabic - Standard input method"),
255 util_
.GetInputMethodLongName(desc
));
258 InputMethodDescriptor desc
= GetDesc("xkb:be::nld", "be", "nl");
259 EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian keyboard"),
260 util_
.GetInputMethodLongName(desc
));
263 InputMethodDescriptor desc
= GetDesc("xkb:fr::fra", "fr", "fr");
264 EXPECT_EQ(ASCIIToUTF16("French - French keyboard"),
265 util_
.GetInputMethodLongName(desc
));
268 InputMethodDescriptor desc
= GetDesc("xkb:be::fra", "be", "fr");
269 EXPECT_EQ(ASCIIToUTF16("French - Belgian keyboard"),
270 util_
.GetInputMethodLongName(desc
));
273 InputMethodDescriptor desc
= GetDesc("xkb:de::ger", "de", "de");
274 EXPECT_EQ(ASCIIToUTF16("German - German keyboard"),
275 util_
.GetInputMethodLongName(desc
));
278 InputMethodDescriptor desc
= GetDesc("xkb:be::ger", "be", "de");
279 EXPECT_EQ(ASCIIToUTF16("German - Belgian keyboard"),
280 util_
.GetInputMethodLongName(desc
));
283 InputMethodDescriptor desc
= GetDesc("m17n:hi:itrans", "us", "hi");
284 EXPECT_EQ(ASCIIToUTF16("Hindi - Standard input method"),
285 util_
.GetInputMethodLongName(desc
));
289 InputMethodDescriptor desc
= GetDesc("invalid-id", "us", "xx");
290 // You can safely ignore the "Resouce ID is not found for: invalid-id"
292 EXPECT_EQ(ASCIIToUTF16("invalid-id"),
293 util_
.GetInputMethodLongName(desc
));
297 TEST_F(InputMethodUtilTest
, TestIsValidInputMethodId
) {
298 EXPECT_TRUE(util_
.IsValidInputMethodId("xkb:us:colemak:eng"));
299 EXPECT_TRUE(util_
.IsValidInputMethodId(pinyin_ime_id
));
300 EXPECT_FALSE(util_
.IsValidInputMethodId("unsupported-input-method"));
303 TEST_F(InputMethodUtilTest
, TestIsKeyboardLayout
) {
304 EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
305 EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(pinyin_ime_id
));
308 TEST_F(InputMethodUtilTest
, TestGetKeyboardLayoutName
) {
310 EXPECT_EQ("", util_
.GetKeyboardLayoutName("UNSUPPORTED_ID"));
312 // Supported cases (samples).
313 EXPECT_EQ("us", util_
.GetKeyboardLayoutName(pinyin_ime_id
));
314 EXPECT_EQ("es", util_
.GetKeyboardLayoutName("xkb:es::spa"));
315 EXPECT_EQ("es(cat)", util_
.GetKeyboardLayoutName("xkb:es:cat:cat"));
316 EXPECT_EQ("gb(extd)", util_
.GetKeyboardLayoutName("xkb:gb:extd:eng"));
317 EXPECT_EQ("us", util_
.GetKeyboardLayoutName("xkb:us::eng"));
318 EXPECT_EQ("us(dvorak)", util_
.GetKeyboardLayoutName("xkb:us:dvorak:eng"));
319 EXPECT_EQ("us(colemak)", util_
.GetKeyboardLayoutName("xkb:us:colemak:eng"));
320 EXPECT_EQ("de(neo)", util_
.GetKeyboardLayoutName("xkb:de:neo:ger"));
323 TEST_F(InputMethodUtilTest
, TestGetLanguageCodeFromInputMethodId
) {
324 // Make sure that the -CN is added properly.
325 EXPECT_EQ("zh-CN", util_
.GetLanguageCodeFromInputMethodId(pinyin_ime_id
));
328 TEST_F(InputMethodUtilTest
, TestGetInputMethodDisplayNameFromId
) {
329 EXPECT_EQ("US keyboard",
330 util_
.GetInputMethodDisplayNameFromId("xkb:us::eng"));
331 EXPECT_EQ("", util_
.GetInputMethodDisplayNameFromId("nonexistent"));
334 TEST_F(InputMethodUtilTest
, TestGetInputMethodDescriptorFromId
) {
335 EXPECT_EQ(NULL
, util_
.GetInputMethodDescriptorFromId("non_existent"));
337 const InputMethodDescriptor
* descriptor
=
338 util_
.GetInputMethodDescriptorFromId(pinyin_ime_id
);
339 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
340 EXPECT_EQ(pinyin_ime_id
, descriptor
->id());
341 EXPECT_EQ("us", descriptor
->GetPreferredKeyboardLayout());
342 // This used to be "zh" but now we have "zh-CN" in input_methods.h,
343 // hence this should be zh-CN now.
344 ASSERT_TRUE(!descriptor
->language_codes().empty());
345 EXPECT_EQ("zh-CN", descriptor
->language_codes().at(0));
348 TEST_F(InputMethodUtilTest
, TestGetInputMethodIdsForLanguageCode
) {
349 std::multimap
<std::string
, std::string
> language_code_to_ids_map
;
350 language_code_to_ids_map
.insert(std::make_pair("ja", pinyin_ime_id
));
351 language_code_to_ids_map
.insert(std::make_pair("ja", pinyin_ime_id
));
352 language_code_to_ids_map
.insert(std::make_pair("ja", "xkb:jp:jpn"));
353 language_code_to_ids_map
.insert(std::make_pair("fr", "xkb:fr:fra"));
355 std::vector
<std::string
> result
;
356 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
357 language_code_to_ids_map
, "ja", kAllInputMethods
, &result
));
358 EXPECT_EQ(3U, result
.size());
359 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
360 language_code_to_ids_map
, "ja", kKeyboardLayoutsOnly
, &result
));
361 ASSERT_EQ(1U, result
.size());
362 EXPECT_EQ("xkb:jp:jpn", result
[0]);
364 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
365 language_code_to_ids_map
, "fr", kAllInputMethods
, &result
));
366 ASSERT_EQ(1U, result
.size());
367 EXPECT_EQ("xkb:fr:fra", result
[0]);
368 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
369 language_code_to_ids_map
, "fr", kKeyboardLayoutsOnly
, &result
));
370 ASSERT_EQ(1U, result
.size());
371 EXPECT_EQ("xkb:fr:fra", result
[0]);
373 EXPECT_FALSE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
374 language_code_to_ids_map
, "invalid_lang", kAllInputMethods
, &result
));
375 EXPECT_FALSE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
376 language_code_to_ids_map
, "invalid_lang", kKeyboardLayoutsOnly
, &result
));
379 // US keyboard + English US UI = US keyboard only.
380 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_EnUs
) {
381 const InputMethodDescriptor
* descriptor
=
382 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
383 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
384 std::vector
<std::string
> input_method_ids
;
385 util_
.GetFirstLoginInputMethodIds("en-US", *descriptor
, &input_method_ids
);
386 ASSERT_EQ(1U, input_method_ids
.size());
387 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
390 // US keyboard + Chinese UI = US keyboard + Pinyin IME.
391 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Zh
) {
392 const InputMethodDescriptor
* descriptor
=
393 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
394 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
395 std::vector
<std::string
> input_method_ids
;
396 util_
.GetFirstLoginInputMethodIds("zh-CN", *descriptor
, &input_method_ids
);
397 ASSERT_EQ(2U, input_method_ids
.size());
398 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
399 EXPECT_EQ(pinyin_ime_id
, input_method_ids
[1]); // Pinyin for US keybaord.
402 // US keyboard + Russian UI = US keyboard + Russsian keyboard
403 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Ru
) {
404 const InputMethodDescriptor
* descriptor
=
405 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
406 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
407 std::vector
<std::string
> input_method_ids
;
408 util_
.GetFirstLoginInputMethodIds("ru", *descriptor
, &input_method_ids
);
409 ASSERT_EQ(2U, input_method_ids
.size());
410 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
411 EXPECT_EQ("xkb:ru::rus", input_method_ids
[1]); // Russian keyboard.
414 // US keyboard + Traditional Chinese = US keyboard + chewing.
415 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_ZhTw
) {
416 const InputMethodDescriptor
* descriptor
=
417 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
418 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
419 std::vector
<std::string
> input_method_ids
;
420 util_
.GetFirstLoginInputMethodIds("zh-TW", *descriptor
, &input_method_ids
);
421 ASSERT_EQ(2U, input_method_ids
.size());
422 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
423 EXPECT_EQ(zhuyin_ime_id
, input_method_ids
[1]); // Chewing.
426 // US keyboard + Thai = US keyboard + kesmanee.
427 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Th
) {
428 const InputMethodDescriptor
* descriptor
=
429 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
430 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
431 std::vector
<std::string
> input_method_ids
;
432 util_
.GetFirstLoginInputMethodIds("th", *descriptor
, &input_method_ids
);
433 ASSERT_EQ(2U, input_method_ids
.size());
434 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
435 EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th",
436 input_method_ids
[1]); // Kesmanee.
439 // US keyboard + Vietnamese = US keyboard + TCVN6064.
440 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Vi
) {
441 const InputMethodDescriptor
* descriptor
=
442 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
443 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
444 std::vector
<std::string
> input_method_ids
;
445 util_
.GetFirstLoginInputMethodIds("vi", *descriptor
, &input_method_ids
);
446 ASSERT_EQ(2U, input_method_ids
.size());
447 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
448 EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn",
449 input_method_ids
[1]); // TCVN6064.
452 TEST_F(InputMethodUtilTest
, TestGetLanguageCodesFromInputMethodIds
) {
453 std::vector
<std::string
> input_method_ids
;
454 input_method_ids
.push_back("xkb:us::eng"); // English US.
455 input_method_ids
.push_back("xkb:us:dvorak:eng"); // English US Dvorak.
456 input_method_ids
.push_back(pinyin_ime_id
); // Pinyin
457 input_method_ids
.push_back("xkb:fr::fra"); // French France.
458 std::vector
<std::string
> language_codes
;
459 util_
.GetLanguageCodesFromInputMethodIds(input_method_ids
, &language_codes
);
460 ASSERT_EQ(3U, language_codes
.size());
461 EXPECT_EQ("en-US", language_codes
[0]);
462 EXPECT_EQ("zh-CN", language_codes
[1]);
463 EXPECT_EQ("fr", language_codes
[2]);
466 // Test all supported descriptors to detect a typo in ibus_input_methods.txt.
467 TEST_F(InputMethodUtilTest
, TestIBusInputMethodText
) {
468 for (size_t i
= 0; i
< util_
.supported_input_methods_
->size(); ++i
) {
469 const std::string language_code
=
470 util_
.supported_input_methods_
->at(i
).language_codes().at(0);
471 const base::string16 display_name
=
472 l10n_util::GetDisplayNameForLocale(language_code
, "en", false);
473 // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
474 // allowed. See the text file for details.
475 EXPECT_TRUE(language_code
.length() == 2 ||
476 (language_code
.length() == 5 && language_code
[2] == '-'))
477 << "Invalid language code " << language_code
;
478 EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code
))
479 << "Invalid language code " << language_code
;
480 EXPECT_FALSE(display_name
.empty())
481 << "Invalid language code " << language_code
;
482 // On error, GetDisplayNameForLocale() returns the |language_code| as-is.
483 EXPECT_NE(language_code
, base::UTF16ToUTF8(display_name
))
484 << "Invalid language code " << language_code
;
488 } // namespace input_method
489 } // namespace chromeos