chromium/chrome/browser/ash/input_method/input_method_settings_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/input_method/input_method_settings.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/feature_list.h"
#include "base/strings/strcat.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/services/ime/public/mojom/input_method.mojom-shared.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {
namespace input_method {
namespace {

namespace mojom = ::ash::ime::mojom;

constexpr char kUsEnglishEngineId[] = "xkb:us::eng";
constexpr char kKoreanEngineId[] = "ko-t-i0-und";
constexpr char kPinyinEngineId[] = "zh-t-i0-pinyin";
constexpr char kZhuyinEngineId[] = "zh-hant-t-i0-und";
constexpr char kJapaneseEngineId[] = "nacl_mozc_jp";

constexpr char kVietnameseVniEngineId[] = "vkd_vi_vni";
constexpr char kVietnameseTelexEngineId[] = "vkd_vi_telex";

void RegisterTestingPrefs(TestingPrefServiceSimple& prefs,
                          const base::Value::Dict& dict) {
  prefs.registry()->RegisterDictionaryPref(
      ::prefs::kLanguageInputMethodSpecificSettings);
  prefs.Set(::prefs::kLanguageInputMethodSpecificSettings,
            base::Value(dict.Clone()));
}

TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsDefault) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId);

  ASSERT_TRUE(settings->is_latin_settings());
  const auto& latin_settings = *settings->get_latin_settings();
  EXPECT_FALSE(latin_settings.autocorrect);
  EXPECT_TRUE(latin_settings.predictive_writing);
}

TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsWithMultiwordEnabled) {
  base::test::ScopedFeatureList features;
  TestingPrefServiceSimple prefs;
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kUsEnglishEngineId,
                                     ".physicalKeyboardAutoCorrectionLevel"}),
                       1);
  dict.SetByDottedPath(
      base::StrCat(
          {kUsEnglishEngineId, ".physicalKeyboardEnablePredictiveWriting"}),
      base::Value(true));
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId);

  ASSERT_TRUE(settings->is_latin_settings());
  const auto& latin_settings = *settings->get_latin_settings();
  EXPECT_TRUE(latin_settings.autocorrect);
  EXPECT_TRUE(latin_settings.predictive_writing);
}

TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsWithMultiwordDisabled) {
  base::test::ScopedFeatureList features;
  TestingPrefServiceSimple prefs;
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kUsEnglishEngineId,
                                     ".physicalKeyboardAutoCorrectionLevel"}),
                       1);
  dict.SetByDottedPath(
      base::StrCat(
          {kUsEnglishEngineId, ".physicalKeyboardEnablePredictiveWriting"}),
      base::Value(false));
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId);

  ASSERT_TRUE(settings->is_latin_settings());
  const auto& latin_settings = *settings->get_latin_settings();
  EXPECT_TRUE(latin_settings.autocorrect);
  EXPECT_FALSE(latin_settings.predictive_writing);
}

TEST(CreateSettingsFromPrefsTest,
     PredictiveWritingEnabledWhenMultiWordAllowedAndEnabled) {
  base::test::ScopedFeatureList features;
  TestingPrefServiceSimple prefs;
  base::Value::Dict dict;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId);

  ASSERT_TRUE(settings->is_latin_settings());
  const auto& latin_settings = *settings->get_latin_settings();
  EXPECT_TRUE(latin_settings.predictive_writing);
}

TEST(CreateSettingsFromPrefsTest,
     PredictiveWritingDisabledWhenMultiwordDisabled) {
  base::test::ScopedFeatureList features;
  features.InitWithFeatures({}, {features::kAssistMultiWord});
  TestingPrefServiceSimple prefs;
  base::Value::Dict dict;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId);

  ASSERT_TRUE(settings->is_latin_settings());
  const auto& latin_settings = *settings->get_latin_settings();
  EXPECT_FALSE(latin_settings.predictive_writing);
}

TEST(CreateSettingsFromPrefsTest, CreateKoreanSettingsDefault) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kKoreanEngineId);

  ASSERT_TRUE(settings->is_korean_settings());
  const auto& korean_settings = *settings->get_korean_settings();
  EXPECT_EQ(korean_settings.layout, mojom::KoreanLayout::kDubeolsik);
  EXPECT_FALSE(korean_settings.input_multiple_syllables);
}

