Home | History | Annotate | Download | only in input_method
      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/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 "grit/generated_resources.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 
     20 using base::ASCIIToUTF16;
     21 
     22 namespace chromeos {
     23 
     24 extern const char* kExtensionImePrefix;
     25 
     26 namespace input_method {
     27 
     28 namespace {
     29 
     30 const char pinyin_ime_id[] = "zh-t-i0-pinyin";
     31 const char zhuyin_ime_id[] = "zh-hant-t-i0-und";
     32 
     33 class TestableInputMethodUtil : public InputMethodUtil {
     34  public:
     35   explicit TestableInputMethodUtil(InputMethodDelegate* delegate,
     36                                    scoped_ptr<InputMethodDescriptors> methods)
     37       : InputMethodUtil(delegate, methods.Pass()) {
     38   }
     39   // Change access rights.
     40   using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal;
     41   using InputMethodUtil::GetKeyboardLayoutName;
     42 };
     43 
     44 }  // namespace
     45 
     46 class InputMethodUtilTest : public testing::Test {
     47  public:
     48   InputMethodUtilTest()
     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));
     54   }
     55 
     56   virtual void SetUp() OVERRIDE {
     57     InputMethodDescriptors input_methods;
     58 
     59     std::vector<std::string> layouts;
     60     std::vector<std::string> languages;
     61     layouts.push_back("us");
     62     languages.push_back("zh-CN");
     63 
     64     InputMethodDescriptor pinyin_ime(Id(pinyin_ime_id),
     65                                      "Pinyin input for testing",
     66                                      "CN",
     67                                      layouts,
     68                                      languages,
     69                                      false,
     70                                      GURL(""),
     71                                      GURL(""));
     72     input_methods.push_back(pinyin_ime);
     73 
     74     languages.clear();
     75     languages.push_back("zh-TW");
     76     InputMethodDescriptor zhuyin_ime(zhuyin_ime_id,
     77                                      "Zhuyin input for testing",
     78                                      "TW",
     79                                      layouts,
     80                                      languages,
     81                                      false,
     82                                      GURL(""),
     83                                      GURL(""));
     84     input_methods.push_back(zhuyin_ime);
     85 
     86     util_.InitXkbInputMethodsForTesting();
     87     util_.AppendInputMethods(input_methods);
     88   }
     89 
     90   std::string Id(const std::string& id) {
     91     return extension_ime_util::GetInputMethodIDByEngineID(id);
     92   }
     93 
     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),
    103                                  "",         // Description.
    104                                  indicator,  // Short name used for indicator.
    105                                  layouts,
    106                                  languages,
    107                                  true,
    108                                  GURL(),   // options page url
    109                                  GURL());  // input view page url
    110   }
    111 
    112   static base::string16 GetDisplayLanguageName(const std::string& language_code) {
    113     return l10n_util::GetDisplayNameForLocale(language_code, "en", true);
    114   }
    115 
    116   FakeInputMethodDelegate delegate_;
    117   InputMethodWhitelist whitelist_;
    118   TestableInputMethodUtil util_;
    119 };
    120 
    121 TEST_F(InputMethodUtilTest, GetInputMethodShortNameTest) {
    122   // Test invalid cases. Two-letter language code should be returned.
    123   {
    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));
    127   }
    128 
    129   // Test special cases.
    130   {
    131     InputMethodDescriptor desc =
    132         GetDesc("xkb:us:dvorak:eng", "us", "en-US", "DV");
    133     EXPECT_EQ(ASCIIToUTF16("DV"), util_.GetInputMethodShortName(desc));
    134   }
    135   {
    136     InputMethodDescriptor desc =
    137         GetDesc("xkb:us:colemak:eng", "us", "en-US", "CO");
    138     EXPECT_EQ(ASCIIToUTF16("CO"), util_.GetInputMethodShortName(desc));
    139   }
    140   {
    141     InputMethodDescriptor desc =
    142         GetDesc("xkb:us:altgr-intl:eng", "us", "en-US", "EXTD");
    143     EXPECT_EQ(ASCIIToUTF16("EXTD"), util_.GetInputMethodShortName(desc));
    144   }
    145   {
    146     InputMethodDescriptor desc =
    147         GetDesc("xkb:us:intl:eng", "us", "en-US", "INTL");
    148     EXPECT_EQ(ASCIIToUTF16("INTL"), util_.GetInputMethodShortName(desc));
    149   }
    150   {
    151     InputMethodDescriptor desc =
    152         GetDesc("xkb:de:neo:ger", "de(neo)", "de", "NEO");
    153     EXPECT_EQ(ASCIIToUTF16("NEO"), util_.GetInputMethodShortName(desc));
    154   }
    155   {
    156     InputMethodDescriptor desc =
    157         GetDesc("xkb:es:cat:cat", "es(cat)", "ca", "CAS");
    158     EXPECT_EQ(ASCIIToUTF16("CAS"), util_.GetInputMethodShortName(desc));
    159   }
    160   {
    161     InputMethodDescriptor desc =
    162         GetDesc(pinyin_ime_id, "us", "zh-CN", "");
    163     EXPECT_EQ(base::UTF8ToUTF16("\xe6\x8b\xbc"),
    164               util_.GetInputMethodShortName(desc));
    165   }
    166   {
    167     InputMethodDescriptor desc = GetDesc(zhuyin_ime_id, "us", "zh-TW", "");
    168     EXPECT_EQ(base::UTF8ToUTF16("\xE6\xB3\xA8"),
    169               util_.GetInputMethodShortName(desc));
    170   }
    171 }
    172 
    173 TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) {
    174   {
    175     // input methods with medium name equal to short name
    176     const char * input_method_id[] = {
    177       "xkb:us:altgr-intl:eng",
    178       "xkb:us:dvorak:eng",
    179       "xkb:us:intl:eng",
    180       "xkb:us:colemak:eng",
    181       "xkb:de:neo:ger",
    182       "xkb:es:cat:cat",
    183       "xkb:gb:dvorak:eng",
    184     };
    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);
    191     }
    192   }
    193   {
    194     // input methods with medium name not equal to short name
    195     const char * input_method_id[] = {
    196       pinyin_ime_id,
    197       zhuyin_ime_id,
    198     };
    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);
    205     }
    206   }
    207 }
    208 
    209 TEST_F(InputMethodUtilTest, GetInputMethodLongNameTest) {
    210   // For most languages input method or keyboard layout name is returned.
    211   // See below for exceptions.
    212   {
    213     InputMethodDescriptor desc = GetDesc("xkb:jp::jpn", "jp", "ja", "");
    214     EXPECT_EQ(ASCIIToUTF16("Japanese"),
    215               util_.GetInputMethodLongName(desc));
    216   }
    217   {
    218     InputMethodDescriptor desc =
    219         GetDesc("xkb:us:dvorak:eng", "us(dvorak)", "en-US", "");
    220     EXPECT_EQ(ASCIIToUTF16("US Dvorak"),
    221               util_.GetInputMethodLongName(desc));
    222   }
    223   {
    224     InputMethodDescriptor desc =
    225         GetDesc("xkb:gb:dvorak:eng", "gb(dvorak)", "en-US", "");
    226     EXPECT_EQ(ASCIIToUTF16("UK Dvorak"),
    227               util_.GetInputMethodLongName(desc));
    228   }
    229 
    230   // For Dutch, French, German and Hindi,
    231   // "language - keyboard layout" pair is returned.
    232   {
    233     InputMethodDescriptor desc = GetDesc("xkb:be::nld", "be", "nl", "");
    234     EXPECT_EQ(ASCIIToUTF16("Dutch - Belgian"),
    235               util_.GetInputMethodLongName(desc));
    236   }
    237   {
    238     InputMethodDescriptor desc = GetDesc("xkb:fr::fra", "fr", "fr", "");
    239     EXPECT_EQ(ASCIIToUTF16("French - French"),
    240               util_.GetInputMethodLongName(desc));
    241   }
    242   {
    243     InputMethodDescriptor desc = GetDesc("xkb:be::fra", "be", "fr", "");
    244     EXPECT_EQ(ASCIIToUTF16("French - Belgian"),
    245               util_.GetInputMethodLongName(desc));
    246   }
    247   {
    248     InputMethodDescriptor desc = GetDesc("xkb:de::ger", "de", "de", "");
    249     EXPECT_EQ(ASCIIToUTF16("German - German"),
    250               util_.GetInputMethodLongName(desc));
    251   }
    252   {
    253     InputMethodDescriptor desc = GetDesc("xkb:be::ger", "be", "de", "");
    254     EXPECT_EQ(ASCIIToUTF16("German - Belgian"),
    255               util_.GetInputMethodLongName(desc));
    256   }
    257 
    258   {
    259     InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx", "");
    260     // You can safely ignore the "Resouce ID is not found for: invalid-id"
    261     // error.
    262     EXPECT_EQ(ASCIIToUTF16("invalid-id"),
    263               util_.GetInputMethodLongName(desc));
    264   }
    265 }
    266 
    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"));
    271 }
    272 
    273 TEST_F(InputMethodUtilTest, TestIsKeyboardLayout) {
    274   EXPECT_TRUE(InputMethodUtil::IsKeyboardLayout("xkb:us::eng"));
    275   EXPECT_FALSE(InputMethodUtil::IsKeyboardLayout(Id(pinyin_ime_id)));
    276 }
    277 
    278 TEST_F(InputMethodUtilTest, TestGetKeyboardLayoutName) {
    279   // Unsupported case.
    280   EXPECT_EQ("", util_.GetKeyboardLayoutName("UNSUPPORTED_ID"));
    281 
    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")));
    292 }
    293 
    294 TEST_F(InputMethodUtilTest, TestGetInputMethodDisplayNameFromId) {
    295   EXPECT_EQ("US",
    296             util_.GetInputMethodDisplayNameFromId("xkb:us::eng"));
    297   EXPECT_EQ("", util_.GetInputMethodDisplayNameFromId("nonexistent"));
    298 }
    299 
    300 TEST_F(InputMethodUtilTest, TestGetInputMethodDescriptorFromId) {
    301   EXPECT_EQ(NULL, util_.GetInputMethodDescriptorFromId("non_existent"));
    302 
    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));
    312 }
    313 
    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"));
    320 
    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]);
    329 
    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]);
    338 
    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));
    343 }
    344 
    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]);
    354 }
    355 
    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.
    366 }
    367 
    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.
    378 }
    379 
    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.
    390 }
    391 
    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.
    402 }
    403 
    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.
    414 }
    415 
    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]);
    428 }
    429 
    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;
    451   }
    452 }
    453 
    454 }  // namespace input_method
    455 }  // namespace chromeos
    456