blob: 660161b04697df6e5bf1cd8869fd553a71b49507 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "composer/table.h"
#include "base/file_util.h"
#include "base/port.h"
#include "base/system_util.h"
#include "composer/internal/composition_input.h"
#include "config/config.pb.h"
#include "config/config_handler.h"
#include "testing/base/public/gunit.h"
#include "session/commands.pb.h"
DECLARE_string(test_tmpdir);
namespace mozc {
namespace composer {
using mozc::config::Config;
using mozc::commands::Request;
static void InitTable(Table* table) {
// "あ"
table->AddRule("a", "\xe3\x81\x82", "");
// "い"
table->AddRule("i", "\xe3\x81\x84", "");
// "か"
table->AddRule("ka", "\xe3\x81\x8b", "");
// "き"
table->AddRule("ki", "\xe3\x81\x8d", "");
// "く"
table->AddRule("ku", "\xe3\x81\x8f", "");
// "け"
table->AddRule("ke", "\xe3\x81\x91", "");
// "こ"
table->AddRule("ko", "\xe3\x81\x93", "");
// "っ"
table->AddRule("kk", "\xe3\x81\xa3", "k");
// "な"
table->AddRule("na", "\xe3\x81\xaa", "");
// "に"
table->AddRule("ni", "\xe3\x81\xab", "");
// "ん"
table->AddRule("n", "\xe3\x82\x93", "");
// "ん"
table->AddRule("nn", "\xe3\x82\x93", "");
}
string GetResult(const Table &table, const string &key) {
const Entry *entry = table.LookUp(key);
if (entry == NULL) {
return "<NULL>";
}
return entry->result();
}
string GetInput(const Table &table, const string &key) {
const Entry *entry = table.LookUp(key);
if (entry == NULL) {
return "<NULL>";
}
return entry->input();
}
class TableTest : public testing::Test {
protected:
TableTest() {}
virtual void SetUp() {
SystemUtil::SetUserProfileDirectory(FLAGS_test_tmpdir);
config::ConfigHandler::GetDefaultConfig(&default_config_);
config::ConfigHandler::SetConfig(default_config_);
}
virtual void TearDown() {
config::ConfigHandler::SetConfig(default_config_);
}
void SetCustomRomanTable(const string &roman_table) {
config::Config config;
config::ConfigHandler::GetConfig(&config);
config.set_custom_roman_table(roman_table);
config::ConfigHandler::SetConfig(config);
}
config::Config default_config_;
private:
DISALLOW_COPY_AND_ASSIGN(TableTest);
};
TEST_F(TableTest, LookUp) {
static const struct TestCase {
const char* input;
const bool expected_result;
const char* expected_output;
const char* expected_pending;
} test_cases[] = {
// "あ"
{ "a", true, "\xe3\x81\x82", "" },
{ "k", false, "", "" },
// "か"
{ "ka", true, "\xe3\x81\x8b", "" },
// "き"
{ "ki", true, "\xe3\x81\x8d", "" },
// "く"
{ "ku", true, "\xe3\x81\x8f", "" },
// "っ"
{ "kk", true, "\xe3\x81\xa3", "k" },
{ "aka", false, "", "" },
// "な"
{ "na", true, "\xe3\x81\xaa", "" },
// "ん"
{ "n", true, "\xe3\x82\x93", "" },
// "ん"
{ "nn", true, "\xe3\x82\x93", "" },
};
static const int size = arraysize(test_cases);
Table table;
InitTable(&table);
for (int i = 0; i < size; ++i) {
const TestCase& test = test_cases[i];
string output;
string pending;
const Entry* entry;
entry = table.LookUp(test.input);
EXPECT_EQ(test.expected_result, (entry != NULL));
if (entry == NULL) {
continue;
}
EXPECT_EQ(test.expected_output, entry->result());
EXPECT_EQ(test.expected_pending, entry->pending());
}
}
TEST_F(TableTest, LookUpPredictiveAll) {
Table table;
InitTable(&table);
vector<const Entry *> results;
table.LookUpPredictiveAll("k", &results);
EXPECT_EQ(6, results.size());
}
TEST_F(TableTest, Punctuations) {
static const struct TestCase {
config::Config::PunctuationMethod method;
const char *input;
const char *expected;
} test_cases[] = {
// "、"
{ config::Config::KUTEN_TOUTEN, ",", "\xe3\x80\x81" },
// "。"
{ config::Config::KUTEN_TOUTEN, ".", "\xe3\x80\x82" },
// ","
{ config::Config::COMMA_PERIOD, ",", "\xef\xbc\x8c" },
// "."
{ config::Config::COMMA_PERIOD, ".", "\xef\xbc\x8e" },
// "、"
{ config::Config::KUTEN_PERIOD, ",", "\xe3\x80\x81" },
// "."
{ config::Config::KUTEN_PERIOD, ".", "\xef\xbc\x8e" },
// ","
{ config::Config::COMMA_TOUTEN, ",", "\xef\xbc\x8c" },
// "。"
{ config::Config::COMMA_TOUTEN, ".", "\xe3\x80\x82" },
};
const string config_file = FileUtil::JoinPath(FLAGS_test_tmpdir,
"mozc_config_test_tmp");
FileUtil::Unlink(config_file);
config::ConfigHandler::SetConfigFileName(config_file);
config::ConfigHandler::Reload();
commands::Request request;
for (int i = 0; i < arraysize(test_cases); ++i) {
config::Config config;
config.set_punctuation_method(test_cases[i].method);
EXPECT_TRUE(config::ConfigHandler::SetConfig(config));
Table table;
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config));
const Entry *entry = table.LookUp(test_cases[i].input);
EXPECT_TRUE(entry != NULL) << "Failed index = " << i;
if (entry) {
EXPECT_EQ(test_cases[i].expected, entry->result());
}
}
}
TEST_F(TableTest, Symbols) {
static const struct TestCase {
config::Config::SymbolMethod method;
const char *input;
const char *expected;
} test_cases[] = {
// "「"
{ config::Config::CORNER_BRACKET_MIDDLE_DOT, "[", "\xe3\x80\x8c" },
// "」"
{ config::Config::CORNER_BRACKET_MIDDLE_DOT, "]", "\xe3\x80\x8d" },
// "・"
{ config::Config::CORNER_BRACKET_MIDDLE_DOT, "/", "\xe3\x83\xbb" },
{ config::Config::SQUARE_BRACKET_SLASH, "[", "[" },
{ config::Config::SQUARE_BRACKET_SLASH, "]", "]" },
// "/"
{ config::Config::SQUARE_BRACKET_SLASH, "/", "\xef\xbc\x8f" },
// "「"
{ config::Config::CORNER_BRACKET_SLASH, "[", "\xe3\x80\x8c" },
// "」"
{ config::Config::CORNER_BRACKET_SLASH, "]", "\xe3\x80\x8d" },
// "/"
{ config::Config::CORNER_BRACKET_SLASH, "/", "\xef\xbc\x8f" },
{ config::Config::SQUARE_BRACKET_MIDDLE_DOT, "[", "[" },
{ config::Config::SQUARE_BRACKET_MIDDLE_DOT, "]", "]" },
// "・"
{ config::Config::SQUARE_BRACKET_MIDDLE_DOT, "/", "\xe3\x83\xbb" },
};
const string config_file = FileUtil::JoinPath(FLAGS_test_tmpdir,
"mozc_config_test_tmp");
FileUtil::Unlink(config_file);
config::ConfigHandler::SetConfigFileName(config_file);
config::ConfigHandler::Reload();
commands::Request request;
for (int i = 0; i < arraysize(test_cases); ++i) {
config::Config config;
config.set_symbol_method(test_cases[i].method);
EXPECT_TRUE(config::ConfigHandler::SetConfig(config));
Table table;
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config));
const Entry *entry = table.LookUp(test_cases[i].input);
EXPECT_TRUE(entry != NULL) << "Failed index = " << i;
if (entry) {
EXPECT_EQ(test_cases[i].expected, entry->result());
}
}
}
TEST_F(TableTest, KanaSuppressed) {
config::Config config;
config::ConfigHandler::GetConfig(&config);
config.set_preedit_method(config::Config::KANA);
config::ConfigHandler::SetConfig(config);
commands::Request request;
Table table;
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config));
const Entry *entry = table.LookUp("a");
ASSERT_TRUE(entry != NULL);
// "あ"
EXPECT_EQ("\xE3\x81\x82", entry->result());
EXPECT_TRUE(entry->pending().empty());
}
TEST_F(TableTest, KanaCombination) {
Table table;
commands::Request request;
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, default_config_));
// "か゛"
const Entry *entry = table.LookUp("\xE3\x81\x8B\xE3\x82\x9B");
ASSERT_TRUE(entry != NULL);
// "が"
EXPECT_EQ("\xE3\x81\x8C", entry->result());
EXPECT_TRUE(entry->pending().empty());
}
TEST_F(TableTest, InvalidEntryTest) {
{
Table table;
EXPECT_FALSE(table.IsLoopingEntry("a", "b"));
table.AddRule("a", "aa", "b");
EXPECT_TRUE(table.IsLoopingEntry("b", "a"));
table.AddRule("b", "aa", "a"); // looping
EXPECT_TRUE(table.LookUp("a") != NULL);
EXPECT_TRUE(table.LookUp("b") == NULL);
}
{
Table table;
EXPECT_FALSE(table.IsLoopingEntry("a", "ba"));
table.AddRule("a", "aa", "ba");
EXPECT_TRUE(table.IsLoopingEntry("b", "a"));
table.AddRule("b", "aa", "a"); // looping
EXPECT_TRUE(table.LookUp("a") != NULL);
EXPECT_TRUE(table.LookUp("b") == NULL);
}
{
Table table;
EXPECT_FALSE(table.IsLoopingEntry("a", "b"));
table.AddRule("a", "aa", "b");
EXPECT_FALSE(table.IsLoopingEntry("b", "c"));
table.AddRule("b", "aa", "c");
EXPECT_FALSE(table.IsLoopingEntry("c", "d"));
table.AddRule("c", "aa", "d");
EXPECT_TRUE(table.IsLoopingEntry("d", "a"));
table.AddRule("d", "aa", "a"); // looping
EXPECT_TRUE(table.LookUp("a") != NULL);
EXPECT_TRUE(table.LookUp("b") != NULL);
EXPECT_TRUE(table.LookUp("c") != NULL);
EXPECT_TRUE(table.LookUp("d") == NULL);
}
{
Table table;
table.AddRule("wa", "WA", "");
table.AddRule("ww", "X", "w");
EXPECT_FALSE(table.IsLoopingEntry("www", "ww"));
table.AddRule("www", "W", "ww"); // not looping
EXPECT_TRUE(table.LookUp("wa") != NULL);
EXPECT_TRUE(table.LookUp("ww") != NULL);
EXPECT_TRUE(table.LookUp("www") != NULL);
}
{
Table table;
table.AddRule("wa", "WA", "");
table.AddRule("www", "W", "ww");
EXPECT_FALSE(table.IsLoopingEntry("ww", "w"));
table.AddRule("ww", "X", "w");
EXPECT_TRUE(table.LookUp("wa") != NULL);
EXPECT_TRUE(table.LookUp("ww") != NULL);
EXPECT_TRUE(table.LookUp("www") != NULL);
}
{
Table table;
EXPECT_TRUE(table.IsLoopingEntry("a", "a"));
table.AddRule("a", "aa", "a"); // looping
EXPECT_TRUE(table.LookUp("a") == NULL);
}
// Too long input
{
Table table;
string too_long;
// Maximum size is 300 now.
for (int i = 0; i < 1024; ++i) {
too_long += 'a';
}
table.AddRule(too_long, "test", "test");
EXPECT_TRUE(table.LookUp(too_long) == NULL);
table.AddRule("a", too_long, "test");
EXPECT_TRUE(table.LookUp("a") == NULL);
table.AddRule("a", "test", too_long);
EXPECT_TRUE(table.LookUp("a") == NULL);
}
// reasonably long
{
Table table;
string reasonably_long;
// Maximum size is 300 now.
for (int i = 0; i < 200; ++i) {
reasonably_long += 'a';
}
table.AddRule(reasonably_long, "test", "test");
EXPECT_TRUE(table.LookUp(reasonably_long) != NULL);
table.AddRule("a", reasonably_long, "test");
EXPECT_TRUE(table.LookUp("a") != NULL);
table.AddRule("a", "test", reasonably_long);
EXPECT_TRUE(table.LookUp("a") != NULL);
}
}
TEST_F(TableTest, CustomPunctuationsAndSymbols) {
// Test against Issue2465801.
string custom_roman_table;
custom_roman_table.append("mozc\tMOZC\n");
custom_roman_table.append(",\tCOMMA\n");
custom_roman_table.append(".\tPERIOD\n");
custom_roman_table.append("/\tSLASH\n");
custom_roman_table.append("[\tOPEN\n");
custom_roman_table.append("]\tCLOSE\n");
SetCustomRomanTable(custom_roman_table);
Table table;
commands::Request request;
config::Config config;
config::ConfigHandler::GetConfig(&config);
table.InitializeWithRequestAndConfig(request, config);
const Entry *entry = NULL;
entry = table.LookUp("mozc");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("MOZC", entry->result());
entry = table.LookUp(",");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("COMMA", entry->result());
entry = table.LookUp(".");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("PERIOD", entry->result());
entry = table.LookUp("/");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("SLASH", entry->result());
entry = table.LookUp("[");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("OPEN", entry->result());
entry = table.LookUp("]");
ASSERT_TRUE(entry != NULL);
EXPECT_EQ("CLOSE", entry->result());
}
TEST_F(TableTest, CaseSensitive) {
Table table;
table.AddRule("a", "[a]", "");
table.AddRule("A", "[A]", "");
table.AddRule("ba", "[ba]", "");
table.AddRule("BA", "[BA]", "");
table.AddRule("Ba", "[Ba]", "");
// The rule of "bA" is intentionally dropped.
// table.AddRule("bA", "[bA]", "");
table.AddRule("za", "[za]", "");
// case insensitive
table.set_case_sensitive(false);
EXPECT_EQ("[a]", GetResult(table, "a"));
EXPECT_EQ("[a]", GetResult(table, "A"));
EXPECT_EQ("[ba]", GetResult(table, "ba"));
EXPECT_EQ("[ba]", GetResult(table, "BA"));
EXPECT_EQ("[ba]", GetResult(table, "Ba"));
EXPECT_EQ("[ba]", GetResult(table, "bA"));
EXPECT_EQ("a", GetInput(table, "a"));
EXPECT_EQ("a", GetInput(table, "A"));
EXPECT_EQ("ba", GetInput(table, "ba"));
EXPECT_EQ("ba", GetInput(table, "BA"));
EXPECT_EQ("ba", GetInput(table, "Ba"));
EXPECT_EQ("ba", GetInput(table, "bA"));
// Test for HasSubRules
EXPECT_TRUE(table.HasSubRules("Z"));
{ // Test for LookUpPrefix
const Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("bA", &key_length, &fixed);
EXPECT_TRUE(entry != NULL);
EXPECT_EQ("[ba]", entry->result());
EXPECT_EQ(2, key_length);
EXPECT_TRUE(fixed);
}
// case sensitive
table.set_case_sensitive(true);
EXPECT_TRUE(table.case_sensitive());
EXPECT_EQ("[a]", GetResult(table, "a"));
EXPECT_EQ("[A]", GetResult(table, "A"));
EXPECT_EQ("[ba]", GetResult(table, "ba"));
EXPECT_EQ("[BA]", GetResult(table, "BA"));
EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
EXPECT_EQ("<NULL>", GetResult(table, "bA"));
EXPECT_EQ("a", GetInput(table, "a"));
EXPECT_EQ("A", GetInput(table, "A"));
EXPECT_EQ("ba", GetInput(table, "ba"));
EXPECT_EQ("BA", GetInput(table, "BA"));
EXPECT_EQ("Ba", GetInput(table, "Ba"));
EXPECT_EQ("<NULL>", GetInput(table, "bA"));
// Test for HasSubRules
EXPECT_FALSE(table.HasSubRules("Z"));
{ // Test for LookUpPrefix
const Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("bA", &key_length, &fixed);
EXPECT_TRUE(entry == NULL);
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
}
}
TEST_F(TableTest, CaseSensitivity) {
commands::Request request;
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
EXPECT_FALSE(table.case_sensitive());
}
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
table.AddRule("", "", "");
EXPECT_FALSE(table.case_sensitive());
}
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
table.AddRule("a", "", "");
EXPECT_FALSE(table.case_sensitive());
}
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
table.AddRule("A", "", "");
EXPECT_TRUE(table.case_sensitive());
}
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
table.AddRule("a{A}a", "", "");
EXPECT_FALSE(table.case_sensitive());
}
{
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
table.AddRule("A{A}A", "", "");
EXPECT_TRUE(table.case_sensitive());
}
}
// This test case was needed because the case sensitivity was configured
// by the configuration.
// Currently the case sensitivity is independent from the configuration.
TEST_F(TableTest, CaseSensitiveByConfiguration) {
config::Config config;
commands::Request request;
Table table;
// config::Config::OFF
{
config.set_shift_key_mode_switch(config::Config::OFF);
EXPECT_TRUE(config::ConfigHandler::SetConfig(config));
table.InitializeWithRequestAndConfig(request, config);
table.AddRule("a", "[a]", "");
table.AddRule("A", "[A]", "");
table.AddRule("ba", "[ba]", "");
table.AddRule("BA", "[BA]", "");
table.AddRule("Ba", "[Ba]", "");
EXPECT_TRUE(table.case_sensitive());
EXPECT_EQ("[a]", GetResult(table, "a"));
EXPECT_EQ("[A]", GetResult(table, "A"));
EXPECT_EQ("[ba]", GetResult(table, "ba"));
EXPECT_EQ("[BA]", GetResult(table, "BA"));
EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
EXPECT_EQ("<NULL>", GetResult(table, "bA"));
EXPECT_EQ("a", GetInput(table, "a"));
EXPECT_EQ("A", GetInput(table, "A"));
EXPECT_EQ("ba", GetInput(table, "ba"));
EXPECT_EQ("BA", GetInput(table, "BA"));
EXPECT_EQ("Ba", GetInput(table, "Ba"));
EXPECT_EQ("<NULL>", GetInput(table, "bA"));
// Test for HasSubRules
EXPECT_FALSE(table.HasSubRules("Z"));
{ // Test for LookUpPrefix
const Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("bA", &key_length, &fixed);
EXPECT_TRUE(entry == NULL);
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
}
}
// config::Config::ASCII_INPUT_MODE
{
config.set_shift_key_mode_switch(config::Config::ASCII_INPUT_MODE);
EXPECT_TRUE(config::ConfigHandler::SetConfig(config));
table.InitializeWithRequestAndConfig(request, config);
table.AddRule("a", "[a]", "");
table.AddRule("A", "[A]", "");
table.AddRule("ba", "[ba]", "");
table.AddRule("BA", "[BA]", "");
table.AddRule("Ba", "[Ba]", "");
EXPECT_TRUE(table.case_sensitive());
EXPECT_EQ("[a]", GetResult(table, "a"));
EXPECT_EQ("[A]", GetResult(table, "A"));
EXPECT_EQ("[ba]", GetResult(table, "ba"));
EXPECT_EQ("[BA]", GetResult(table, "BA"));
EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
EXPECT_EQ("<NULL>", GetResult(table, "bA"));
EXPECT_EQ("a", GetInput(table, "a"));
EXPECT_EQ("A", GetInput(table, "A"));
EXPECT_EQ("ba", GetInput(table, "ba"));
EXPECT_EQ("BA", GetInput(table, "BA"));
EXPECT_EQ("Ba", GetInput(table, "Ba"));
EXPECT_EQ("<NULL>", GetInput(table, "bA"));
// Test for HasSubRules
EXPECT_FALSE(table.HasSubRules("Z"));
{ // Test for LookUpPrefix
const Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("bA", &key_length, &fixed);
EXPECT_TRUE(entry == NULL);
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
}
}
// config::Config::KATAKANA_INPUT_MODE
{
config.set_shift_key_mode_switch(config::Config::KATAKANA_INPUT_MODE);
EXPECT_TRUE(config::ConfigHandler::SetConfig(config));
table.InitializeWithRequestAndConfig(request, config);
table.AddRule("a", "[a]", "");
table.AddRule("A", "[A]", "");
table.AddRule("ba", "[ba]", "");
table.AddRule("BA", "[BA]", "");
table.AddRule("Ba", "[Ba]", "");
EXPECT_TRUE(table.case_sensitive());
EXPECT_EQ("[a]", GetResult(table, "a"));
EXPECT_EQ("[A]", GetResult(table, "A"));
EXPECT_EQ("[ba]", GetResult(table, "ba"));
EXPECT_EQ("[BA]", GetResult(table, "BA"));
EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
EXPECT_EQ("<NULL>", GetResult(table, "bA"));
EXPECT_EQ("a", GetInput(table, "a"));
EXPECT_EQ("A", GetInput(table, "A"));
EXPECT_EQ("ba", GetInput(table, "ba"));
EXPECT_EQ("BA", GetInput(table, "BA"));
EXPECT_EQ("Ba", GetInput(table, "Ba"));
EXPECT_EQ("<NULL>", GetInput(table, "bA"));
// Test for HasSubRules
EXPECT_FALSE(table.HasSubRules("Z"));
{ // Test for LookUpPrefix
const Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("bA", &key_length, &fixed);
EXPECT_TRUE(entry == NULL);
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
}
}
}
// Table class automatically enables case-sensitive mode when the given roman
// table has any input rule which contains one or more upper case characters.
// e.g. "V" -> "5" or "YT" -> "You there"
// This feature was implemented as b/2910223 as per following request.
// http://www.google.com/support/forum/p/ime/thread?tid=4ea9aed4ac8a2ba6&hl=ja
//
// The following test checks if a case-sensitive and a case-inensitive roman
// table enables and disables this "case-sensitive mode", respectively.
TEST_F(TableTest, AutomaticCaseSensitiveDetection) {
static const char kCaseInsensitiveRomanTable[] = {
"m\tmozc\n" // m -> mozc
"n\tnamazu\n" // n -> namazu
};
static const char kCaseSensitiveRomanTable[] = {
"m\tmozc\n" // m -> mozc
"M\tMozc\n" // M -> Mozc
};
commands::Request request;
{
Table table;
SetCustomRomanTable(kCaseSensitiveRomanTable);
config::Config config;
config::ConfigHandler::GetConfig(&config);
EXPECT_FALSE(table.case_sensitive())
<< "case-sensitive mode should be desabled by default.";
// Load a custom config with case-sensitive custom roman table.
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config));
EXPECT_TRUE(table.case_sensitive())
<< "Case sensitive roman table should enable case-sensitive mode.";
// Explicitly disable case-sensitive mode.
table.set_case_sensitive(false);
ASSERT_FALSE(table.case_sensitive());
}
{
Table table;
// Load a custom config with case-insensitive custom roman table.
SetCustomRomanTable(kCaseInsensitiveRomanTable);
config::Config config;
config::ConfigHandler::GetConfig(&config);
ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config));
EXPECT_FALSE(table.case_sensitive())
<< "Case insensitive roman table should disable case-sensitive mode.";
// Explicitly enable case-sensitive mode.
table.set_case_sensitive(true);
ASSERT_TRUE(table.case_sensitive());
}
}
TEST_F(TableTest, MobileMode) {
mozc::commands::Request request;
request.set_zero_query_suggestion(true);
request.set_mixed_conversion(true);
request.set_combine_all_segments(true);
{
// To 12keys -> Hiragana mode
request.set_special_romanji_table(
mozc::commands::Request::TWELVE_KEYS_TO_HIRAGANA);
mozc::composer::Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
{
const mozc::composer::Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("2", &key_length, &fixed);
EXPECT_EQ("2", entry->input());
EXPECT_EQ("", entry->result());
// "か"
EXPECT_EQ("\xE3\x81\x8B", entry->pending());
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
}
{
const mozc::composer::Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
// "し*"
entry = table.LookUpPrefix("\xE3\x81\x97*", &key_length, &fixed);
// "し*"
EXPECT_EQ("\xE3\x81\x97*", entry->input());
EXPECT_EQ("", entry->result());
// "\x0F*\x0Eじ"
// 0F and 0E are shift in/out characters.
EXPECT_EQ("\x0F*\x0E\xE3\x81\x98", entry->pending());
EXPECT_EQ(4, key_length);
EXPECT_TRUE(fixed);
}
}
{
// To 12keys -> Halfwidth Ascii mode
request.set_special_romanji_table(
mozc::commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
const mozc::composer::Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("2", &key_length, &fixed);
EXPECT_EQ("a", entry->pending());
}
{
// To Godan -> Hiragana mode
request.set_special_romanji_table(
mozc::commands::Request::GODAN_TO_HIRAGANA);
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
{
const mozc::composer::Entry *entry = NULL;
size_t key_length = 0;
bool fixed = false;
// "しゃ*"
entry = table.LookUpPrefix("\xE3\x81\x97\xE3\x82\x83*", &key_length,
&fixed);
// "じゃ"
EXPECT_EQ("\xE3\x81\x98\xE3\x82\x83", entry->pending());
}
}
{
// To Flick -> Hiragana mode.
request.set_special_romanji_table(
mozc::commands::Request::FLICK_TO_HIRAGANA);
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
size_t key_length = 0;
bool fixed = false;
const mozc::composer::Entry *entry = table.LookUpPrefix("a", &key_length,
&fixed);
// "き"
EXPECT_EQ("\xE3\x81\x8D", entry->pending());
}
{
// To Notouch -> Hiragana mode.
request.set_special_romanji_table(
mozc::commands::Request::NOTOUCH_TO_HIRAGANA);
Table table;
table.InitializeWithRequestAndConfig(request, default_config_);
size_t key_length = 0;
bool fixed = false;
const mozc::composer::Entry *entry = table.LookUpPrefix("a", &key_length,
&fixed);
// "き"
EXPECT_EQ("\xE3\x81\x8D", entry->pending());
}
}
TEST_F(TableTest, OrderOfAddRule) {
// The order of AddRule should not be sensitive.
{
Table table;
table.AddRule("www", "w", "ww");
table.AddRule("ww", "[X]", "w");
table.AddRule("we", "[WE]", "");
EXPECT_TRUE(table.HasSubRules("ww"));
const Entry *entry;
entry = table.LookUp("ww");
EXPECT_TRUE(NULL != entry);
size_t key_length;
bool fixed;
entry = table.LookUpPrefix("ww", &key_length, &fixed);
EXPECT_TRUE(NULL != entry);
EXPECT_EQ(2, key_length);
EXPECT_FALSE(fixed);
}
{
Table table;
table.AddRule("ww", "[X]", "w");
table.AddRule("we", "[WE]", "");
table.AddRule("www", "w", "ww");
EXPECT_TRUE(table.HasSubRules("ww"));
const Entry *entry = NULL;
entry = table.LookUp("ww");
EXPECT_TRUE(NULL != entry);
size_t key_length = 0;
bool fixed = false;
entry = table.LookUpPrefix("ww", &key_length, &fixed);
EXPECT_TRUE(NULL != entry);
EXPECT_EQ(2, key_length);
EXPECT_FALSE(fixed);
}
}
TEST_F(TableTest, AddRuleWithAttributes) {
const string kInput = "1";
Table table;
table.AddRuleWithAttributes(kInput, "", "a", NEW_CHUNK);
EXPECT_TRUE(table.HasNewChunkEntry(kInput));
size_t key_length = 0;
bool fixed = false;
const Entry *entry = table.LookUpPrefix(kInput, &key_length, &fixed);
EXPECT_EQ(1, key_length);
EXPECT_TRUE(fixed);
ASSERT_TRUE(NULL != entry);
EXPECT_EQ(kInput, entry->input());
EXPECT_EQ("", entry->result());
EXPECT_EQ("a", entry->pending());
EXPECT_EQ(NEW_CHUNK, entry->attributes());
const string kInput2 = "22";
table.AddRuleWithAttributes(kInput2, "", "b", NEW_CHUNK | NO_TRANSLITERATION);
EXPECT_TRUE(table.HasNewChunkEntry(kInput2));
key_length = 0;
fixed = false;
entry = table.LookUpPrefix(kInput2, &key_length, &fixed);
EXPECT_EQ(2, key_length);
EXPECT_TRUE(fixed);
ASSERT_TRUE(NULL != entry);
EXPECT_EQ(kInput2, entry->input());
EXPECT_EQ("", entry->result());
EXPECT_EQ("b", entry->pending());
EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION), entry->attributes());
}
TEST_F(TableTest, LoadFromString) {
const string kRule =
"# This is a comment\n"
"\n" // Empty line to be ignored.
"a\t[A]\n" // 2 entry rule
"kk\t[X]\tk\n" // 3 entry rule
"ww\t[W]\tw\tNewChunk\n" // 3 entry rule + attribute rule
"xx\t[X]\tx\tNewChunk NoTransliteration\n" // multiple attribute rules
// all attributes
"yy\t[Y]\ty\tNewChunk NoTransliteration DirectInput EndChunk\n"
"#\t[#]\n"; // This line starts with '#' but should be a rule.
Table table;
table.LoadFromString(kRule);
const Entry *entry = NULL;
// Test for "a\t[A]\n" -- 2 entry rule
EXPECT_FALSE(table.HasNewChunkEntry("a"));
entry = table.LookUp("a");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[A]", entry->result());
EXPECT_EQ("", entry->pending());
// Test for "kk\t[X]\tk\n" -- 3 entry rule
EXPECT_FALSE(table.HasNewChunkEntry("kk"));
entry = table.LookUp("kk");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[X]", entry->result());
EXPECT_EQ("k", entry->pending());
// Test for "ww\t[W]\tw\tNewChunk\n" -- 3 entry rule + attribute rule
EXPECT_TRUE(table.HasNewChunkEntry("ww"));
entry = table.LookUp("ww");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[W]", entry->result());
EXPECT_EQ("w", entry->pending());
EXPECT_EQ(NEW_CHUNK, entry->attributes());
// Test for "xx\t[X]\tx\tNewChunk NoTransliteration\n" -- multiple
// attribute rules
EXPECT_TRUE(table.HasNewChunkEntry("xx"));
entry = table.LookUp("xx");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[X]", entry->result());
EXPECT_EQ("x", entry->pending());
EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION), entry->attributes());
// Test for "yy\t[Y]\ty\tNewChunk NoTransliteration DirectInput EndChunk\n"
// -- all attributes
EXPECT_TRUE(table.HasNewChunkEntry("yy"));
entry = table.LookUp("yy");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[Y]", entry->result());
EXPECT_EQ("y", entry->pending());
EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION | DIRECT_INPUT | END_CHUNK),
entry->attributes());
// Test for "#\t[#]\n" -- This line starts with '#' but should be a rule.
entry = table.LookUp("#");
ASSERT_TRUE(NULL != entry);
EXPECT_EQ("[#]", entry->result());
EXPECT_EQ("", entry->pending());
}
TEST_F(TableTest, SpecialKeys) {
{
Table table;
table.AddRule("x{#1}y", "X1Y", "");
table.AddRule("x{#2}y", "X2Y", "");
table.AddRule("x{{}", "X{", "");
table.AddRule("xy", "XY", "");
const Entry *entry = NULL;
entry = table.LookUp("x{#1}y");
EXPECT_TRUE(NULL == entry);
string key;
key = Table::ParseSpecialKey("x{#1}y");
entry = table.LookUp(key);
ASSERT_TRUE(NULL != entry);
EXPECT_EQ(key, entry->input());
EXPECT_EQ("X1Y", entry->result());
key = Table::ParseSpecialKey("x{#2}y");
entry = table.LookUp(key);
ASSERT_TRUE(NULL != entry);
EXPECT_EQ(key, entry->input());
EXPECT_EQ("X2Y", entry->result());
key = "x{";
entry = table.LookUp(key);
ASSERT_TRUE(NULL != entry);
EXPECT_EQ(key, entry->input());
EXPECT_EQ("X{", entry->result());
}
{
// "{{}" is replaced with "{".
// "{*}" is replaced with "\x0F*\x0E".
Table table;
EXPECT_EQ("\x0F" "\x0E", table.AddRule("{}", "", "")->input());
EXPECT_EQ("{", table.AddRule("{", "", "")->input());
EXPECT_EQ("}", table.AddRule("}", "", "")->input());
EXPECT_EQ("{", table.AddRule("{{}", "", "")->input());
EXPECT_EQ("{}", table.AddRule("{{}}", "", "")->input());
EXPECT_EQ("a{", table.AddRule("a{", "", "")->input());
EXPECT_EQ("{a", table.AddRule("{a", "", "")->input());
EXPECT_EQ("a{a", table.AddRule("a{a", "", "")->input());
EXPECT_EQ("a}", table.AddRule("a}", "", "")->input());
EXPECT_EQ("}a", table.AddRule("}a", "", "")->input());
EXPECT_EQ("a}a", table.AddRule("a}a", "", "")->input());
EXPECT_EQ("a" "\x0F" "b" "\x0E" "c",
table.AddRule("a{b}c", "", "")->input());
EXPECT_EQ("a" "\x0F" "b" "\x0E" "c" "\x0F" "d" "\x0E" "\x0F" "e" "\x0E",
table.AddRule("a{b}c{d}{e}", "", "")->input());
EXPECT_EQ("}-{", table.AddRule("}-{", "", "")->input());
EXPECT_EQ("a{bc", table.AddRule("a{bc", "", "")->input());
// This is not a fixed specification, but a current behavior.
EXPECT_EQ("\x0F" "{-" "\x0E" "}",
table.AddRule("{{-}}", "", "")->input());
}
}
TEST_F(TableTest, TableManager) {
TableManager table_manager;
set<const Table*> table_set;
static const commands::Request::SpecialRomanjiTable
special_romanji_table[] = {
commands::Request::DEFAULT_TABLE,
commands::Request::TWELVE_KEYS_TO_HIRAGANA,
commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII,
commands::Request::FLICK_TO_HIRAGANA,
commands::Request::FLICK_TO_HALFWIDTHASCII,
commands::Request::TOGGLE_FLICK_TO_HIRAGANA,
commands::Request::TOGGLE_FLICK_TO_HALFWIDTHASCII,
commands::Request::GODAN_TO_HIRAGANA,
commands::Request::QWERTY_MOBILE_TO_HIRAGANA,
commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII,
commands::Request::NOTOUCH_TO_HIRAGANA,
commands::Request::NOTOUCH_TO_HALFWIDTHASCII,
};
static const config::Config::PreeditMethod preedit_method[] = {
config::Config::ROMAN,
config::Config::KANA
};
static const config::Config::PunctuationMethod punctuation_method[] = {
config::Config::KUTEN_TOUTEN,
config::Config::COMMA_PERIOD,
config::Config::KUTEN_PERIOD,
config::Config::COMMA_TOUTEN
};
static const config::Config::SymbolMethod symbol_method[] = {
config::Config::CORNER_BRACKET_MIDDLE_DOT,
config::Config::SQUARE_BRACKET_SLASH,
config::Config::CORNER_BRACKET_SLASH,
config::Config::SQUARE_BRACKET_MIDDLE_DOT
};
for (int romanji = 0; romanji < arraysize(special_romanji_table); ++romanji) {
for (int preedit = 0; preedit < arraysize(preedit_method); ++preedit) {
for (int punctuation = 0; punctuation < arraysize(punctuation_method);
++punctuation) {
for (int symbol = 0; symbol < arraysize(symbol_method); ++symbol) {
commands::Request request;
request.set_special_romanji_table(special_romanji_table[romanji]);
config::Config config;
config.set_preedit_method(preedit_method[preedit]);
config.set_punctuation_method(punctuation_method[punctuation]);
config.set_symbol_method(symbol_method[symbol]);
const Table *table = table_manager.GetTable(request, config);
EXPECT_TRUE(table != NULL);
EXPECT_TRUE(table_manager.GetTable(request, config) == table);
EXPECT_TRUE(table_set.find(table) == table_set.end());
table_set.insert(table);
}
}
}
}
{
// b/6788850.
const string kRule =
"a\t[A]\n"; // 2 entry rule
commands::Request request;
request.set_special_romanji_table(Request::DEFAULT_TABLE);
config::Config config;
config.set_preedit_method(Config::ROMAN);
config.set_punctuation_method(Config::KUTEN_TOUTEN);
config.set_symbol_method(Config::CORNER_BRACKET_MIDDLE_DOT);
config.set_custom_roman_table(kRule);
const Table *table = table_manager.GetTable(request, config);
EXPECT_TRUE(table != NULL);
EXPECT_TRUE(table_manager.GetTable(request, config) == table);
EXPECT_TRUE(NULL != table->LookUp("a"));
EXPECT_TRUE(NULL == table->LookUp("kk"));
const string kRule2 =
"a\t[A]\n" // 2 entry rule
"kk\t[X]\tk\n"; // 3 entry rule
config.set_custom_roman_table(kRule2);
const Table *table2 = table_manager.GetTable(request, config);
EXPECT_TRUE(table2 != NULL);
EXPECT_TRUE(table_manager.GetTable(request, config) == table2);
EXPECT_TRUE(NULL != table2->LookUp("a"));
EXPECT_TRUE(NULL != table2->LookUp("kk"));
}
}
} // namespace composer
} // namespace mozc