TEST(CreateSettingsFromPrefsTest, CreateKoreanSettings) {
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kKoreanEngineId, ".koreanKeyboardLayout"}),
                       "3 Set (390) / 세벌식 (390)");
  dict.SetByDottedPath(
      base::StrCat({kKoreanEngineId, ".koreanEnableSyllableInput"}), false);
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kKoreanEngineId);

  ASSERT_TRUE(settings->is_korean_settings());
  const auto& korean_settings = *settings->get_korean_settings();
  EXPECT_EQ(korean_settings.layout, mojom::KoreanLayout::kSebeolsik390);
  EXPECT_TRUE(korean_settings.input_multiple_syllables);
}

TEST(CreateSettingsFromPrefsTest, CreatePinyinSettingsDefault) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kPinyinEngineId);

  ASSERT_TRUE(settings->is_pinyin_settings());
  const auto& pinyin_settings = *settings->get_pinyin_settings();
  ASSERT_TRUE(pinyin_settings.fuzzy_pinyin);
  const auto& fuzzy_pinyin = *pinyin_settings.fuzzy_pinyin;
  EXPECT_FALSE(fuzzy_pinyin.an_ang);
  EXPECT_FALSE(fuzzy_pinyin.en_eng);
  EXPECT_FALSE(fuzzy_pinyin.ian_iang);
  EXPECT_FALSE(fuzzy_pinyin.k_g);
  EXPECT_FALSE(fuzzy_pinyin.r_l);
  EXPECT_FALSE(fuzzy_pinyin.uan_uang);
  EXPECT_FALSE(fuzzy_pinyin.c_ch);
  EXPECT_FALSE(fuzzy_pinyin.f_h);
  EXPECT_FALSE(fuzzy_pinyin.in_ing);
  EXPECT_FALSE(fuzzy_pinyin.l_n);
  EXPECT_FALSE(fuzzy_pinyin.s_sh);
  EXPECT_FALSE(fuzzy_pinyin.z_zh);
  EXPECT_EQ(pinyin_settings.layout, mojom::PinyinLayout::kUsQwerty);
  EXPECT_TRUE(pinyin_settings.use_hyphen_and_equals_to_page_candidates);
  EXPECT_TRUE(pinyin_settings.use_comma_and_period_to_page_candidates);
  EXPECT_TRUE(pinyin_settings.default_to_chinese);
  EXPECT_FALSE(pinyin_settings.default_to_full_width_characters);
  EXPECT_TRUE(pinyin_settings.default_to_full_width_punctuation);
}

TEST(CreateSettingsFromPrefsTest, CreatePinyinSettings) {
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".en:eng"}), true);
  dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".k:g"}), true);
  dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".in:ing"}), true);
  dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".xkbLayout"}),
                       "Colemak");
  dict.SetByDottedPath(
      base::StrCat({kPinyinEngineId, ".pinyinEnableLowerPaging"}), false);
  dict.SetByDottedPath(
      base::StrCat({kPinyinEngineId, ".pinyinEnableUpperPaging"}), false);
  dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".pinyinDefaultChinese"}),
                       false);
  dict.SetByDottedPath(
      base::StrCat({kPinyinEngineId, ".pinyinFullWidthCharacter"}), true);
  dict.SetByDottedPath(
      base::StrCat({kPinyinEngineId, ".pinyinChinesePunctuation"}), false);
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kPinyinEngineId);

  ASSERT_TRUE(settings->is_pinyin_settings());
  const auto& pinyin_settings = *settings->get_pinyin_settings();
  ASSERT_TRUE(pinyin_settings.fuzzy_pinyin);
  const auto& fuzzy_pinyin = *pinyin_settings.fuzzy_pinyin;
  EXPECT_FALSE(fuzzy_pinyin.an_ang);
  EXPECT_TRUE(fuzzy_pinyin.en_eng);
  EXPECT_FALSE(fuzzy_pinyin.ian_iang);
  EXPECT_TRUE(fuzzy_pinyin.k_g);
  EXPECT_FALSE(fuzzy_pinyin.r_l);
  EXPECT_FALSE(fuzzy_pinyin.uan_uang);
  EXPECT_FALSE(fuzzy_pinyin.c_ch);
  EXPECT_FALSE(fuzzy_pinyin.f_h);
  EXPECT_TRUE(fuzzy_pinyin.in_ing);
  EXPECT_FALSE(fuzzy_pinyin.l_n);
  EXPECT_FALSE(fuzzy_pinyin.s_sh);
  EXPECT_FALSE(fuzzy_pinyin.z_zh);
  EXPECT_EQ(pinyin_settings.layout, mojom::PinyinLayout::kColemak);
  EXPECT_FALSE(pinyin_settings.use_comma_and_period_to_page_candidates);
  EXPECT_FALSE(pinyin_settings.use_hyphen_and_equals_to_page_candidates);
  EXPECT_FALSE(pinyin_settings.default_to_chinese);
  EXPECT_TRUE(pinyin_settings.default_to_full_width_characters);
  EXPECT_FALSE(pinyin_settings.default_to_full_width_punctuation);
}

