input_method_util_unittest.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
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.
4
5#include "chrome/browser/chromeos/input_method/input_method_util.h"
6
7#include <string>
8
9#include "base/bind.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"
18
19namespace chromeos {
20
21extern const char* kExtensionImePrefix;
22
23namespace input_method {
24
25namespace {
26
27const char pinyin_ime_id[] =
28    "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin";
29const char zhuyin_ime_id[] =
30    "_comp_ime_goedamlknlnjaengojinmfgpmdjmkooozh-hant-t-i0-und";
31
32class TestableInputMethodUtil : public InputMethodUtil {
33 public:
34  explicit TestableInputMethodUtil(InputMethodDelegate* delegate,
35                                   scoped_ptr<InputMethodDescriptors> methods)
36      : InputMethodUtil(delegate, methods.Pass()) {
37  }
38  // Change access rights.
39  using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal;
40  using InputMethodUtil::GetKeyboardLayoutName;
41  using InputMethodUtil::ReloadInternalMaps;
42  using InputMethodUtil::supported_input_methods_;
43};
44
45}  // namespace
46
47class InputMethodUtilTest : public testing::Test {
48 public:
49  InputMethodUtilTest()
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));
55  }
56
57  virtual void SetUp() OVERRIDE {
58    InputMethodDescriptors input_methods;
59
60    std::vector<std::string> layouts;
61    std::vector<std::string> languages;
62    layouts.push_back("us");
63    languages.push_back("zh-CN");
64
65    InputMethodDescriptor pinyin_ime(pinyin_ime_id,
66                                     "Pinyin input for testing",
67                                     layouts,
68                                     languages,
69                                     GURL(""));
70    input_methods.push_back(pinyin_ime);
71
72    languages.clear();
73    languages.push_back("zh-TW");
74    InputMethodDescriptor zhuyin_ime(zhuyin_ime_id,
75                                     "Zhuyin input for testing",
76                                     layouts,
77                                     languages,
78                                     GURL(""));
79    input_methods.push_back(zhuyin_ime);
80
81    util_.SetComponentExtensions(input_methods);
82  }
83
84  InputMethodDescriptor GetDesc(const std::string& id,
85                                const std::string& raw_layout,
86                                const std::string& language_code) {
87    std::vector<std::string> layouts;
88    layouts.push_back(raw_layout);
89    std::vector<std::string> languages;
90    languages.push_back(language_code);
91    return InputMethodDescriptor(id,
92                                 "",
93                                 layouts,
94                                 languages,
95                                 GURL());  // options page url
96  }
97
98  static string16 GetDisplayLanguageName(const std::string& language_code) {
99    return l10n_util::GetDisplayNameForLocale(language_code, "en", true);
100  }
101
102  FakeInputMethodDelegate delegate_;
103  InputMethodWhitelist whitelist_;
104  TestableInputMethodUtil util_;
105};
106
107TEST_F(InputMethodUtilTest, GetInputMethodShortNameTest) {
108  // Test normal cases. Two-letter language code should be returned.
109  {
110    InputMethodDescriptor desc = GetDesc("m17n:fa:isiri",  // input method id
111                                         "us",  // keyboard layout name
112                                         "fa");  // language name
113    EXPECT_EQ(ASCIIToUTF16("FA"), util_.GetInputMethodShortName(desc));
114  }
115  {
116    InputMethodDescriptor desc = GetDesc("mozc-hangul", "us", "ko");
117    EXPECT_EQ(UTF8ToUTF16("\xed\x95\x9c"),
118              util_.GetInputMethodShortName(desc));
119  }
120  {
121    InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx");
122    // Upper-case string of the unknown language code, "xx", should be returned.
123    EXPECT_EQ(ASCIIToUTF16("XX"), util_.GetInputMethodShortName(desc));
124  }
125
126  // Test special cases.
127  {
128    InputMethodDescriptor desc = GetDesc("xkb:us:dvorak:eng", "us", "en-US");
129    EXPECT_EQ(ASCIIToUTF16("DV"), util_.GetInputMethodShortName(desc));
130  }
131  {
132    InputMethodDescriptor desc = GetDesc("xkb:us:colemak:eng", "us", "en-US");
133    EXPECT_EQ(ASCIIToUTF16("CO"), util_.GetInputMethodShortName(desc));
134  }
135  {
136    InputMethodDescriptor desc =
137        GetDesc("xkb:us:altgr-intl:eng", "us", "en-US");
138    EXPECT_EQ(ASCIIToUTF16("EXTD"), util_.GetInputMethodShortName(desc));
139  }
140  {
141    InputMethodDescriptor desc = GetDesc("xkb:us:intl:eng", "us", "en-US");
142    EXPECT_EQ(ASCIIToUTF16("INTL"), util_.GetInputMethodShortName(desc));
143  }
144  {
145    InputMethodDescriptor desc = GetDesc("xkb:de:neo:ger", "de(neo)", "de");
146    EXPECT_EQ(ASCIIToUTF16("NEO"), util_.GetInputMethodShortName(desc));
147  }
148  {
149    InputMethodDescriptor desc = GetDesc("xkb:es:cat:cat", "es(cat)", "ca");
150    EXPECT_EQ(ASCIIToUTF16("CAS"), util_.GetInputMethodShortName(desc));
151  }
152  {
153    InputMethodDescriptor desc = GetDesc(pinyin_ime_id, "us", "zh-CN");
154    EXPECT_EQ(UTF8ToUTF16("\xe6\x8b\xbc"),
155              util_.GetInputMethodShortName(desc));
156  }
157  {
158    InputMethodDescriptor desc = GetDesc(zhuyin_ime_id, "us", "zh-TW");
159    EXPECT_EQ(UTF8ToUTF16("\xe9\x85\xb7"),
160              util_.GetInputMethodShortName(desc));
161  }
162  {
163    InputMethodDescriptor desc = GetDesc("m17n:zh:cangjie", "us", "zh-TW");
164    EXPECT_EQ(UTF8ToUTF16("\xe5\x80\x89"),
165              util_.GetInputMethodShortName(desc));
166  }
167  {
168    InputMethodDescriptor desc = GetDesc("m17n:zh:quick", "us", "zh-TW");
169    EXPECT_EQ(UTF8ToUTF16("\xe9\x80\x9f"),
170              util_.GetInputMethodShortName(desc));
171  }
172}
173
174TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) {
175  {
176    // input methods with medium name equal to short name
177    const char * input_method_id[] = {
178      "xkb:us:altgr-intl:eng",
179      "xkb:us:dvorak:eng",
180      "xkb:us:intl:eng",
181      "xkb:us:colemak:eng",
182      "english-m",
183      "xkb:de:neo:ger",
184      "xkb:es:cat:cat",
185      "xkb:gb:dvorak:eng",
186    };
187    const int len = ARRAYSIZE_UNSAFE(input_method_id);
188    for (int i=0; i<len; ++i) {
189      InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "");
190      string16 medium_name = util_.GetInputMethodMediumName(desc);
191      string16 short_name = util_.GetInputMethodShortName(desc);
192      EXPECT_EQ(medium_name,short_name);
193    }
194  }
195  {
196    // input methods with medium name not equal to short name
197    const char * input_method_id[] = {
198      "m17n:zh:cangjie",
199      "m17n:zh:quick",
200      pinyin_ime_id,
201      zhuyin_ime_id,
202      "mozc-hangul",
203      pinyin_ime_id,
204      pinyin_ime_id,
205    };
206    const int len = ARRAYSIZE_UNSAFE(input_method_id);
207    for (int i=0; i<len; ++i) {
208      InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "");
209      string16 medium_name = util_.GetInputMethodMediumName(desc);
210      string16 short_name = util_.GetInputMethodShortName(desc);
211      EXPECT_NE(medium_name,short_name);
212    }
213  }
214}
215
216TEST_F(InputMethodUtilTest, GetInputMethodLongNameTest) {
217  // For most languages input method or keyboard layout name is returned.
218  // See below for exceptions.
219  {
220    InputMethodDescriptor desc = GetDesc("m17n:fa:isiri", "us", "fa");
221    EXPECT_EQ(ASCIIToUTF16("Persian input method (ISIRI 2901 layout)"),
222              util_.GetInputMethodLongName(desc));
223  }
224  {
225    InputMethodDescriptor desc = GetDesc("mozc-hangul", "us", "ko");
226    EXPECT_EQ(ASCIIToUTF16("Korean input method"),
227              util_.GetInputMethodLongName(desc));
228  }
229  {
230    InputMethodDescriptor desc = GetDesc("m17n:vi:tcvn", "us", "vi");
231    EXPECT_EQ(ASCIIToUTF16("Vietnamese input method (TCVN6064)"),
232              util_.GetInputMethodLongName(desc));
233  }
234  {
235    InputMethodDescriptor desc = GetDesc("xkb:jp::jpn", "jp", "ja");
236    EXPECT_EQ(ASCIIToUTF16("Japanese keyboard"),
237              util_.GetInputMethodLongName(desc));
238  }
239  {
240    InputMethodDescriptor desc =
241        GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US");
242    EXPECT_EQ(ASCIIToUTF16("US Dvorak keyboard"),
243              util_.GetInputMethodLongName(desc));
244  }
245  {
246    InputMethodDescriptor desc =
247        GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US");
248    EXPECT_EQ(ASCIIToUTF16("UK Dvorak keyboard"),
249              util_.GetInputMethodLongName(desc));
250  }
251
252  // For Arabic, Dutch, French, German and Hindi,
253  // "language - keyboard layout" pair is returned.
254  {
255    InputMethodDescriptor desc = GetDesc("m17n:ar:kbd", "us", "ar");
256    EXPECT_EQ(ASCIIToUTF16("Arabic - Standard input method"),
257              util_.GetInputMethodLongName(desc));
258  }
259  {
260    InputMethodDescriptor desc = GetDesc("xkb:be::nld", "be", "nl");
261    EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian keyboard"),
262              util_.GetInputMethodLongName(desc));
263  }
264  {
265    InputMethodDescriptor desc = GetDesc("xkb:fr::fra", "fr", "fr");
266    EXPECT_EQ(ASCIIToUTF16("French - French keyboard"),
267              util_.GetInputMethodLongName(desc));
268  }
269  {
270    InputMethodDescriptor desc = GetDesc("xkb:be::fra", "be", "fr");
271    EXPECT_EQ(ASCIIToUTF16("French - Belgian keyboard"),
272              util_.GetInputMethodLongName(desc));
273  }
274  {
275    InputMethodDescriptor desc = GetDesc("xkb:de::ger", "de", "de");
276    EXPECT_EQ(ASCIIToUTF16("German - German keyboard"),
277              util_.GetInputMethodLongName(desc));
278  }
279  {
280    InputMethodDescriptor desc = GetDesc("xkb:be::ger", "be", "de");
281    EXPECT_EQ(ASCIIToUTF16("German - Belgian keyboard"),
282              util_.GetInputMethodLongName(desc));
283  }
284  {
285    InputMethodDescriptor desc = GetDesc("m17n:hi:itrans", "us", "hi");
286    EXPECT_EQ(ASCIIToUTF16("Hindi - Standard input method"),
287              util_.GetInputMethodLongName(desc));
288  }
289
290  {
291    InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx");
292    // You can safely ignore the "Resouce ID is not found for: invalid-id"
293    // error.
294    EXPECT_EQ(ASCIIToUTF16("invalid-id"),
295              util_.GetInputMethodLongName(desc));
296  }
297}
298
299TEST_F(InputMethodUtilTest, TestIsValidInputMethodId) {
300  EXPECT_TRUE(util_.IsValidInputMethodId("xkb:us:colemak:eng"));
301  EXPECT_TRUE(util_.IsValidInputMethodId(pinyin_ime_id));
302  EXPECT_FALSE(util_.IsValidInputMethodId("unsupported-input-method"));
303}
304
305TEST_F(InputMethodUtilTest, TestIsKeyboardLayout) {
306  EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
307  EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(pinyin_ime_id));
308}
309
310TEST_F(InputMethodUtilTest, TestGetKeyboardLayoutName) {
311  // Unsupported case.
312  EXPECT_EQ("", util_.GetKeyboardLayoutName("UNSUPPORTED_ID"));
313
314  // Supported cases (samples).
315  EXPECT_EQ("us", util_.GetKeyboardLayoutName(pinyin_ime_id));
316  EXPECT_EQ("es", util_.GetKeyboardLayoutName("xkb:es::spa"));
317  EXPECT_EQ("es(cat)", util_.GetKeyboardLayoutName("xkb:es:cat:cat"));
318  EXPECT_EQ("gb(extd)", util_.GetKeyboardLayoutName("xkb:gb:extd:eng"));
319  EXPECT_EQ("us", util_.GetKeyboardLayoutName("xkb:us::eng"));
320  EXPECT_EQ("us(dvorak)", util_.GetKeyboardLayoutName("xkb:us:dvorak:eng"));
321  EXPECT_EQ("us(colemak)", util_.GetKeyboardLayoutName("xkb:us:colemak:eng"));
322  EXPECT_EQ("de(neo)", util_.GetKeyboardLayoutName("xkb:de:neo:ger"));
323}
324
325TEST_F(InputMethodUtilTest, TestGetLanguageCodeFromInputMethodId) {
326  // Make sure that the -CN is added properly.
327  EXPECT_EQ("zh-CN", util_.GetLanguageCodeFromInputMethodId(pinyin_ime_id));
328}
329
330TEST_F(InputMethodUtilTest, TestGetInputMethodDisplayNameFromId) {
331  EXPECT_EQ("US keyboard",
332            util_.GetInputMethodDisplayNameFromId("xkb:us::eng"));
333  EXPECT_EQ("", util_.GetInputMethodDisplayNameFromId("nonexistent"));
334}
335
336TEST_F(InputMethodUtilTest, TestGetInputMethodDescriptorFromId) {
337  EXPECT_EQ(NULL, util_.GetInputMethodDescriptorFromId("non_existent"));
338
339  const InputMethodDescriptor* descriptor =
340      util_.GetInputMethodDescriptorFromId(pinyin_ime_id);
341  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
342  EXPECT_EQ(pinyin_ime_id, descriptor->id());
343  EXPECT_EQ("us", descriptor->GetPreferredKeyboardLayout());
344  // This used to be "zh" but now we have "zh-CN" in input_methods.h,
345  // hence this should be zh-CN now.
346  ASSERT_TRUE(!descriptor->language_codes().empty());
347  EXPECT_EQ("zh-CN", descriptor->language_codes().at(0));
348}
349
350TEST_F(InputMethodUtilTest, TestGetInputMethodIdsForLanguageCode) {
351  std::multimap<std::string, std::string> language_code_to_ids_map;
352  language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
353  language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id));
354  language_code_to_ids_map.insert(std::make_pair("ja", "xkb:jp:jpn"));
355  language_code_to_ids_map.insert(std::make_pair("fr", "xkb:fr:fra"));
356
357  std::vector<std::string> result;
358  EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
359      language_code_to_ids_map, "ja", kAllInputMethods, &result));
360  EXPECT_EQ(3U, result.size());
361  EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
362      language_code_to_ids_map, "ja", kKeyboardLayoutsOnly, &result));
363  ASSERT_EQ(1U, result.size());
364  EXPECT_EQ("xkb:jp:jpn", result[0]);
365
366  EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
367      language_code_to_ids_map, "fr", kAllInputMethods, &result));
368  ASSERT_EQ(1U, result.size());
369  EXPECT_EQ("xkb:fr:fra", result[0]);
370  EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal(
371      language_code_to_ids_map, "fr", kKeyboardLayoutsOnly, &result));
372  ASSERT_EQ(1U, result.size());
373  EXPECT_EQ("xkb:fr:fra", result[0]);
374
375  EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
376      language_code_to_ids_map, "invalid_lang", kAllInputMethods, &result));
377  EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal(
378      language_code_to_ids_map, "invalid_lang", kKeyboardLayoutsOnly, &result));
379}
380
381// US keyboard + English US UI = US keyboard only.
382TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_EnUs) {
383  const InputMethodDescriptor* descriptor =
384      util_.GetInputMethodDescriptorFromId("xkb:us::eng");  // US keyboard.
385  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
386  std::vector<std::string> input_method_ids;
387  util_.GetFirstLoginInputMethodIds("en-US", *descriptor, &input_method_ids);
388  ASSERT_EQ(1U, input_method_ids.size());
389  EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
390}
391
392// US keyboard + Chinese UI = US keyboard + Pinyin IME.
393TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Zh) {
394  const InputMethodDescriptor* descriptor =
395      util_.GetInputMethodDescriptorFromId("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("zh-CN", *descriptor, &input_method_ids);
399  ASSERT_EQ(2U, input_method_ids.size());
400  EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
401  EXPECT_EQ(pinyin_ime_id, input_method_ids[1]);  // Pinyin for US keybaord.
402}
403
404// US keyboard + Russian UI = US keyboard + Russsian keyboard
405TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Ru) {
406  const InputMethodDescriptor* descriptor =
407      util_.GetInputMethodDescriptorFromId("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("ru", *descriptor, &input_method_ids);
411  ASSERT_EQ(2U, input_method_ids.size());
412  EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
413  EXPECT_EQ("xkb:ru::rus", input_method_ids[1]);  // Russian keyboard.
414}
415
416// US keyboard + Thai = US keyboard + kesmanee.
417TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Th) {
418  const InputMethodDescriptor* descriptor =
419      util_.GetInputMethodDescriptorFromId("xkb:us::eng");  // US keyboard.
420  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
421  std::vector<std::string> input_method_ids;
422  util_.GetFirstLoginInputMethodIds("th", *descriptor, &input_method_ids);
423  ASSERT_EQ(2U, input_method_ids.size());
424  EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
425  EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th",
426            input_method_ids[1]);  // Kesmanee.
427}
428
429// US keyboard + Vietnamese = US keyboard + TCVN6064.
430TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Vi) {
431  const InputMethodDescriptor* descriptor =
432      util_.GetInputMethodDescriptorFromId("xkb:us::eng");  // US keyboard.
433  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
434  std::vector<std::string> input_method_ids;
435  util_.GetFirstLoginInputMethodIds("vi", *descriptor, &input_method_ids);
436  ASSERT_EQ(2U, input_method_ids.size());
437  EXPECT_EQ("xkb:us::eng", input_method_ids[0]);
438  EXPECT_EQ("_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn",
439            input_method_ids[1]);  // TCVN6064.
440}
441
442TEST_F(InputMethodUtilTest, TestGetLanguageCodesFromInputMethodIds) {
443  std::vector<std::string> input_method_ids;
444  input_method_ids.push_back("xkb:us::eng");  // English US.
445  input_method_ids.push_back("xkb:us:dvorak:eng");  // English US Dvorak.
446  input_method_ids.push_back(pinyin_ime_id);  // Pinyin
447  input_method_ids.push_back("xkb:fr::fra");  // French France.
448  std::vector<std::string> language_codes;
449  util_.GetLanguageCodesFromInputMethodIds(input_method_ids, &language_codes);
450  ASSERT_EQ(3U, language_codes.size());
451  EXPECT_EQ("en-US", language_codes[0]);
452  EXPECT_EQ("zh-CN", language_codes[1]);
453  EXPECT_EQ("fr", language_codes[2]);
454}
455
456// Test all supported descriptors to detect a typo in ibus_input_methods.txt.
457TEST_F(InputMethodUtilTest, TestIBusInputMethodText) {
458  for (size_t i = 0; i < util_.supported_input_methods_->size(); ++i) {
459    const std::string language_code =
460        util_.supported_input_methods_->at(i).language_codes().at(0);
461    const string16 display_name =
462        l10n_util::GetDisplayNameForLocale(language_code, "en", false);
463    // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
464    // allowed. See the text file for details.
465    EXPECT_TRUE(language_code.length() == 2 ||
466                (language_code.length() == 5 && language_code[2] == '-'))
467        << "Invalid language code " << language_code;
468    EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code))
469        << "Invalid language code " << language_code;
470    EXPECT_FALSE(display_name.empty())
471        << "Invalid language code " << language_code;
472    // On error, GetDisplayNameForLocale() returns the |language_code| as-is.
473    EXPECT_NE(language_code, UTF16ToUTF8(display_name))
474        << "Invalid language code " << language_code;
475  }
476}
477
478}  // namespace input_method
479}  // namespace chromeos
480