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";
33 const char kKeyboardExtId
[] =
34 #if defined(OFFICIAL_BUILD)
35 "habcdindjejkmepknlhkkloncjcpcnbf";
37 "jhffeifommiaekmbkkjlpmilogcfdohp";
40 class TestableInputMethodUtil
: public InputMethodUtil
{
42 explicit TestableInputMethodUtil(InputMethodDelegate
* delegate
,
43 scoped_ptr
<InputMethodDescriptors
> methods
)
44 : InputMethodUtil(delegate
, methods
.Pass()) {
46 // Change access rights.
47 using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal
;
48 using InputMethodUtil::GetKeyboardLayoutName
;
49 using InputMethodUtil::ReloadInternalMaps
;
50 using InputMethodUtil::supported_input_methods_
;
55 class InputMethodUtilTest
: public testing::Test
{
58 : util_(&delegate_
, whitelist_
.GetSupportedInputMethods()) {
59 delegate_
.set_get_localized_string_callback(
60 base::Bind(&l10n_util::GetStringUTF16
));
61 delegate_
.set_get_display_language_name_callback(
62 base::Bind(&InputMethodUtilTest::GetDisplayLanguageName
));
65 virtual void SetUp() OVERRIDE
{
66 InputMethodDescriptors input_methods
;
68 std::vector
<std::string
> layouts
;
69 std::vector
<std::string
> languages
;
70 layouts
.push_back("us");
71 languages
.push_back("zh-CN");
73 InputMethodDescriptor
pinyin_ime(pinyin_ime_id
,
74 "Pinyin input for testing",
81 input_methods
.push_back(pinyin_ime
);
84 languages
.push_back("zh-TW");
85 InputMethodDescriptor
zhuyin_ime(zhuyin_ime_id
,
86 "Zhuyin input for testing",
93 input_methods
.push_back(zhuyin_ime
);
95 util_
.SetComponentExtensions(input_methods
);
98 InputMethodDescriptor
GetDesc(const std::string
& id
,
99 const std::string
& raw_layout
,
100 const std::string
& language_code
,
101 const std::string
& indicator
) {
102 std::vector
<std::string
> layouts
;
103 layouts
.push_back(raw_layout
);
104 std::vector
<std::string
> languages
;
105 languages
.push_back(language_code
);
106 return InputMethodDescriptor(id
,
108 indicator
, // Short name used for indicator.
112 GURL(), // options page url
113 GURL()); // input view page url
116 static base::string16
GetDisplayLanguageName(const std::string
& language_code
) {
117 return l10n_util::GetDisplayNameForLocale(language_code
, "en", true);
120 FakeInputMethodDelegate delegate_
;
121 InputMethodWhitelist whitelist_
;
122 TestableInputMethodUtil util_
;
125 TEST_F(InputMethodUtilTest
, GetInputMethodShortNameTest
) {
126 // Test normal cases. Two-letter language code should be returned.
128 InputMethodDescriptor desc
= GetDesc("m17n:fa:isiri", // input method id
129 "us", // keyboard layout name
130 "fa", // language name
132 EXPECT_EQ(ASCIIToUTF16("FA"), util_
.GetInputMethodShortName(desc
));
135 InputMethodDescriptor desc
= GetDesc("mozc-hangul", "us", "ko", "");
136 EXPECT_EQ(base::UTF8ToUTF16("\xed\x95\x9c"),
137 util_
.GetInputMethodShortName(desc
));
140 InputMethodDescriptor desc
= GetDesc("invalid-id", "us", "xx", "");
141 // Upper-case string of the unknown language code, "xx", should be returned.
142 EXPECT_EQ(ASCIIToUTF16("XX"), util_
.GetInputMethodShortName(desc
));
145 // Test special cases.
147 InputMethodDescriptor desc
=
148 GetDesc("xkb:us:dvorak:eng", "us", "en-US", "DV");
149 EXPECT_EQ(ASCIIToUTF16("DV"), util_
.GetInputMethodShortName(desc
));
152 InputMethodDescriptor desc
=
153 GetDesc("xkb:us:colemak:eng", "us", "en-US", "CO");
154 EXPECT_EQ(ASCIIToUTF16("CO"), util_
.GetInputMethodShortName(desc
));
157 InputMethodDescriptor desc
=
158 GetDesc("xkb:us:altgr-intl:eng", "us", "en-US", "EXTD");
159 EXPECT_EQ(ASCIIToUTF16("EXTD"), util_
.GetInputMethodShortName(desc
));
162 InputMethodDescriptor desc
=
163 GetDesc("xkb:us:intl:eng", "us", "en-US", "INTL");
164 EXPECT_EQ(ASCIIToUTF16("INTL"), util_
.GetInputMethodShortName(desc
));
167 InputMethodDescriptor desc
=
168 GetDesc("xkb:de:neo:ger", "de(neo)", "de", "NEO");
169 EXPECT_EQ(ASCIIToUTF16("NEO"), util_
.GetInputMethodShortName(desc
));
172 InputMethodDescriptor desc
=
173 GetDesc("xkb:es:cat:cat", "es(cat)", "ca", "CAS");
174 EXPECT_EQ(ASCIIToUTF16("CAS"), util_
.GetInputMethodShortName(desc
));
177 InputMethodDescriptor desc
=
178 GetDesc(pinyin_ime_id
, "us", "zh-CN", "");
179 EXPECT_EQ(base::UTF8ToUTF16("\xe6\x8b\xbc"),
180 util_
.GetInputMethodShortName(desc
));
183 InputMethodDescriptor desc
=
184 GetDesc(zhuyin_ime_id
, "us", "zh-TW", "");
185 EXPECT_EQ(base::UTF8ToUTF16("\xE6\xB3\xA8"),
186 util_
.GetInputMethodShortName(desc
));
190 TEST_F(InputMethodUtilTest
, GetInputMethodMediumNameTest
) {
192 // input methods with medium name equal to short name
193 const char * input_method_id
[] = {
194 "xkb:us:altgr-intl:eng",
197 "xkb:us:colemak:eng",
202 const int len
= ARRAYSIZE_UNSAFE(input_method_id
);
203 for (int i
=0; i
<len
; ++i
) {
204 InputMethodDescriptor desc
= GetDesc(input_method_id
[i
], "", "", "");
205 base::string16 medium_name
= util_
.GetInputMethodMediumName(desc
);
206 base::string16 short_name
= util_
.GetInputMethodShortName(desc
);
207 EXPECT_EQ(medium_name
,short_name
);
211 // input methods with medium name not equal to short name
212 const char * input_method_id
[] = {
221 const int len
= ARRAYSIZE_UNSAFE(input_method_id
);
222 for (int i
=0; i
<len
; ++i
) {
223 InputMethodDescriptor desc
= GetDesc(input_method_id
[i
], "", "", "");
224 base::string16 medium_name
= util_
.GetInputMethodMediumName(desc
);
225 base::string16 short_name
= util_
.GetInputMethodShortName(desc
);
226 EXPECT_NE(medium_name
,short_name
);
231 TEST_F(InputMethodUtilTest
, GetInputMethodLongNameTest
) {
232 // For most languages input method or keyboard layout name is returned.
233 // See below for exceptions.
235 InputMethodDescriptor desc
= GetDesc("m17n:fa:isiri", "us", "fa", "");
236 EXPECT_EQ(ASCIIToUTF16("Persian input method (ISIRI 2901 layout)"),
237 util_
.GetInputMethodLongName(desc
));
240 InputMethodDescriptor desc
= GetDesc("mozc-hangul", "us", "ko", "");
241 EXPECT_EQ(ASCIIToUTF16("Korean input method"),
242 util_
.GetInputMethodLongName(desc
));
245 InputMethodDescriptor desc
= GetDesc("m17n:vi:tcvn", "us", "vi", "");
246 EXPECT_EQ(ASCIIToUTF16("Vietnamese input method (TCVN6064)"),
247 util_
.GetInputMethodLongName(desc
));
250 InputMethodDescriptor desc
= GetDesc("xkb:jp::jpn", "jp", "ja", "");
251 EXPECT_EQ(ASCIIToUTF16("Japanese"),
252 util_
.GetInputMethodLongName(desc
));
255 InputMethodDescriptor desc
=
256 GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US", "");
257 EXPECT_EQ(ASCIIToUTF16("US Dvorak"),
258 util_
.GetInputMethodLongName(desc
));
261 InputMethodDescriptor desc
=
262 GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US", "");
263 EXPECT_EQ(ASCIIToUTF16("UK Dvorak"),
264 util_
.GetInputMethodLongName(desc
));
267 // For Arabic, Dutch, French, German and Hindi,
268 // "language - keyboard layout" pair is returned.
270 InputMethodDescriptor desc
= GetDesc("m17n:ar:kbd", "us", "ar", "");
271 EXPECT_EQ(ASCIIToUTF16("Arabic - Standard input method"),
272 util_
.GetInputMethodLongName(desc
));
275 InputMethodDescriptor desc
= GetDesc("xkb:be::nld", "be", "nl", "");
276 EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian"),
277 util_
.GetInputMethodLongName(desc
));
280 InputMethodDescriptor desc
= GetDesc("xkb:fr::fra", "fr", "fr", "");
281 EXPECT_EQ(ASCIIToUTF16("French - French"),
282 util_
.GetInputMethodLongName(desc
));
285 InputMethodDescriptor desc
= GetDesc("xkb:be::fra", "be", "fr", "");
286 EXPECT_EQ(ASCIIToUTF16("French - Belgian"),
287 util_
.GetInputMethodLongName(desc
));
290 InputMethodDescriptor desc
= GetDesc("xkb:de::ger", "de", "de", "");
291 EXPECT_EQ(ASCIIToUTF16("German - German"),
292 util_
.GetInputMethodLongName(desc
));
295 InputMethodDescriptor desc
= GetDesc("xkb:be::ger", "be", "de", "");
296 EXPECT_EQ(ASCIIToUTF16("German - Belgian"),
297 util_
.GetInputMethodLongName(desc
));
300 InputMethodDescriptor desc
= GetDesc("m17n:hi:itrans", "us", "hi", "");
301 EXPECT_EQ(ASCIIToUTF16("Hindi - Standard input method"),
302 util_
.GetInputMethodLongName(desc
));
306 InputMethodDescriptor desc
= GetDesc("invalid-id", "us", "xx", "");
307 // You can safely ignore the "Resouce ID is not found for: invalid-id"
309 EXPECT_EQ(ASCIIToUTF16("invalid-id"),
310 util_
.GetInputMethodLongName(desc
));
314 TEST_F(InputMethodUtilTest
, TestIsValidInputMethodId
) {
315 EXPECT_TRUE(util_
.IsValidInputMethodId("xkb:us:colemak:eng"));
316 EXPECT_TRUE(util_
.IsValidInputMethodId(pinyin_ime_id
));
317 EXPECT_FALSE(util_
.IsValidInputMethodId("unsupported-input-method"));
320 TEST_F(InputMethodUtilTest
, TestIsKeyboardLayout
) {
321 EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
322 EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(pinyin_ime_id
));
325 TEST_F(InputMethodUtilTest
, TestGetKeyboardLayoutName
) {
327 EXPECT_EQ("", util_
.GetKeyboardLayoutName("UNSUPPORTED_ID"));
329 // Supported cases (samples).
330 EXPECT_EQ("us", util_
.GetKeyboardLayoutName(pinyin_ime_id
));
331 EXPECT_EQ("es", util_
.GetKeyboardLayoutName("xkb:es::spa"));
332 EXPECT_EQ("es(cat)", util_
.GetKeyboardLayoutName("xkb:es:cat:cat"));
333 EXPECT_EQ("gb(extd)", util_
.GetKeyboardLayoutName("xkb:gb:extd:eng"));
334 EXPECT_EQ("us", util_
.GetKeyboardLayoutName("xkb:us::eng"));
335 EXPECT_EQ("us(dvorak)", util_
.GetKeyboardLayoutName("xkb:us:dvorak:eng"));
336 EXPECT_EQ("us(colemak)", util_
.GetKeyboardLayoutName("xkb:us:colemak:eng"));
337 EXPECT_EQ("de(neo)", util_
.GetKeyboardLayoutName("xkb:de:neo:ger"));
340 TEST_F(InputMethodUtilTest
, TestGetLanguageCodeFromInputMethodId
) {
341 // Make sure that the -CN is added properly.
342 EXPECT_EQ("zh-CN", util_
.GetLanguageCodeFromInputMethodId(pinyin_ime_id
));
345 TEST_F(InputMethodUtilTest
, TestGetInputMethodDisplayNameFromId
) {
347 util_
.GetInputMethodDisplayNameFromId("xkb:us::eng"));
348 EXPECT_EQ("", util_
.GetInputMethodDisplayNameFromId("nonexistent"));
351 TEST_F(InputMethodUtilTest
, TestGetInputMethodDescriptorFromId
) {
352 EXPECT_EQ(NULL
, util_
.GetInputMethodDescriptorFromId("non_existent"));
354 const InputMethodDescriptor
* descriptor
=
355 util_
.GetInputMethodDescriptorFromId(pinyin_ime_id
);
356 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
357 EXPECT_EQ(pinyin_ime_id
, descriptor
->id());
358 EXPECT_EQ("us", descriptor
->GetPreferredKeyboardLayout());
359 // This used to be "zh" but now we have "zh-CN" in input_methods.h,
360 // hence this should be zh-CN now.
361 ASSERT_TRUE(!descriptor
->language_codes().empty());
362 EXPECT_EQ("zh-CN", descriptor
->language_codes().at(0));
365 TEST_F(InputMethodUtilTest
, TestGetInputMethodIdsForLanguageCode
) {
366 std::multimap
<std::string
, std::string
> language_code_to_ids_map
;
367 language_code_to_ids_map
.insert(std::make_pair("ja", pinyin_ime_id
));
368 language_code_to_ids_map
.insert(std::make_pair("ja", pinyin_ime_id
));
369 language_code_to_ids_map
.insert(std::make_pair("ja", "xkb:jp:jpn"));
370 language_code_to_ids_map
.insert(std::make_pair("fr", "xkb:fr:fra"));
372 std::vector
<std::string
> result
;
373 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
374 language_code_to_ids_map
, "ja", kAllInputMethods
, &result
));
375 EXPECT_EQ(3U, result
.size());
376 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
377 language_code_to_ids_map
, "ja", kKeyboardLayoutsOnly
, &result
));
378 ASSERT_EQ(1U, result
.size());
379 EXPECT_EQ("xkb:jp:jpn", result
[0]);
381 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
382 language_code_to_ids_map
, "fr", kAllInputMethods
, &result
));
383 ASSERT_EQ(1U, result
.size());
384 EXPECT_EQ("xkb:fr:fra", result
[0]);
385 EXPECT_TRUE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
386 language_code_to_ids_map
, "fr", kKeyboardLayoutsOnly
, &result
));
387 ASSERT_EQ(1U, result
.size());
388 EXPECT_EQ("xkb:fr:fra", result
[0]);
390 EXPECT_FALSE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
391 language_code_to_ids_map
, "invalid_lang", kAllInputMethods
, &result
));
392 EXPECT_FALSE(util_
.GetInputMethodIdsFromLanguageCodeInternal(
393 language_code_to_ids_map
, "invalid_lang", kKeyboardLayoutsOnly
, &result
));
396 // US keyboard + English US UI = US keyboard only.
397 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_EnUs
) {
398 const InputMethodDescriptor
* descriptor
=
399 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
400 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
401 std::vector
<std::string
> input_method_ids
;
402 util_
.GetFirstLoginInputMethodIds("en-US", *descriptor
, &input_method_ids
);
403 ASSERT_EQ(1U, input_method_ids
.size());
404 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
407 // US keyboard + Chinese UI = US keyboard + Pinyin IME.
408 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Zh
) {
409 const InputMethodDescriptor
* descriptor
=
410 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
411 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
412 std::vector
<std::string
> input_method_ids
;
413 util_
.GetFirstLoginInputMethodIds("zh-CN", *descriptor
, &input_method_ids
);
414 ASSERT_EQ(2U, input_method_ids
.size());
415 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
416 EXPECT_EQ(pinyin_ime_id
, input_method_ids
[1]); // Pinyin for US keybaord.
419 // US keyboard + Russian UI = US keyboard + Russsian keyboard
420 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Ru
) {
421 const InputMethodDescriptor
* descriptor
=
422 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
423 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
424 std::vector
<std::string
> input_method_ids
;
425 util_
.GetFirstLoginInputMethodIds("ru", *descriptor
, &input_method_ids
);
426 ASSERT_EQ(2U, input_method_ids
.size());
427 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
428 EXPECT_EQ("xkb:ru::rus", input_method_ids
[1]); // Russian keyboard.
431 // US keyboard + Traditional Chinese = US keyboard + chewing.
432 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_ZhTw
) {
433 const InputMethodDescriptor
* descriptor
=
434 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
435 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
436 std::vector
<std::string
> input_method_ids
;
437 util_
.GetFirstLoginInputMethodIds("zh-TW", *descriptor
, &input_method_ids
);
438 ASSERT_EQ(2U, input_method_ids
.size());
439 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
440 EXPECT_EQ(zhuyin_ime_id
, input_method_ids
[1]); // Chewing.
443 // US keyboard + Thai = US keyboard + kesmanee.
444 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Th
) {
445 const InputMethodDescriptor
* descriptor
=
446 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
447 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
448 std::vector
<std::string
> input_method_ids
;
449 util_
.GetFirstLoginInputMethodIds("th", *descriptor
, &input_method_ids
);
450 ASSERT_EQ(2U, input_method_ids
.size());
451 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
452 EXPECT_EQ(std::string("_comp_ime_") + kKeyboardExtId
+ "vkd_th",
453 input_method_ids
[1]); // Kesmanee.
456 // US keyboard + Vietnamese = US keyboard + TCVN6064.
457 TEST_F(InputMethodUtilTest
, TestGetFirstLoginInputMethodIds_Us_And_Vi
) {
458 const InputMethodDescriptor
* descriptor
=
459 util_
.GetInputMethodDescriptorFromId("xkb:us::eng"); // US keyboard.
460 ASSERT_TRUE(NULL
!= descriptor
); // ASSERT_NE doesn't compile.
461 std::vector
<std::string
> input_method_ids
;
462 util_
.GetFirstLoginInputMethodIds("vi", *descriptor
, &input_method_ids
);
463 ASSERT_EQ(2U, input_method_ids
.size());
464 EXPECT_EQ("xkb:us::eng", input_method_ids
[0]);
465 EXPECT_EQ(std::string("_comp_ime_") + kKeyboardExtId
+ "vkd_vi_tcvn",
466 input_method_ids
[1]); // TCVN6064.
469 TEST_F(InputMethodUtilTest
, TestGetLanguageCodesFromInputMethodIds
) {
470 std::vector
<std::string
> input_method_ids
;
471 input_method_ids
.push_back("xkb:us::eng"); // English US.
472 input_method_ids
.push_back("xkb:us:dvorak:eng"); // English US Dvorak.
473 input_method_ids
.push_back(pinyin_ime_id
); // Pinyin
474 input_method_ids
.push_back("xkb:fr::fra"); // French France.
475 std::vector
<std::string
> language_codes
;
476 util_
.GetLanguageCodesFromInputMethodIds(input_method_ids
, &language_codes
);
477 ASSERT_EQ(3U, language_codes
.size());
478 EXPECT_EQ("en-US", language_codes
[0]);
479 EXPECT_EQ("zh-CN", language_codes
[1]);
480 EXPECT_EQ("fr", language_codes
[2]);
483 // Test all supported descriptors to detect a typo in input_methods.txt.
484 TEST_F(InputMethodUtilTest
, TestIBusInputMethodText
) {
485 for (size_t i
= 0; i
< util_
.supported_input_methods_
->size(); ++i
) {
486 const std::string language_code
=
487 util_
.supported_input_methods_
->at(i
).language_codes().at(0);
488 const base::string16 display_name
=
489 l10n_util::GetDisplayNameForLocale(language_code
, "en", false);
490 // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
491 // allowed. See the text file for details.
492 EXPECT_TRUE(language_code
.length() == 2 ||
493 (language_code
.length() == 5 && language_code
[2] == '-'))
494 << "Invalid language code " << language_code
;
495 EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code
))
496 << "Invalid language code " << language_code
;
497 EXPECT_FALSE(display_name
.empty())
498 << "Invalid language code " << language_code
;
499 // On error, GetDisplayNameForLocale() returns the |language_code| as-is.
500 EXPECT_NE(language_code
, base::UTF16ToUTF8(display_name
))
501 << "Invalid language code " << language_code
;
505 } // namespace input_method
506 } // namespace chromeos