TEST(CreateSettingsFromPrefsTest, CreateZhuyinSettingsDefault) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kZhuyinEngineId);

  ASSERT_TRUE(settings->is_zhuyin_settings());
  const auto& zhuyin_settings = *settings->get_zhuyin_settings();
  EXPECT_EQ(zhuyin_settings.layout, mojom::ZhuyinLayout::kStandard);
  EXPECT_EQ(zhuyin_settings.selection_keys,
            mojom::ZhuyinSelectionKeys::k1234567890);
  EXPECT_EQ(zhuyin_settings.page_size, 10u);
}

TEST(CreateSettingsFromPrefsTest, CreateVietnameseVniSettings) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kVietnameseVniEngineId);

  ASSERT_TRUE(settings->is_vietnamese_vni_settings());
  const auto& vni_settings = *settings->get_vietnamese_vni_settings();
  EXPECT_TRUE(vni_settings.allow_flexible_diacritics);
}

TEST(CreateSettingsFromPrefsTest, CreateVietnameseTelexSettings) {
  base::Value::Dict dict;
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings =
      CreateSettingsFromPrefs(prefs, kVietnameseTelexEngineId);

  ASSERT_TRUE(settings->is_vietnamese_telex_settings());
  const auto& telex_settings = *settings->get_vietnamese_telex_settings();
  EXPECT_TRUE(telex_settings.allow_flexible_diacritics);
}

TEST(CreateSettingsFromPrefsTest, CreateZhuyinSettings) {
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinKeyboardLayout"}),
                       "IBM");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinSelectKeys"}),
                       "asdfghjkl;");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinPageSize"}), "8");
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  const auto settings = CreateSettingsFromPrefs(prefs, kZhuyinEngineId);

  ASSERT_TRUE(settings->is_zhuyin_settings());
  const auto& zhuyin_settings = *settings->get_zhuyin_settings();
  EXPECT_EQ(zhuyin_settings.layout, mojom::ZhuyinLayout::kIbm);
  EXPECT_EQ(zhuyin_settings.selection_keys,
            mojom::ZhuyinSelectionKeys::kAsdfghjkl);
  EXPECT_EQ(zhuyin_settings.page_size, 8u);
}

TEST(CreateSettingsFromPrefsTest, CreateJapaneseSettings) {
  using ::ash::ime::mojom::JapaneseSettings;

  base::Value::Dict jp_prefs;
  jp_prefs.Set("AutomaticallySendStatisticsToGoogle", false);
  jp_prefs.Set("AutomaticallySwitchToHalfwidth", false);
  jp_prefs.Set("JapaneseDisableSuggestions", true);
  jp_prefs.Set("JapaneseInputMode", "Kana");
  jp_prefs.Set("JapaneseKeymapStyle", "ChromeOs");
  jp_prefs.Set("JapanesePunctuationStyle", "CommaPeriod");
  jp_prefs.Set("JapaneseSectionShortcut", "ASDFGHJKL");
  jp_prefs.Set("JapaneseSpaceInputStyle", "Fullwidth");
  jp_prefs.Set("JapaneseSymbolStyle", "SquareBracketMiddleDot");
  jp_prefs.Set("ShiftKeyModeStyle", "Off");
  jp_prefs.Set("UseInputHistory", false);
  jp_prefs.Set("UseSystemDictionary", false);
  jp_prefs.Set("numberOfSuggestions", 5);

  base::Value::Dict full_prefs;
  full_prefs.Set(kJapaneseEngineId, std::move(jp_prefs));
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, full_prefs);

  const mojom::InputMethodSettingsPtr settings =
      CreateSettingsFromPrefs(prefs, kJapaneseEngineId);

  ASSERT_TRUE(settings->is_japanese_settings());
  mojom::JapaneseSettingsPtr expected = mojom::JapaneseSettings::New();
  expected->automatically_send_statistics_to_google = false;
  expected->automatically_switch_to_halfwidth = true;
  expected->disable_personalized_suggestions = true;
  expected->input_mode = JapaneseSettings::InputMode::kKana;
  expected->keymap_style = JapaneseSettings::KeymapStyle::kChromeos;
  expected->punctuation_style =
      JapaneseSettings::PunctuationStyle::kCommaPeriod;
  expected->selection_shortcut =
      JapaneseSettings::SelectionShortcut::kAsdfghjkl;
  expected->space_input_style = JapaneseSettings::SpaceInputStyle::kFullWidth;
  expected->symbol_style =
      JapaneseSettings::SymbolStyle::kSquareBracketMiddleDot;
  expected->shift_key_mode_style = JapaneseSettings::ShiftKeyModeStyle::kOff;
  expected->use_input_history = false;
  expected->use_system_dictionary = false;
  expected->number_of_suggestions = 5;
  EXPECT_EQ(settings->get_japanese_settings(), expected);
}

