// Copyright 2010-2020, 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/internal/composition.h"
#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include "composer/internal/char_chunk.h"
#include "composer/internal/composition_input.h"
#include "composer/internal/transliterators.h"
#include "composer/table.h"
#include "testing/base/public/gunit.h"
namespace mozc {
namespace composer {
namespace {
std::string GetString(const Composition& composition) {
std::string output;
composition.GetString(&output);
return output;
}
std::string GetRawString(const Composition& composition) {
std::string output;
composition.GetStringWithTransliterator(Transliterators::RAW_STRING, &output);
return output;
}
void SetInput(const std::string& raw, const std::string& conversion,
const bool is_new_input, CompositionInput* input) {
input->Clear();
input->set_raw(raw);
if (!conversion.empty()) {
input->set_conversion(conversion);
}
input->set_is_new_input(is_new_input);
}
size_t InsertCharacters(const std::string& input, size_t pos,
Composition* composition) {
for (size_t i = 0; i < input.size(); ++i) {
pos = composition->InsertAt(pos, input.substr(i, 1));
}
return pos;
}
} // namespace
class CompositionTest : public testing::Test {
protected:
void SetUp() override {
table_.reset(new Table);
composition_.reset(new Composition(table_.get()));
composition_->SetInputMode(Transliterators::CONVERSION_STRING);
}
std::unique_ptr<Table> table_;
std::unique_ptr<Composition> composition_;
};
static int InitComposition(Composition* comp) {
static const struct TestCharChunk {
const char* conversion;
const char* pending;
const char* raw;
} test_chunks[] = {
// "あ ky き った っty" (9 chars)
// a ky ki tta tty
{"あ", "", "a"}, {"", "ky", "ky"}, {"き", "", "ki"},
{"った", "", "tta"}, {"っ", "ty", "tty"},
};
static const int test_chunks_size = arraysize(test_chunks);
CharChunkList::iterator it;
comp->MaybeSplitChunkAt(0, &it);
for (int i = 0; i < test_chunks_size; ++i) {
const TestCharChunk& data = test_chunks[i];
CharChunk* chunk = *comp->InsertChunk(&it);
chunk->set_conversion(data.conversion);
chunk->set_pending(data.pending);
chunk->set_raw(data.raw);
}
return test_chunks_size;
}
static CharChunk* AppendChunk(const char* conversion, const char* pending,
const char* raw, Composition* comp) {
CharChunkList::iterator it;
comp->MaybeSplitChunkAt(comp->GetLength(), &it);
CharChunk* chunk = *comp->InsertChunk(&it);
chunk->set_conversion(conversion);
chunk->set_pending(pending);
chunk->set_raw(raw);
return chunk;
}
TEST_F(CompositionTest, GetChunkLength) {
static const struct TestCase {
const char* conversion;
const char* pending;
const char* raw;
const int expected_conv_length;
const int expected_raw_length;
} test_cases[] = {
{"あ", "", "a", 1, 1}, {"", "ky", "ky", 2, 2},
{"き", "", "ki", 1, 2}, {"った", "", "tta", 2, 3},
{"っ", "ty", "tty", 3, 3},
};
CharChunk* chunk = AppendChunk("", "", "", composition_.get());
for (int i = 0; i < arraysize(test_cases); ++i) {
const TestCase& test = test_cases[i];
chunk->set_conversion(test.conversion);
chunk->set_pending(test.pending);
chunk->set_raw(test.raw);
const int conv_length =
chunk->GetLength(Transliterators::CONVERSION_STRING);
EXPECT_EQ(test.expected_conv_length, conv_length);
const int raw_length = chunk->GetLength(Transliterators::RAW_STRING);
EXPECT_EQ(test.expected_raw_length, raw_length);
}
}
namespace {
bool TestGetChunkAt(Composition* comp,
Transliterators::Transliterator transliterator,
const size_t index, const size_t expected_index,
const size_t expected_inner_position) {
auto expected_it = comp->GetCharChunkList().begin();
for (int j = 0; j < expected_index; ++j, ++expected_it) {
}
size_t inner_position;
auto it = comp->GetChunkAt(index, transliterator, &inner_position);
if (it == comp->GetCharChunkList().end()) {
EXPECT_TRUE(expected_it == it);
EXPECT_EQ(expected_inner_position, inner_position);
} else {
std::string result, expected_result;
EXPECT_EQ((*expected_it)->conversion(), (*it)->conversion());
EXPECT_EQ((*expected_it)->pending(), (*it)->pending());
EXPECT_EQ((*expected_it)->raw(), (*it)->raw());
EXPECT_EQ(expected_inner_position, inner_position);
}
return true;
}
} // namespace
TEST_F(CompositionTest, GetChunkAt) {
InitComposition(composition_.get());
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 0, 0,
0);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 1, 0,
1);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 2, 1,
1);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 3, 1,
2);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 4, 2,
1);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 5, 3,
1);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 6, 3,
2);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 7, 4,
1);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 8, 4,
2);
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 9, 4,
3);
// end
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 10, 4,
3);
// end
TestGetChunkAt(composition_.get(), Transliterators::CONVERSION_STRING, 11, 4,
3);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 0, 0, 0);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 1, 0, 1);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 2, 1, 1);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 3, 1, 2);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 4, 2, 1);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 5, 2, 2);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 6, 3, 1);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 7, 3, 2);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 8, 3, 3);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 9, 4, 1);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 10, 4, 2);
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 11, 4, 3);
// end
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 12, 4, 3);
// end
TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 13, 4, 3);
}
TEST_F(CompositionTest, GetString) {
InitComposition(composition_.get());
std::string composition;
const size_t dummy_position = 0;
// Test RAW mode
composition_->SetDisplayMode(dummy_position, Transliterators::RAW_STRING);
composition_->GetString(&composition);
EXPECT_EQ("akykittatty", composition);
// Test CONVERSION mode
composition_->SetDisplayMode(dummy_position,
Transliterators::CONVERSION_STRING);
composition_->GetString(&composition);
EXPECT_EQ("あkyきったっty", composition);
}
TEST_F(CompositionTest, GetStringWithDisplayMode) {
AppendChunk("も", "", "mo", composition_.get());
AppendChunk("ず", "", "z", composition_.get());
AppendChunk("く", "", "c", composition_.get());
std::string composition;
composition_->GetStringWithTransliterator(Transliterators::CONVERSION_STRING,
&composition);
EXPECT_EQ("もずく", composition);
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&composition);
EXPECT_EQ("mozc", composition);
}
TEST_F(CompositionTest, SplitRawChunk) {
static const struct TestCase {
const char* conversion;
const char* pending;
const char* raw;
const int position;
const char* expected_left_conversion;
const char* expected_left_pending;
const char* expected_left_raw;
const char* expected_right_conversion;
const char* expected_right_pending;
const char* expected_right_raw;
} test_cases[] = {
{"あ", "", "a", 0, "", "", "", "あ", "", "a"},
{"", "ky", "ky", 1, "", "k", "k", "", "y", "y"},
{"き", "", "ki", 1, "k", "", "k", "i", "", "i"},
{"った", "", "tta", 1, "t", "", "t", "ta", "", "ta"},
{"った", "", "tta", 2, "tt", "", "tt", "a", "", "a"},
{"っ", "ty", "tty", 1, "っ", "", "t", "", "ty", "ty"},
{"っ", "ty", "tty", 2, "っ", "t", "tt", "", "y", "y"},
{"っ", "ty", "tty", 3, "", "", "", "っ", "ty", "tty"},
};
for (int i = 0; i < arraysize(test_cases); ++i) {
const TestCase& test = test_cases[i];
CharChunk right_orig_chunk(Transliterators::CONVERSION_STRING, NULL);
right_orig_chunk.set_conversion(test.conversion);
right_orig_chunk.set_pending(test.pending);
right_orig_chunk.set_raw(test.raw);
CharChunk* left_new_chunk_ptr = NULL;
right_orig_chunk.SplitChunk(Transliterators::RAW_STRING, test.position,
&left_new_chunk_ptr);
std::unique_ptr<CharChunk> left_new_chunk(left_new_chunk_ptr);
if (left_new_chunk.get() != NULL) {
EXPECT_EQ(test.expected_left_conversion, left_new_chunk->conversion());
EXPECT_EQ(test.expected_left_pending, left_new_chunk->pending());
EXPECT_EQ(test.expected_left_raw, left_new_chunk->raw());
}
EXPECT_EQ(test.expected_right_conversion, right_orig_chunk.conversion());
EXPECT_EQ(test.expected_right_pending, right_orig_chunk.pending());
EXPECT_EQ(test.expected_right_raw, right_orig_chunk.raw());
}
}
TEST_F(CompositionTest, SplitConversionChunk) {
static const struct TestCase {
const char* conversion;
const char* pending;
const char* raw;
const int position;
const char* expected_left_conversion;
const char* expected_left_pending;
const char* expected_left_raw;
const char* expected_right_conversion;
const char* expected_right_pending;
const char* expected_right_raw;
} test_cases[] = {
{"あ", "", "a", 0, "", "", "", "あ", "", "a"},
{"", "ky", "ky", 1, "", "k", "k", "", "y", "y"},
{"きょ", "", "kyo", 1, "き", "", "き", "ょ", "", "ょ"},
{"っ", "t", "tt", 1, "っ", "", "t", "", "t", "t"},
{"った", "", "tta", 1, "っ", "", "っ", "た", "", "た"},
{"っ", "ty", "tty", 1, "っ", "", "t", "", "ty", "ty"},
{"っ", "ty", "tty", 2, "っ", "t", "tt", "", "y", "y"},
{"っ", "ty", "tty", 3, "", "", "", "っ", "ty", "tty"},
};
for (int i = 0; i < arraysize(test_cases); ++i) {
const TestCase& test = test_cases[i];
CharChunk right_orig_chunk(Transliterators::CONVERSION_STRING, NULL);
right_orig_chunk.set_conversion(test.conversion);
right_orig_chunk.set_pending(test.pending);
right_orig_chunk.set_raw(test.raw);
CharChunk* left_new_chunk_ptr = NULL;
right_orig_chunk.SplitChunk(Transliterators::CONVERSION_STRING,
test.position, &left_new_chunk_ptr);
std::unique_ptr<CharChunk> left_new_chunk(left_new_chunk_ptr);
if (left_new_chunk.get() != NULL) {
EXPECT_EQ(test.expected_left_conversion, left_new_chunk->conversion());
EXPECT_EQ(test.expected_left_pending, left_new_chunk->pending());
EXPECT_EQ(test.expected_left_raw, left_new_chunk->raw());
}
EXPECT_EQ(test.expected_right_conversion, right_orig_chunk.conversion());
EXPECT_EQ(test.expected_right_pending, right_orig_chunk.pending());
EXPECT_EQ(test.expected_right_raw, right_orig_chunk.raw());
}
}
TEST_F(CompositionTest, GetLength) {
table_->AddRule("a", "A", "");
table_->AddRule("ka", "K", "");
EXPECT_EQ(0, composition_->GetLength());
InsertCharacters("aka", 0, composition_.get());
EXPECT_EQ(2, composition_->GetLength());
}
TEST_F(CompositionTest, MaybeSplitChunkAt) {
static const struct TestCase {
const int position;
const int expected_raw_chunks_size;
const int expected_conv_chunks_size;
} test_cases[] = {
// "あ ky き った っty" (9 chars)
// a ky ki tta tty (11 chars)
{0, 5, 5}, {1, 5, 5}, {2, 6, 6}, {3, 5, 5}, {4, 6, 5},
{5, 5, 6}, {6, 6, 5}, {7, 6, 6}, {8, 5, 6}, {9, 6, 5},
{10, 6, 5}, {11, 5, 5}, {12, 5, 5},
};
const size_t dummy_position = 0;
for (int i = 0; i < arraysize(test_cases); ++i) {
const TestCase& test = test_cases[i];
{ // Test RAW mode
Composition raw_comp(table_.get());
InitComposition(&raw_comp);
raw_comp.SetDisplayMode(dummy_position, Transliterators::RAW_STRING);
CharChunkList::iterator raw_it;
raw_comp.MaybeSplitChunkAt(test.position, &raw_it);
const size_t raw_chunks_size = raw_comp.GetCharChunkList().size();
EXPECT_EQ(test.expected_raw_chunks_size, raw_chunks_size);
}
{ // Test CONVERSION mode
Composition conv_comp(table_.get());
InitComposition(&conv_comp);
conv_comp.SetDisplayMode(dummy_position,
Transliterators::CONVERSION_STRING);
CharChunkList::iterator conv_it;
conv_comp.MaybeSplitChunkAt(test.position, &conv_it);
const size_t conv_chunks_size = conv_comp.GetCharChunkList().size();
EXPECT_EQ(test.expected_conv_chunks_size, conv_chunks_size);
}
}
}
namespace {
std::string GetDeletedString(Transliterators::Transliterator t12r,
const int position) {
std::unique_ptr<Table> table(new Table);
std::unique_ptr<Composition> comp(new Composition(table.get()));
InitComposition(comp.get());
comp->SetDisplayMode(0, t12r);
comp->DeleteAt(position);
std::string composition;
comp->GetString(&composition);
comp.reset();
table.reset();
return composition;
}
} // namespace
TEST_F(CompositionTest, DeleteAt) {
// "あkyきったっty" is the original string
EXPECT_EQ("kyきったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 0));
EXPECT_EQ("あyきったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 1));
EXPECT_EQ("あkきったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 2));
EXPECT_EQ("あkyったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 3));
EXPECT_EQ("あkyきたっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 4));
EXPECT_EQ("あkyきっっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 5));
EXPECT_EQ("あkyきったty",
GetDeletedString(Transliterators::CONVERSION_STRING, 6));
EXPECT_EQ("あkyきったっy",
GetDeletedString(Transliterators::CONVERSION_STRING, 7));
EXPECT_EQ("あkyきったっt",
GetDeletedString(Transliterators::CONVERSION_STRING, 8));
// end
EXPECT_EQ("あkyきったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, 9));
EXPECT_EQ("あkyきったっty",
GetDeletedString(Transliterators::CONVERSION_STRING, -1));
// "akykittatty" is the original string
EXPECT_EQ("kykittatty", GetDeletedString(Transliterators::RAW_STRING, 0));
EXPECT_EQ("aykittatty", GetDeletedString(Transliterators::RAW_STRING, 1));
EXPECT_EQ("akkittatty", GetDeletedString(Transliterators::RAW_STRING, 2));
EXPECT_EQ("akyittatty", GetDeletedString(Transliterators::RAW_STRING, 3));
EXPECT_EQ("akykttatty", GetDeletedString(Transliterators::RAW_STRING, 4));
EXPECT_EQ("akykitatty", GetDeletedString(Transliterators::RAW_STRING, 5));
EXPECT_EQ("akykitatty", GetDeletedString(Transliterators::RAW_STRING, 6));
EXPECT_EQ("akykitttty", GetDeletedString(Transliterators::RAW_STRING, 7));
EXPECT_EQ("akykittaty", GetDeletedString(Transliterators::RAW_STRING, 8));
EXPECT_EQ("akykittaty", GetDeletedString(Transliterators::RAW_STRING, 9));
EXPECT_EQ("akykittatt", GetDeletedString(Transliterators::RAW_STRING, 10));
// end
EXPECT_EQ("akykittatty", GetDeletedString(Transliterators::RAW_STRING, 11));
EXPECT_EQ("akykittatty", GetDeletedString(Transliterators::RAW_STRING, -1));
}
TEST_F(CompositionTest, DeleteAt_InvisibleCharacter) {
CharChunkList::iterator it;
CharChunk* chunk;
composition_->MaybeSplitChunkAt(0, &it);
chunk = *composition_->InsertChunk(&it);
chunk->set_raw("1");
chunk->set_pending(Table::ParseSpecialKey("{1}"));
chunk = *composition_->InsertChunk(&it);
chunk->set_raw("2");
chunk->set_pending(Table::ParseSpecialKey("{2}2"));
chunk = *composition_->InsertChunk(&it);
chunk->set_raw("3");
chunk->set_pending("3");
// Now the CharChunks in the comp are expected to be following;
// (raw, pending) = [ ("1", "{1}"), ("2", "{2}2"), ("3", "3") ]
// {} means invisible characters.
composition_->DeleteAt(0);
std::string composition;
composition_->GetString(&composition);
EXPECT_EQ("3", composition);
}
namespace {
void InitTable(Table* table) {
table->AddRule("i", "い", "");
table->AddRule("ki", "き", "");
table->AddRule("kyi", "きぃ", "");
table->AddRule("ti", "ち", "");
table->AddRule("tya", "ちゃ", "");
table->AddRule("tyi", "ちぃ", "");
table->AddRule("ya", "や", "");
table->AddRule("yy", "っ", "y");
}
std::string GetInsertedString(Transliterators::Transliterator t12r,
const size_t position, const std::string& input) {
std::unique_ptr<Table> table(new Table);
InitTable(table.get());
std::unique_ptr<Composition> comp(new Composition(table.get()));
InitComposition(comp.get());
comp->SetTable(table.get());
comp->SetDisplayMode(0, t12r);
comp->InsertAt(position, input);
std::string composition;
comp->GetString(&composition);
comp.reset();
table.reset();
return composition;
}
} // namespace
TEST_F(CompositionTest, InsertAt) {
// "あkyきったっty" is the original string
EXPECT_EQ("いあkyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 0, "i"));
EXPECT_EQ("あいkyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 1, "i"));
EXPECT_EQ("あきyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 2, "i"));
EXPECT_EQ("あきぃきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 3, "i"));
EXPECT_EQ("あkyきいったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 4, "i"));
EXPECT_EQ("あkyきっいたっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 5, "i"));
EXPECT_EQ("あkyきったっちぃ",
GetInsertedString(Transliterators::CONVERSION_STRING, 9, "i"));
EXPECT_EQ("yあkyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 0, "y"));
EXPECT_EQ("あykyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 1, "y"));
EXPECT_EQ("あkyyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 2, "y"));
EXPECT_EQ("あkyyきったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 3, "y"));
EXPECT_EQ("あkyきyったっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 4, "y"));
EXPECT_EQ("あkyきっyたっty",
GetInsertedString(Transliterators::CONVERSION_STRING, 5, "y"));
// end
EXPECT_EQ("あkyきったっちぃ",
GetInsertedString(Transliterators::CONVERSION_STRING, 9, "i"));
// end
EXPECT_EQ("あkyきったっtyy",
GetInsertedString(Transliterators::CONVERSION_STRING, 9, "y"));
// "akykittatty" is the original string
EXPECT_EQ("iakykittatty",
GetInsertedString(Transliterators::RAW_STRING, 0, "i"));
EXPECT_EQ("aikykittatty",
GetInsertedString(Transliterators::RAW_STRING, 1, "i"));
EXPECT_EQ("akiykittatty",
GetInsertedString(Transliterators::RAW_STRING, 2, "i"));
EXPECT_EQ("akyikittatty",
GetInsertedString(Transliterators::RAW_STRING, 3, "i"));
EXPECT_EQ("akykiittatty",
GetInsertedString(Transliterators::RAW_STRING, 4, "i"));
EXPECT_EQ("akykiittatty",
GetInsertedString(Transliterators::RAW_STRING, 5, "i"));
EXPECT_EQ("akykittattyi",
GetInsertedString(Transliterators::RAW_STRING, 11, "i")); // end
}
TEST_F(CompositionTest, GetExpandedStrings) {
Table table;
InitTable(&table);
composition_->SetTable(&table);
InitComposition(composition_.get());
// a ky ki tta tty
std::string base;
std::set<std::string> expanded;
composition_->GetExpandedStrings(&base, &expanded);
EXPECT_EQ("あkyきったっ", base);
EXPECT_EQ(2, expanded.size());
// You cannot use EXPECT_NE here because it causes compile error in gtest
// when the compiler is Visual C++. b/5655673
EXPECT_TRUE(expanded.find("ちぃ") != expanded.end());
// You cannot use EXPECT_NE here because it causes compile error in gtest
// when the compiler is Visual C++. b/5655673
EXPECT_TRUE(expanded.find("ちゃ") != expanded.end());
}
TEST_F(CompositionTest, ConvertPosition) {
// Test against http://b/1550597
// Invalid positions.
EXPECT_EQ(0, composition_->ConvertPosition(static_cast<size_t>(-1),
Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
EXPECT_EQ(0,
composition_->ConvertPosition(0, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
EXPECT_EQ(0,
composition_->ConvertPosition(1, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
EXPECT_EQ(0,
composition_->ConvertPosition(0, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
EXPECT_EQ(0, composition_->ConvertPosition(
static_cast<size_t>(-1), Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
EXPECT_EQ(0,
composition_->ConvertPosition(1, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
AppendChunk("ね", "", "ne", composition_.get());
AppendChunk("っと", "", "tto", composition_.get());
// "|ねっと" -> "|netto"
EXPECT_EQ(0,
composition_->ConvertPosition(0, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
// "ね|っと" -> "ne|tto"
EXPECT_EQ(2,
composition_->ConvertPosition(1, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
// "ねっ|と" -> "net|to"
EXPECT_EQ(3,
composition_->ConvertPosition(2, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
// "ねっと|" -> "netto|"
EXPECT_EQ(5,
composition_->ConvertPosition(3, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
// Invalid positions.
EXPECT_EQ(5, composition_->ConvertPosition(static_cast<size_t>(-1),
Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
EXPECT_EQ(5,
composition_->ConvertPosition(4, Transliterators::CONVERSION_STRING,
Transliterators::RAW_STRING));
// "|netto" -> "|ねっと"
EXPECT_EQ(0,
composition_->ConvertPosition(0, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// "n|etto" -> "ね|っと"
EXPECT_EQ(1,
composition_->ConvertPosition(1, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// "ne|tto" -> "ね|っと"
EXPECT_EQ(1,
composition_->ConvertPosition(2, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// "net|to" -> "ねっ|と"
EXPECT_EQ(2,
composition_->ConvertPosition(3, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// "nett|o" -> "ねっと|"
EXPECT_EQ(3,
composition_->ConvertPosition(4, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// "netto|" -> "ねっと|"
EXPECT_EQ(3,
composition_->ConvertPosition(5, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
// Invalid positions.
EXPECT_EQ(3, composition_->ConvertPosition(
static_cast<size_t>(-1), Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
EXPECT_EQ(3,
composition_->ConvertPosition(6, Transliterators::RAW_STRING,
Transliterators::CONVERSION_STRING));
size_t inner_position;
auto chunk_it =
composition_->GetChunkAt(5, Transliterators::RAW_STRING, &inner_position);
EXPECT_EQ("tto", (*chunk_it)->raw());
EXPECT_EQ(3, inner_position);
}
TEST_F(CompositionTest, SetDisplayMode) {
AppendChunk("も", "", "mo", composition_.get());
AppendChunk("ず", "", "zu", composition_.get());
AppendChunk("く", "", "ku", composition_.get());
size_t inner_position;
auto chunk_it = composition_->GetChunkAt(
0, Transliterators::CONVERSION_STRING, &inner_position);
EXPECT_EQ("mo", (*chunk_it)->raw());
EXPECT_EQ(0, inner_position);
chunk_it = composition_->GetChunkAt(1, Transliterators::CONVERSION_STRING,
&inner_position);
EXPECT_EQ("mo", (*chunk_it)->raw());
EXPECT_EQ(1, inner_position);
chunk_it = composition_->GetChunkAt(2, Transliterators::CONVERSION_STRING,
&inner_position);
EXPECT_EQ("zu", (*chunk_it)->raw());
EXPECT_EQ(1, inner_position);
chunk_it = composition_->GetChunkAt(3, Transliterators::CONVERSION_STRING,
&inner_position);
EXPECT_EQ("ku", (*chunk_it)->raw());
EXPECT_EQ(1, inner_position);
EXPECT_EQ(6, composition_->SetDisplayMode(1, Transliterators::RAW_STRING));
EXPECT_EQ(
3, composition_->SetDisplayMode(2, Transliterators::CONVERSION_STRING));
EXPECT_EQ(6, composition_->SetDisplayMode(2, Transliterators::RAW_STRING));
}
TEST_F(CompositionTest, GetStringWithTrimMode) {
Table table;
table.AddRule("ka", "か", "");
table.AddRule("n", "ん", "");
// This makes the above rule ambiguous.
table.AddRule("na", "な", "");
composition_->SetTable(&table);
std::string output_empty;
composition_->GetStringWithTrimMode(TRIM, &output_empty);
EXPECT_TRUE(output_empty.empty());
size_t pos = 0;
pos = composition_->InsertAt(pos, "k");
pos = composition_->InsertAt(pos, "a");
pos = composition_->InsertAt(pos, "n");
std::string output_trim;
composition_->GetStringWithTrimMode(TRIM, &output_trim);
EXPECT_EQ("か", output_trim);
std::string output_asis;
composition_->GetStringWithTrimMode(ASIS, &output_asis);
EXPECT_EQ("かn", output_asis);
std::string output_fix;
composition_->GetStringWithTrimMode(FIX, &output_asis);
EXPECT_EQ("かん", output_asis);
}
TEST_F(CompositionTest, InsertKeyAndPreeditAt) {
Table table;
table.AddRule("す゛", "ず", "");
table.AddRule("く゛", "ぐ", "");
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
pos = composition_->InsertKeyAndPreeditAt(pos, "r", "す");
pos = composition_->InsertKeyAndPreeditAt(pos, "@", "゛");
pos = composition_->InsertKeyAndPreeditAt(pos, "h", "く");
pos = composition_->InsertKeyAndPreeditAt(pos, "!", "!");
std::string comp_str;
composition_->GetString(&comp_str);
EXPECT_EQ("もずく!", comp_str);
std::string comp_ascii_str;
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&comp_ascii_str);
EXPECT_EQ("mr@h!", comp_ascii_str);
}
TEST_F(CompositionTest, InsertKey_ForN) {
Table table;
table.AddRule("a", "[A]", "");
table.AddRule("n", "[N]", "");
table.AddRule("nn", "[N]", "");
table.AddRule("na", "[NA]", "");
table.AddRule("nya", "[NYA]", "");
table.AddRule("ya", "[YA]", "");
table.AddRule("ka", "[KA]", "");
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertAt(pos, "n");
pos = composition_->InsertAt(pos, "y");
pos = composition_->InsertAt(pos, "n");
pos = composition_->InsertAt(pos, "y");
pos = composition_->InsertAt(pos, "a");
std::string comp_str;
composition_->GetString(&comp_str);
EXPECT_EQ("ny[NYA]", comp_str);
}
TEST_F(CompositionTest, GetStringWithDisplayMode_ForKana) {
Table table;
// Empty table is OK.
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
std::string comp_str;
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&comp_str);
EXPECT_EQ("m", comp_str);
}
TEST_F(CompositionTest, InputMode) {
Table table;
table.AddRule("a", "あ", "");
table.AddRule("ka", "か", "");
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertAt(pos, "k");
std::string result;
composition_->GetString(&result);
EXPECT_EQ("k", result);
composition_->SetInputMode(Transliterators::FULL_KATAKANA);
pos = composition_->InsertAt(pos, "a");
composition_->GetString(&result);
// If a vowel and a consonant were typed with different
// transliterators, these characters should not be combined.
EXPECT_EQ("kア", result);
composition_->SetInputMode(Transliterators::HALF_ASCII);
pos = composition_->InsertAt(pos, "k");
composition_->GetString(&result);
EXPECT_EQ("kアk", result);
composition_->SetInputMode(Transliterators::HIRAGANA);
pos = composition_->InsertAt(pos, "a");
composition_->GetString(&result);
EXPECT_EQ("kアkあ", result);
EXPECT_EQ(Transliterators::CONVERSION_STRING,
composition_->GetTransliterator(0));
EXPECT_EQ(Transliterators::CONVERSION_STRING,
composition_->GetTransliterator(1));
EXPECT_EQ(Transliterators::FULL_KATAKANA, composition_->GetTransliterator(2));
EXPECT_EQ(Transliterators::HALF_ASCII, composition_->GetTransliterator(3));
EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(4));
EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(5));
EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(10));
}
TEST_F(CompositionTest, SetTable) {
Table table;
table.AddRule("a", "あ", "");
table.AddRule("ka", "か", "");
Table table2;
table2.AddRule("a", "あ", "");
table2.AddRule("ka", "か", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "k");
std::string result;
composition_->GetString(&result);
EXPECT_EQ("k", result);
composition_->SetTable(&table2);
pos = composition_->InsertAt(pos, "a");
composition_->GetString(&result);
EXPECT_EQ("kあ", result);
}
TEST_F(CompositionTest, Transliterator) {
Table table;
table.AddRule("a", "あ", "");
composition_->SetTable(&table);
// Insert "a" which is converted to "あ".
size_t pos = 0;
pos = composition_->InsertAt(pos, "a");
EXPECT_EQ(1, pos);
std::string result;
composition_->GetString(&result);
EXPECT_EQ("あ", result);
// Set transliterator for Half Ascii.
composition_->SetTransliterator(0, pos, Transliterators::HALF_ASCII);
composition_->GetString(&result);
EXPECT_EQ("a", result);
// Insert "a" again.
pos = composition_->InsertAt(pos, "a");
EXPECT_EQ(2, pos);
result.clear();
composition_->GetString(&result);
EXPECT_EQ("aあ", result);
// Set transliterator for Full Katakana.
composition_->SetTransliterator(0, pos, Transliterators::FULL_KATAKANA);
composition_->GetString(&result);
EXPECT_EQ("アア", result);
}
TEST_F(CompositionTest, HalfAsciiTransliterator) {
Table table;
table.AddRule("-", "ー", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HALF_ASCII);
size_t pos = 0;
pos = composition_->InsertKeyAndPreeditAt(pos, "-", "-");
EXPECT_EQ(1, pos);
EXPECT_EQ("-", GetString(*composition_));
pos = composition_->InsertKeyAndPreeditAt(pos, "-", "-");
EXPECT_EQ(2, pos);
EXPECT_EQ("--", GetString(*composition_));
}
TEST_F(CompositionTest, ShouldCommit) {
table_->AddRuleWithAttributes("ka", "[KA]", "", DIRECT_INPUT);
table_->AddRuleWithAttributes("tt", "[X]", "t", DIRECT_INPUT);
table_->AddRuleWithAttributes("ta", "[TA]", "", NO_TABLE_ATTRIBUTE);
size_t pos = 0;
pos = composition_->InsertAt(pos, "k");
EXPECT_FALSE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "a");
EXPECT_TRUE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "t");
EXPECT_FALSE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "t");
EXPECT_FALSE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "a");
EXPECT_TRUE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "t");
EXPECT_FALSE(composition_->ShouldCommit());
pos = composition_->InsertAt(pos, "a");
EXPECT_FALSE(composition_->ShouldCommit());
EXPECT_EQ("[KA][X][TA][TA]", GetString(*composition_));
}
TEST_F(CompositionTest, Issue2190364) {
// This is a unittest against http://b/2190364
Table table;
composition_->SetTable(&table);
size_t pos = 0;
composition_->SetInputMode(Transliterators::FULL_ASCII);
pos = composition_->InsertKeyAndPreeditAt(pos, "a", "ち");
EXPECT_EQ("a", GetString(*composition_));
pos = composition_->InsertAt(pos, " ");
EXPECT_EQ("a ", GetString(*composition_));
}
TEST_F(CompositionTest, Issue1817410) {
// This is a unittest against http://b/2190364
Table table;
table.AddRule("ss", "っ", "s");
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertAt(pos, "s");
pos = composition_->InsertAt(pos, "s");
std::string preedit;
composition_->GetString(&preedit);
EXPECT_EQ("っs", preedit);
EXPECT_EQ(0, composition_->ConvertPosition(0, Transliterators::LOCAL,
Transliterators::HALF_ASCII));
EXPECT_EQ(1, composition_->ConvertPosition(1, Transliterators::LOCAL,
Transliterators::HALF_ASCII));
EXPECT_EQ(2, composition_->ConvertPosition(2, Transliterators::LOCAL,
Transliterators::HALF_ASCII));
{ // "s|s"
size_t inner_position;
auto chunk_it =
composition_->GetChunkAt(1, Transliterators::LOCAL, &inner_position);
EXPECT_EQ(1, inner_position);
EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::LOCAL));
EXPECT_EQ(0,
composition_->GetPosition(Transliterators::HALF_ASCII, chunk_it));
EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::HALF_ASCII));
}
{ // "ss|"
size_t inner_position;
auto chunk_it =
composition_->GetChunkAt(2, Transliterators::LOCAL, &inner_position);
EXPECT_EQ(2, inner_position);
EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::LOCAL));
EXPECT_EQ(0,
composition_->GetPosition(Transliterators::HALF_ASCII, chunk_it));
EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::HALF_ASCII));
}
}
TEST_F(CompositionTest, Issue2209634) {
// This is a unittest against http://b/2209634
// "q@" becomes "qた@".
Table table;
table.AddRule("q", "", "た");
table.AddRule("た@", "だ", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HALF_ASCII);
size_t pos = 0;
pos = composition_->InsertAt(pos, "q");
pos = composition_->InsertAt(pos, "@");
std::string preedit;
composition_->GetString(&preedit);
EXPECT_EQ("q@", preedit);
}
TEST_F(CompositionTest, Issue2330530) {
// This is a unittest against http://b/2330530
// "Win" + Numpad7 becomes "Win77" instead of "Win7".
Table table;
table.AddRule("wi", "うぃ", "");
table.AddRule("i", "い", "");
table.AddRule("n", "ん", "");
table.AddRule("na", "な", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HALF_ASCII);
size_t pos = 0;
pos = composition_->InsertAt(pos, "W");
pos = composition_->InsertAt(pos, "i");
pos = composition_->InsertAt(pos, "n");
std::string preedit;
composition_->GetString(&preedit);
EXPECT_EQ("Win", preedit);
pos = composition_->InsertKeyAndPreeditAt(pos, "7", "7");
composition_->GetString(&preedit);
EXPECT_EQ("Win7", preedit);
}
TEST_F(CompositionTest, Issue2819580) {
// This is a unittest against http://b/2819580.
// 'y' after 'n' disappears.
Table table;
table.AddRule("po", "ぽ", "");
table.AddRule("n", "ん", "");
table.AddRule("na", "な", "");
table.AddRule("ya", "や", "");
table.AddRule("nya", "にゃ", "");
table.AddRule("byo", "びょ", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "n");
pos = composition_->InsertAt(pos, "y");
{
std::string output;
composition_->GetStringWithTrimMode(FIX, &output);
EXPECT_EQ("んy", output);
composition_->GetStringWithTrimMode(ASIS, &output);
EXPECT_EQ("ny", output);
composition_->GetStringWithTrimMode(TRIM, &output);
EXPECT_EQ("", output);
}
}
TEST_F(CompositionTest, Issue2990253) {
// SplitChunk fails.
// Ambiguous text is left in rhs CharChunk invalidly.
Table table;
table.AddRule("po", "ぽ", "");
table.AddRule("n", "ん", "");
table.AddRule("na", "な", "");
table.AddRule("ya", "や", "");
table.AddRule("nya", "にゃ", "");
table.AddRule("byo", "びょ", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "n");
pos = composition_->InsertAt(pos, "y");
pos = 1;
pos = composition_->InsertAt(pos, "b");
{
std::string output;
composition_->GetStringWithTrimMode(FIX, &output);
EXPECT_EQ("んby", output);
composition_->GetStringWithTrimMode(ASIS, &output);
EXPECT_EQ("んby", output);
composition_->GetStringWithTrimMode(TRIM, &output);
// doubtful result. should be "ん"
// May relate to http://b/2990358
EXPECT_EQ("んb", output);
}
}
TEST_F(CompositionTest, InsertionIntoPreeditMakesInvalidText_1) {
// http://b/2990358
// Test for mainly Composition::InsertAt()
Table table;
table.AddRule("po", "ぽ", "");
table.AddRule("n", "ん", "");
table.AddRule("na", "な", "");
table.AddRule("ya", "や", "");
table.AddRule("nya", "にゃ", "");
table.AddRule("byo", "びょ", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "n");
pos = composition_->InsertAt(pos, "y");
pos = 1;
pos = composition_->InsertAt(pos, "b");
pos = 3;
pos = composition_->InsertAt(pos, "o");
{
std::string output;
composition_->GetStringWithTrimMode(FIX, &output);
EXPECT_EQ("んびょ", output);
composition_->GetStringWithTrimMode(ASIS, &output);
EXPECT_EQ("んびょ", output);
composition_->GetStringWithTrimMode(TRIM, &output);
EXPECT_EQ("んびょ", output);
}
}
TEST_F(CompositionTest, InsertionIntoPreeditMakesInvalidText_2) {
// http://b/2990358
// Test for mainly Composition::InsertKeyAndPreeditAt()
Table table;
table.AddRule("す゛", "ず", "");
table.AddRule("く゛", "ぐ", "");
composition_->SetTable(&table);
size_t pos = 0;
pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
pos = composition_->InsertKeyAndPreeditAt(pos, "r", "す");
pos = composition_->InsertKeyAndPreeditAt(pos, "h", "く");
pos = composition_->InsertKeyAndPreeditAt(2, "@", "゛");
pos = composition_->InsertKeyAndPreeditAt(5, "!", "!");
std::string comp_str;
composition_->GetString(&comp_str);
EXPECT_EQ("もずく!", comp_str);
std::string comp_ascii_str;
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&comp_ascii_str);
EXPECT_EQ("mr@h!", comp_ascii_str);
}
TEST_F(CompositionTest, CombinePendingChunks) {
Table table;
table.AddRule("po", "ぽ", "");
table.AddRule("n", "ん", "");
table.AddRule("na", "な", "");
table.AddRule("ya", "や", "");
table.AddRule("nya", "にゃ", "");
table.AddRule("byo", "びょ", "");
{
// empty chunks + "n" -> empty chunks + "n"
Composition comp(&table);
comp.SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
CharChunkList::iterator it;
comp.MaybeSplitChunkAt(pos, &it);
CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
CompositionInput input;
SetInput("n", "", false, &input);
comp.CombinePendingChunks(chunk_it, input);
EXPECT_EQ("", (*chunk_it)->pending());
EXPECT_EQ("", (*chunk_it)->conversion());
EXPECT_EQ("", (*chunk_it)->raw());
EXPECT_EQ("", (*chunk_it)->ambiguous());
}
{
// [x] + "n" -> [x] + "n"
// No combination performed.
Composition comp(&table);
comp.SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = comp.InsertAt(pos, "x");
CharChunkList::iterator it;
comp.MaybeSplitChunkAt(pos, &it);
CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
CompositionInput input;
SetInput("n", "", false, &input);
comp.CombinePendingChunks(chunk_it, input);
EXPECT_EQ("", (*chunk_it)->pending());
EXPECT_EQ("", (*chunk_it)->conversion());
EXPECT_EQ("", (*chunk_it)->raw());
EXPECT_EQ("", (*chunk_it)->ambiguous());
}
{
// Append "a" to [n][y] -> [ny] + "a"
// Combination performed.
Composition comp(&table);
comp.SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = comp.InsertAt(pos, "y");
pos = comp.InsertAt(0, "n");
CharChunkList::iterator it;
comp.MaybeSplitChunkAt(2, &it);
CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
CompositionInput input;
SetInput("a", "", false, &input);
comp.CombinePendingChunks(chunk_it, input);
EXPECT_EQ("ny", (*chunk_it)->pending());
EXPECT_EQ("", (*chunk_it)->conversion());
EXPECT_EQ("ny", (*chunk_it)->raw());
EXPECT_EQ("んy", (*chunk_it)->ambiguous());
}
{
// Append "a" to [x][n][y] -> [x][ny] + "a"
// Combination performed.
Composition comp(&table);
comp.SetTable(&table);
comp.SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = comp.InsertAt(pos, "x");
pos = comp.InsertAt(pos, "y");
pos = comp.InsertAt(1, "n");
CharChunkList::iterator it;
comp.MaybeSplitChunkAt(3, &it);
CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
CompositionInput input;
SetInput("a", "", false, &input);
comp.CombinePendingChunks(chunk_it, input);
EXPECT_EQ("ny", (*chunk_it)->pending());
EXPECT_EQ("", (*chunk_it)->conversion());
EXPECT_EQ("ny", (*chunk_it)->raw());
EXPECT_EQ("んy", (*chunk_it)->ambiguous());
}
{
// Append "a" of conversion value to [x][n][y] -> [x][ny] + "a"
// Combination performed. If composition input contains a
// conversion, the conversion is used rather than a raw value.
Composition comp(&table);
comp.SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = comp.InsertAt(pos, "x");
pos = comp.InsertAt(pos, "y");
pos = comp.InsertAt(1, "n");
CharChunkList::iterator it;
comp.MaybeSplitChunkAt(3, &it);
CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
CompositionInput input;
SetInput("x", "a", false, &input);
comp.CombinePendingChunks(chunk_it, input);
EXPECT_EQ("ny", (*chunk_it)->pending());
EXPECT_EQ("", (*chunk_it)->conversion());
EXPECT_EQ("ny", (*chunk_it)->raw());
}
}
TEST_F(CompositionTest, NewChunkBehaviors) {
Table table;
table.AddRule("n", "", "ん");
table.AddRule("na", "", "な");
table.AddRuleWithAttributes("a", "", "あ", NEW_CHUNK);
table.AddRule("ん*", "", "猫");
table.AddRule("*", "", "");
table.AddRule("ん#", "", "猫");
composition_->SetTable(&table);
CompositionInput input;
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("a", "", true, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("nあ", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("a", "", false, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("な", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("*", "", true, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("猫", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("*", "", false, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("猫", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("#", "", true, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("n#", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("#", "", false, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("猫", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("1", "", true, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("n1", GetString(*composition_));
}
{
size_t pos = 0;
composition_->Erase();
SetInput("n", "", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("1", "", false, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ(
"ん"
"1",
GetString(*composition_));
}
}
TEST_F(CompositionTest, TwelveKeysInput) {
// Simulates flick + toggle input mode.
Table table;
table.AddRule("n", "", "ん");
table.AddRule("na", "", "な");
table.AddRule("a", "", "あ");
table.AddRule("*", "", "");
table.AddRule("ほ*", "", "ぼ");
table.AddRuleWithAttributes("7", "", "は", NEW_CHUNK);
table.AddRule(
"は"
"7",
"", "ひ");
table.AddRule("ひ*", "", "び");
composition_->SetTable(&table);
CompositionInput input;
size_t pos = 0;
SetInput("n", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("a", "", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "ほ", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("*", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "ひ", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "は", false, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "", true, &input);
pos = composition_->InsertInput(pos, input);
SetInput("7", "は", false, &input);
pos = composition_->InsertInput(pos, input);
EXPECT_EQ("なぼひはははは", GetString(*composition_));
}
TEST_F(CompositionTest, DifferentRulesForSamePendingWithSpecialKeys) {
table_->AddRule("4", "", "[ta]");
table_->AddRule("[to]4", "", "[x]{#1}");
table_->AddRule("[x]{#1}4", "", "[ta]");
table_->AddRule("*", "", "");
table_->AddRule("[tu]*", "", "[x]{#2}");
table_->AddRule("[x]{#2}*", "", "[tu]");
{
composition_->Erase();
size_t pos = 0;
pos = composition_->InsertAt(pos, "[to]4");
EXPECT_EQ(3, pos);
EXPECT_EQ("[x]", GetString(*composition_));
EXPECT_EQ("[to]4", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "4");
EXPECT_EQ(4, pos);
EXPECT_EQ("[ta]", GetString(*composition_));
EXPECT_EQ("[to]44", GetRawString(*composition_));
}
{
composition_->Erase();
size_t pos = 0;
pos = composition_->InsertAt(pos, "[to]4");
EXPECT_EQ(3, pos);
EXPECT_EQ("[x]", GetString(*composition_));
EXPECT_EQ("[to]4", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "*");
EXPECT_EQ(3, pos);
EXPECT_EQ("[x]", GetString(*composition_));
EXPECT_EQ("[to]4*", GetRawString(*composition_));
}
{
composition_->Erase();
size_t pos = 0;
pos = composition_->InsertAt(pos, "[tu]*");
EXPECT_EQ(3, pos);
EXPECT_EQ("[x]", GetString(*composition_));
EXPECT_EQ("[tu]*", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "*");
EXPECT_EQ(4, pos);
EXPECT_EQ("[tu]", GetString(*composition_));
EXPECT_EQ("[tu]**", GetRawString(*composition_));
}
{
composition_->Erase();
size_t pos = 0;
pos = composition_->InsertAt(pos, "[tu]*");
EXPECT_EQ(3, pos);
EXPECT_EQ("[x]", GetString(*composition_));
EXPECT_EQ("[tu]*", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "*");
EXPECT_EQ(4, pos);
EXPECT_EQ("[tu]", GetString(*composition_));
EXPECT_EQ("[tu]**", GetRawString(*composition_));
}
}
TEST_F(CompositionTest, LoopingRuleFor12KeysWithSpecialKeys) {
table_->AddRule("2", "", "a");
table_->AddRule("a2", "", "b");
table_->AddRule("b2", "", "c");
table_->AddRule("c2", "", "{2}2");
table_->AddRule("{2}22", "", "a");
size_t pos = 0;
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("a", GetString(*composition_));
EXPECT_EQ("2", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("b", GetString(*composition_));
EXPECT_EQ("22", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("c", GetString(*composition_));
EXPECT_EQ("222", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("2", GetString(*composition_));
EXPECT_EQ("2222", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("a", GetString(*composition_));
EXPECT_EQ("22222", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("b", GetString(*composition_));
EXPECT_EQ("222222", GetRawString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("c", GetString(*composition_));
EXPECT_EQ("2222222", GetRawString(*composition_));
}
TEST_F(CompositionTest, AlphanumericOfSSH) {
// This is a unittest against http://b/3199626
// 'ssh' (っsh) + F10 should be 'ssh'.
Table table;
table.AddRule("ss", "っ", "s");
table.AddRule("shi", "し", "");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "s");
pos = composition_->InsertAt(pos, "s");
pos = composition_->InsertAt(pos, "h");
EXPECT_EQ(3, pos);
std::string output;
composition_->GetStringWithTrimMode(FIX, &output);
EXPECT_EQ("っsh", output);
}
TEST_F(CompositionTest, GrassHack) {
Table table;
table.AddRule("ww", "っ", "w");
table.AddRule("we", "うぇ", "");
table.AddRule("www", "w", "ww");
composition_->SetTable(&table);
composition_->SetInputMode(Transliterators::HIRAGANA);
size_t pos = 0;
pos = composition_->InsertAt(pos, "w");
pos = composition_->InsertAt(pos, "w");
pos = composition_->InsertAt(pos, "w");
EXPECT_EQ("www", GetString(*composition_));
pos = composition_->InsertAt(pos, "e");
EXPECT_EQ("wっうぇ", GetString(*composition_));
}
TEST_F(CompositionTest, RulesForFirstKeyEvents) {
table_->AddRuleWithAttributes("a", "[A]", "", NEW_CHUNK);
table_->AddRule("n", "[N]", "");
table_->AddRule("nn", "[N]", "");
table_->AddRule("na", "[NA]", "");
table_->AddRuleWithAttributes("ni", "[NI]", "", NEW_CHUNK);
CompositionInput input;
{
SetInput("a", "", true, &input);
composition_->InsertInput(0, input);
EXPECT_EQ("[A]", GetString(*composition_));
}
{
composition_->Erase();
CompositionInput input;
SetInput("anaa", "", true, &input);
composition_->InsertInput(0, input);
EXPECT_EQ("[A][NA][A]", GetString(*composition_));
}
{
composition_->Erase();
CompositionInput input;
SetInput("an", "", true, &input);
const size_t position_an = composition_->InsertInput(0, input);
SetInput("a", "", true, &input);
composition_->InsertInput(position_an, input);
EXPECT_EQ("[A]n[A]", GetString(*composition_));
// This input should be treated as a part of "NA".
SetInput("a", "", false, &input);
composition_->InsertInput(position_an, input);
EXPECT_EQ("[A][NA][A]", GetString(*composition_));
std::string raw_t13n;
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&raw_t13n);
EXPECT_EQ("anaa", raw_t13n);
}
{
composition_->Erase();
CompositionInput input;
SetInput("an", "", true, &input);
const size_t position_an = composition_->InsertInput(0, input);
SetInput("ni", "", true, &input);
composition_->InsertInput(position_an, input);
EXPECT_EQ("[A]n[NI]", GetString(*composition_));
std::string raw_t13n;
composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
&raw_t13n);
EXPECT_EQ("anni", raw_t13n);
}
}
TEST_F(CompositionTest, NoTransliteration) {
table_->AddRuleWithAttributes("0", "0", "", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("1", "1", "", NO_TRANSLITERATION);
table_->AddRuleWithAttributes("kk", "っ", "k", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("ka", "か", "", NO_TRANSLITERATION);
table_->AddRuleWithAttributes("ss", "っ", "s", NO_TRANSLITERATION);
table_->AddRuleWithAttributes("sa", "さ", "", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("tt", "っ", "t", NO_TRANSLITERATION);
table_->AddRuleWithAttributes("ta", "た", "", NO_TRANSLITERATION);
composition_->SetInputMode(Transliterators::FULL_KATAKANA);
InsertCharacters("01kkassatta", 0, composition_.get());
EXPECT_EQ("01ッカっさった", GetString(*composition_));
}
TEST_F(CompositionTest, NoTransliteration_Issue3497962) {
table_->AddRuleWithAttributes("2", "", "a", NEW_CHUNK | NO_TRANSLITERATION);
table_->AddRuleWithAttributes("a2", "", "b", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("b2", "", "c", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("c2", "", "{2}2", NO_TABLE_ATTRIBUTE);
table_->AddRuleWithAttributes("{2}22", "", "a", NO_TABLE_ATTRIBUTE);
composition_->SetInputMode(Transliterators::HIRAGANA);
int pos = 0;
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("a", GetString(*composition_));
pos = composition_->InsertAt(pos, "2");
EXPECT_EQ("b", GetString(*composition_));
}
TEST_F(CompositionTest, SetTransliteratorOnEmpty) {
composition_->SetTransliterator(0, 0, Transliterators::HIRAGANA);
CompositionInput input;
SetInput("a", "", true, &input);
composition_->InsertInput(0, input);
EXPECT_EQ(1, composition_->GetLength());
}
TEST_F(CompositionTest, Clone) {
Composition src(table_.get());
src.SetInputMode(Transliterators::FULL_KATAKANA);
AppendChunk("も", "", "mo", &src);
AppendChunk("ず", "", "z", &src);
AppendChunk("く", "", "c", &src);
EXPECT_EQ(table_.get(), src.table());
EXPECT_EQ(Transliterators::FULL_KATAKANA, src.input_t12r());
EXPECT_EQ(3, src.chunks().size());
std::unique_ptr<Composition> dest(src.Clone());
ASSERT_NE(nullptr, dest.get());
EXPECT_EQ(src.table(), dest->table());
EXPECT_EQ(src.input_t12r(), dest->input_t12r());
ASSERT_EQ(src.chunks().size(), dest->chunks().size());
CharChunkList::const_iterator src_it = src.chunks().begin();
CharChunkList::const_iterator dest_it = dest->chunks().begin();
for (; src_it != src.chunks().end(); ++src_it, ++dest_it) {
EXPECT_EQ((*src_it)->raw(), (*dest_it)->raw());
}
}
TEST_F(CompositionTest, IsToggleable) {
const int kAttrs =
TableAttribute::NEW_CHUNK | TableAttribute::NO_TRANSLITERATION;
table_->AddRuleWithAttributes("1", "", "{?}あ", kAttrs);
table_->AddRule("{?}あ1", "", "{*}あ");
size_t pos = 0;
pos = composition_->InsertAt(pos, "1");
EXPECT_TRUE(composition_->IsToggleable(0));
pos = composition_->InsertAt(pos, "1");
EXPECT_FALSE(composition_->IsToggleable(0));
}
} // namespace composer
} // namespace mozc