// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/input_method/input_method_util.h" #include #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "chromeos/ime/extension_ime_util.h" #include "chromeos/ime/fake_input_method_delegate.h" #include "chromeos/ime/input_method_manager.h" #include "chromeos/ime/input_method_whitelist.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" using base::ASCIIToUTF16; namespace chromeos { extern const char* kExtensionImePrefix; namespace input_method { namespace { const char pinyin_ime_id[] = "zh-t-i0-pinyin"; const char zhuyin_ime_id[] = "zh-hant-t-i0-und"; class TestableInputMethodUtil : public InputMethodUtil { public: explicit TestableInputMethodUtil(InputMethodDelegate* delegate, scoped_ptr methods) : InputMethodUtil(delegate) { ResetInputMethods(*methods); } // Change access rights. using InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal; using InputMethodUtil::GetKeyboardLayoutName; }; } // namespace class InputMethodUtilTest : public testing::Test { public: InputMethodUtilTest() : util_(&delegate_, whitelist_.GetSupportedInputMethods()) { delegate_.set_get_localized_string_callback( base::Bind(&l10n_util::GetStringUTF16)); delegate_.set_get_display_language_name_callback( base::Bind(&InputMethodUtilTest::GetDisplayLanguageName)); } virtual void SetUp() override { InputMethodDescriptors input_methods; std::vector layouts; std::vector languages; layouts.push_back("us"); languages.push_back("zh-CN"); InputMethodDescriptor pinyin_ime(Id(pinyin_ime_id), "Pinyin input for testing", "CN", layouts, languages, false, GURL(""), GURL("")); input_methods.push_back(pinyin_ime); languages.clear(); languages.push_back("zh-TW"); InputMethodDescriptor zhuyin_ime(zhuyin_ime_id, "Zhuyin input for testing", "TW", layouts, languages, false, GURL(""), GURL("")); input_methods.push_back(zhuyin_ime); util_.InitXkbInputMethodsForTesting(); util_.AppendInputMethods(input_methods); } std::string Id(const std::string& id) { return extension_ime_util::GetInputMethodIDByEngineID(id); } InputMethodDescriptor GetDesc(const std::string& id, const std::string& raw_layout, const std::string& language_code, const std::string& indicator) { std::vector layouts; layouts.push_back(raw_layout); std::vector languages; languages.push_back(language_code); return InputMethodDescriptor(Id(id), "", // Description. indicator, // Short name used for indicator. layouts, languages, true, GURL(), // options page url GURL()); // input view page url } static base::string16 GetDisplayLanguageName(const std::string& language_code) { return l10n_util::GetDisplayNameForLocale(language_code, "en", true); } FakeInputMethodDelegate delegate_; InputMethodWhitelist whitelist_; TestableInputMethodUtil util_; }; TEST_F(InputMethodUtilTest, GetInputMethodShortNameTest) { // Test invalid cases. Two-letter language code should be returned. { InputMethodDescriptor desc = GetDesc("invalid-id", "us", "xx", ""); // Upper-case string of the unknown language code, "xx", should be returned. EXPECT_EQ(ASCIIToUTF16("XX"), util_.GetInputMethodShortName(desc)); } // Test special cases. { InputMethodDescriptor desc = GetDesc("xkb:us:dvorak:eng", "us", "en-US", "DV"); EXPECT_EQ(ASCIIToUTF16("DV"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc("xkb:us:colemak:eng", "us", "en-US", "CO"); EXPECT_EQ(ASCIIToUTF16("CO"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc("xkb:us:altgr-intl:eng", "us", "en-US", "EXTD"); EXPECT_EQ(ASCIIToUTF16("EXTD"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc("xkb:us:intl:eng", "us", "en-US", "INTL"); EXPECT_EQ(ASCIIToUTF16("INTL"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc("xkb:de:neo:ger", "de(neo)", "de", "NEO"); EXPECT_EQ(ASCIIToUTF16("NEO"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc("xkb:es:cat:cat", "es(cat)", "ca", "CAS"); EXPECT_EQ(ASCIIToUTF16("CAS"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc(pinyin_ime_id, "us", "zh-CN", "\xe6\x8b\xbc"); EXPECT_EQ(base::UTF8ToUTF16("\xe6\x8b\xbc"), util_.GetInputMethodShortName(desc)); } { InputMethodDescriptor desc = GetDesc(zhuyin_ime_id, "us", "zh-TW", "\xE6\xB3\xA8"); EXPECT_EQ(base::UTF8ToUTF16("\xE6\xB3\xA8"), util_.GetInputMethodShortName(desc)); } } TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) { { // input methods with medium name equal to short name const char * input_method_id[] = { "xkb:us:altgr-intl:eng", "xkb:us:dvorak:eng", "xkb:us:intl:eng", "xkb:us:colemak:eng", "xkb:de:neo:ger", "xkb:es:cat:cat", "xkb:gb:dvorak:eng", }; const int len = arraysize(input_method_id); for (int i=0; iid()); EXPECT_EQ("us", descriptor->GetPreferredKeyboardLayout()); // This used to be "zh" but now we have "zh-CN" in input_methods.h, // hence this should be zh-CN now. ASSERT_TRUE(!descriptor->language_codes().empty()); EXPECT_EQ("zh-CN", descriptor->language_codes().at(0)); } TEST_F(InputMethodUtilTest, TestGetInputMethodIdsForLanguageCode) { std::multimap language_code_to_ids_map; language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id)); language_code_to_ids_map.insert(std::make_pair("ja", pinyin_ime_id)); language_code_to_ids_map.insert(std::make_pair("ja", "xkb:jp:jpn")); language_code_to_ids_map.insert(std::make_pair("fr", "xkb:fr:fra")); std::vector result; EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "ja", kAllInputMethods, &result)); EXPECT_EQ(3U, result.size()); EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "ja", kKeyboardLayoutsOnly, &result)); ASSERT_EQ(1U, result.size()); EXPECT_EQ("xkb:jp:jpn", result[0]); EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "fr", kAllInputMethods, &result)); ASSERT_EQ(1U, result.size()); EXPECT_EQ("xkb:fr:fra", result[0]); EXPECT_TRUE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "fr", kKeyboardLayoutsOnly, &result)); ASSERT_EQ(1U, result.size()); EXPECT_EQ("xkb:fr:fra", result[0]); EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "invalid_lang", kAllInputMethods, &result)); EXPECT_FALSE(util_.GetInputMethodIdsFromLanguageCodeInternal( language_code_to_ids_map, "invalid_lang", kKeyboardLayoutsOnly, &result)); } // US keyboard + English US UI = US keyboard only. TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_EnUs) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("en-US", *descriptor, &input_method_ids); ASSERT_EQ(1U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); } // US keyboard + Chinese UI = US keyboard + Pinyin IME. TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Zh) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("zh-CN", *descriptor, &input_method_ids); ASSERT_EQ(2U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); EXPECT_EQ(Id(pinyin_ime_id), input_method_ids[1]); // Pinyin for US keybaord. } // US keyboard + Russian UI = US keyboard + Russsian keyboard TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Ru) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("ru", *descriptor, &input_method_ids); ASSERT_EQ(2U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); EXPECT_EQ(Id("xkb:ru::rus"), input_method_ids[1]); // Russian keyboard. } // US keyboard + Traditional Chinese = US keyboard + chewing. TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_ZhTw) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("zh-TW", *descriptor, &input_method_ids); ASSERT_EQ(2U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); EXPECT_EQ(Id(zhuyin_ime_id), input_method_ids[1]); // Chewing. } // US keyboard + Thai = US keyboard + kesmanee. TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Th) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("th", *descriptor, &input_method_ids); ASSERT_EQ(2U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); EXPECT_EQ(Id("vkd_th"), input_method_ids[1]); // Kesmanee. } // US keyboard + Vietnamese = US keyboard + TCVN6064. TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Vi) { const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng")); // US keyboard. ASSERT_TRUE(NULL != descriptor); // ASSERT_NE doesn't compile. std::vector input_method_ids; util_.GetFirstLoginInputMethodIds("vi", *descriptor, &input_method_ids); ASSERT_EQ(2U, input_method_ids.size()); EXPECT_EQ(Id("xkb:us::eng"), input_method_ids[0]); EXPECT_EQ(Id("vkd_vi_tcvn"), input_method_ids[1]); // TCVN6064. } TEST_F(InputMethodUtilTest, TestGetLanguageCodesFromInputMethodIds) { std::vector input_method_ids; input_method_ids.push_back(Id("xkb:us::eng")); // English US. input_method_ids.push_back(Id("xkb:us:dvorak:eng")); // English US Dvorak. input_method_ids.push_back(Id(pinyin_ime_id)); // Pinyin input_method_ids.push_back(Id("xkb:fr::fra")); // French France. std::vector language_codes; util_.GetLanguageCodesFromInputMethodIds(input_method_ids, &language_codes); ASSERT_EQ(3U, language_codes.size()); EXPECT_EQ("en", language_codes[0]); EXPECT_EQ("zh-CN", language_codes[1]); EXPECT_EQ("fr", language_codes[2]); } // Test all supported descriptors to detect a typo in input_methods.txt. TEST_F(InputMethodUtilTest, TestIBusInputMethodText) { const std::map& id_to_descriptor = util_.GetIdToDesciptorMapForTesting(); for (std::map::const_iterator it = id_to_descriptor.begin(); it != id_to_descriptor.end(); ++it) { const std::string language_code = it->second.language_codes().at(0); const base::string16 display_name = l10n_util::GetDisplayNameForLocale(language_code, "en", false); // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are // allowed. See the text file for details. EXPECT_TRUE(language_code == "fil" || language_code.length() == 2 || (language_code.length() == 5 && language_code[2] == '-')) << "Invalid language code " << language_code; EXPECT_TRUE(l10n_util::IsValidLocaleSyntax(language_code)) << "Invalid language code " << language_code; EXPECT_FALSE(display_name.empty()) << "Invalid language code " << language_code; // On error, GetDisplayNameForLocale() returns the |language_code| as-is. EXPECT_NE(language_code, base::UTF16ToUTF8(display_name)) << "Invalid language code " << language_code; } } // Test the input method ID migration. TEST_F(InputMethodUtilTest, TestInputMethodIDMigration) { const char* const migration_cases[][2] = { {"ime:zh:pinyin", "zh-t-i0-pinyin"}, {"ime:zh-t:zhuyin", "zh-hant-t-i0-und"}, {"ime:zh-t:quick", "zh-hant-t-i0-cangjie-1987-x-m0-simplified"}, {"ime:jp:mozc_us", "nacl_mozc_us"}, {"ime:ko:hangul", "hangul_2set"}, {"m17n:deva_phone", "vkd_deva_phone"}, {"m17n:ar", "vkd_ar"}, {"t13n:hi", "hi-t-i0-und"}, {"unknown", "unknown"}, }; std::vector input_method_ids; for (size_t i = 0; i < arraysize(migration_cases); ++i) input_method_ids.push_back(migration_cases[i][0]); // Duplicated hangul_2set. input_method_ids.push_back("ime:ko:hangul_2set"); util_.MigrateInputMethods(&input_method_ids); EXPECT_EQ(arraysize(migration_cases), input_method_ids.size()); for (size_t i = 0; i < arraysize(migration_cases); ++i) { EXPECT_EQ( extension_ime_util::GetInputMethodIDByEngineID(migration_cases[i][1]), input_method_ids[i]); } } } // namespace input_method } // namespace chromeos