TEST(CreateSettingsFromPrefsTest, AutocorrectIsSupportedForLatin) {
  ASSERT_TRUE(IsAutocorrectSupported("xkb:ca:multix:fra"));
  ASSERT_TRUE(IsAutocorrectSupported("xkb:de::ger"));
  ASSERT_TRUE(IsAutocorrectSupported("xkb:us::eng"));
  ASSERT_TRUE(IsAutocorrectSupported("xkb:us:intl_pc:por"));
  ASSERT_TRUE(IsAutocorrectSupported("xkb:us:workman-intl:eng"));
}

TEST(CreateSettingsFromPrefsTest, AutocorrectIsNotSupportedForNonLatin) {
  ASSERT_FALSE(IsAutocorrectSupported("ko-t-i0-und"));
  ASSERT_FALSE(IsAutocorrectSupported("nacl_mozc_us"));
  ASSERT_FALSE(IsAutocorrectSupported("xkb:am:phonetic:arm"));
  ASSERT_FALSE(IsAutocorrectSupported("xkb:in::eng"));
  ASSERT_FALSE(IsAutocorrectSupported("zh-hant-t-i0-pinyin"));
  ASSERT_FALSE(IsAutocorrectSupported("zh-hant-t-i0-und"));
  ASSERT_FALSE(IsAutocorrectSupported("zh-t-i0-pinyin"));
}

TEST(InputMethodSettingsTest, GetLanguageSpecificInputMethodSettings) {
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}), "DEFAULT1");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}), "DEFAULT2");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}), "DEFAULT3");
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  base::Value::Dict new_prefs;
  new_prefs.Set("field2", "CHANGED");
  EXPECT_EQ(
      *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field1"),
      "DEFAULT1");
  EXPECT_EQ(
      *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field2"),
      "DEFAULT2");
  EXPECT_EQ(
      *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field3"),
      "DEFAULT3");
}

TEST(InputMethodSettingsTest,
     SetLanguageInputMethodSpecificSettingExistingEngine) {
  base::Value::Dict dict;
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}), "DEFAULT");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}), "DEFAULT");
  dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}), "DEFAULT");
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  base::Value::Dict new_prefs;
  new_prefs.Set("field2", "CHANGED");
  SetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, new_prefs);

  const base::Value* prefs_val =
      prefs.GetUserPref(::prefs::kLanguageInputMethodSpecificSettings);

  base::Value::Dict expected;
  expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}),
                           "DEFAULT");
  expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}),
                           "CHANGED");
  expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}),
                           "DEFAULT");

  EXPECT_EQ(*prefs_val->GetIfDict(), expected);
}

TEST(InputMethodSettingsTest, SetLanguageInputMethodSpecificSettingNewEngine) {
  base::Value::Dict dict;
  dict.SetByDottedPath("existing-engine.field1", "DEFAULT");
  TestingPrefServiceSimple prefs;
  RegisterTestingPrefs(prefs, dict);

  base::Value::Dict new_prefs;
  new_prefs.Set("field1", "NEW");
  SetLanguageInputMethodSpecificSetting(prefs, "brand-new-engine", new_prefs);

  const base::Value* prefs_val =
      prefs.GetUserPref(::prefs::kLanguageInputMethodSpecificSettings);

  base::Value::Dict expected;
  expected.SetByDottedPath("existing-engine.field1", "DEFAULT");
  expected.SetByDottedPath("brand-new-engine.field1", "NEW");

  EXPECT_EQ(*prefs_val->GetIfDict(), expected);
}

}  // namespace
}  // namespace input_method
}  // namespace ash