| // 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. |
| |
| // Tests for session converter. |
| // |
| // Note that we have a lot of tests which assume that the converter fills |
| // T13Ns. If you want to add test case related to T13Ns, please make sure |
| // you set T13Ns to the result for a mock converter. |
| |
| #include "session/session_converter.h" |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/number_util.h" |
| #include "base/system_util.h" |
| #include "base/util.h" |
| #include "composer/composer.h" |
| #include "composer/table.h" |
| #include "config/config.pb.h" |
| #include "config/config_handler.h" |
| #include "converter/converter_mock.h" |
| #include "converter/segments.h" |
| #include "session/candidates.pb.h" |
| #include "session/commands.pb.h" |
| #include "session/internal/candidate_list.h" |
| #include "session/internal/keymap.h" |
| #include "session/request_test_util.h" |
| #include "testing/base/public/googletest.h" |
| #include "testing/base/public/gunit.h" |
| #include "testing/base/public/testing_util.h" |
| #include "transliteration/transliteration.h" |
| #include "usage_stats/usage_stats.h" |
| #include "usage_stats/usage_stats_testing_util.h" |
| |
| DECLARE_string(test_tmpdir); |
| |
| namespace mozc { |
| namespace session { |
| |
| using mozc::commands::Context; |
| using mozc::commands::Request; |
| using mozc::commands::RequestForUnitTest; |
| |
| // "あいうえお" |
| static const char kChars_Aiueo[] = |
| "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a"; |
| // "も" |
| static const char kChars_Mo[] = "\xE3\x82\x82"; |
| // "もずく" |
| static const char kChars_Mozuku[] = "\xE3\x82\x82\xE3\x81\x9A\xE3\x81\x8F"; |
| // "もずくす" |
| static const char kChars_Mozukusu[] = |
| "\xE3\x82\x82\xE3\x81\x9A\xE3\x81\x8F\xE3\x81\x99"; |
| // "ももんが" |
| static const char kChars_Momonga[] = |
| "\xE3\x82\x82\xE3\x82\x82\xE3\x82\x93\xE3\x81\x8C"; |
| |
| class SessionConverterTest : public ::testing::Test { |
| protected: |
| // Workaround for C2512 error (no default appropriate constructor) on MSVS. |
| SessionConverterTest() {} |
| virtual ~SessionConverterTest() {} |
| |
| virtual void SetUp() { |
| convertermock_.reset(new ConverterMock()); |
| SystemUtil::SetUserProfileDirectory(FLAGS_test_tmpdir); |
| config::Config config; |
| config::ConfigHandler::GetDefaultConfig(&config); |
| config::ConfigHandler::SetConfig(config); |
| |
| mozc::usage_stats::UsageStats::ClearAllStatsForTest(); |
| |
| table_.reset(new composer::Table); |
| table_->InitializeWithRequestAndConfig(default_request_, config); |
| composer_.reset(new composer::Composer(table_.get(), &default_request_)); |
| mobile_request_.reset(new Request); |
| RequestForUnitTest::FillMobileRequest(mobile_request_.get()); |
| } |
| |
| virtual void TearDown() { |
| table_.reset(); |
| composer_.reset(); |
| |
| mozc::usage_stats::UsageStats::ClearAllStatsForTest(); |
| |
| // just in case, reset the config in test_tmpdir |
| config::Config config; |
| config::ConfigHandler::GetDefaultConfig(&config); |
| config::ConfigHandler::SetConfig(config); |
| } |
| |
| static void GetSegments(const SessionConverter &converter, Segments *dest) { |
| CHECK(dest); |
| dest->CopyFrom(*converter.segments_.get()); |
| } |
| |
| static void SetSegments(const Segments &src, SessionConverter *converter) { |
| CHECK(converter); |
| converter->segments_->CopyFrom(src); |
| } |
| |
| static const commands::Result &GetResult(const SessionConverter &converter) { |
| return *converter.result_; |
| } |
| |
| static const CandidateList &GetCandidateList( |
| const SessionConverter &converter) { |
| return *converter.candidate_list_; |
| } |
| |
| static const OperationPreferences &GetOperationPreferences( |
| const SessionConverter &converter) { |
| return converter.operation_preferences_; |
| } |
| |
| static SessionConverterInterface::State GetState( |
| const SessionConverter &converter) { |
| return converter.state_; |
| } |
| |
| static void SetState(SessionConverterInterface::State state, |
| SessionConverter *converter) { |
| converter->state_ = state; |
| } |
| |
| static size_t GetSegmentIndex(const SessionConverter &converter) { |
| return converter.segment_index_; |
| } |
| |
| static bool IsCandidateListVisible(const SessionConverter &converter) { |
| return converter.candidate_list_visible_; |
| } |
| |
| static const commands::Request &GetRequest( |
| const SessionConverter &converter) { |
| return *converter.request_; |
| } |
| |
| static void GetPreedit(const SessionConverter &converter, size_t index, |
| size_t size, string *conversion) { |
| converter.GetPreedit(index, size, conversion); |
| } |
| |
| static void GetConversion(const SessionConverter &converter, size_t index, |
| size_t size, string *conversion) { |
| converter.GetConversion(index, size, conversion); |
| } |
| |
| static void AppendCandidateList(SessionConverter *converter) { |
| converter->AppendCandidateList(); |
| } |
| |
| // set result for "あいうえお" |
| static void SetAiueo(Segments *segments) { |
| segments->Clear(); |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segment = segments->add_segment(); |
| // "あいうえお" |
| segment->set_key( |
| "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a"); |
| candidate = segment->add_candidate(); |
| // "あいうえお" |
| candidate->key = |
| "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a"; |
| candidate->value = candidate->key; |
| candidate = segment->add_candidate(); |
| // "あいうえお" |
| candidate->key = |
| "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a"; |
| // "アイウエオ" |
| candidate->value = |
| "\xe3\x82\xa2\xe3\x82\xa4\xe3\x82\xa6\xe3\x82\xa8\xe3\x82\xaa"; |
| } |
| |
| // set result for "かまぼこのいんぼう" |
| static void SetKamaboko(Segments *segments) { |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segments->Clear(); |
| segment = segments->add_segment(); |
| |
| // "かまぼこの" |
| segment->set_key( |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"); |
| candidate = segment->add_candidate(); |
| // "かまぼこの" |
| candidate->value = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| candidate = segment->add_candidate(); |
| // "カマボコの" |
| candidate->value = |
| "\xe3\x82\xab\xe3\x83\x9e\xe3\x83\x9c\xe3\x82\xb3\xe3\x81\xae"; |
| segment = segments->add_segment(); |
| // "いんぼう" |
| segment->set_key("\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"); |
| candidate = segment->add_candidate(); |
| // "陰謀" |
| candidate->value = "\xe9\x99\xb0\xe8\xac\x80"; |
| candidate = segment->add_candidate(); |
| // "印房" |
| candidate->value = "\xe5\x8d\xb0\xe6\x88\xbf"; |
| |
| // Set dummy T13Ns |
| vector<Segment::Candidate> *meta_candidates = |
| segment->mutable_meta_candidates(); |
| meta_candidates->resize(transliteration::NUM_T13N_TYPES); |
| for (size_t i = 0; i < transliteration::NUM_T13N_TYPES; ++i) { |
| meta_candidates->at(i).Init(); |
| meta_candidates->at(i).value = segment->key(); |
| meta_candidates->at(i).content_value = segment->key(); |
| meta_candidates->at(i).content_key = segment->key(); |
| } |
| } |
| |
| // set T13N candidates to segments using composer |
| static void FillT13Ns(Segments *segments, |
| const composer::Composer *composer) { |
| size_t composition_pos = 0; |
| for (size_t i = 0; i < segments->conversion_segments_size(); ++i) { |
| Segment *segment = segments->mutable_conversion_segment(i); |
| CHECK(segment); |
| const size_t composition_len = Util::CharsLen(segment->key()); |
| vector<string> t13ns; |
| composer->GetSubTransliterations( |
| composition_pos, composition_len, &t13ns); |
| vector<Segment::Candidate> *meta_candidates = |
| segment->mutable_meta_candidates(); |
| meta_candidates->resize(transliteration::NUM_T13N_TYPES); |
| for (size_t j = 0; j < transliteration::NUM_T13N_TYPES; ++j) { |
| meta_candidates->at(j).Init(); |
| meta_candidates->at(j).value = t13ns[j]; |
| meta_candidates->at(j).content_value = t13ns[j]; |
| meta_candidates->at(j).content_key = segment->key(); |
| } |
| composition_pos += composition_len; |
| } |
| } |
| |
| // set result for "like" |
| void InitConverterWithLike(Segments *segments) { |
| // "ぃ" |
| composer_->InsertCharacterKeyAndPreedit("li", "\xE3\x81\x83"); |
| // "け" |
| composer_->InsertCharacterKeyAndPreedit("ke", "\xE3\x81\x91"); |
| |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segments->Clear(); |
| segment = segments->add_segment(); |
| |
| // "ぃ" |
| segment->set_key("\xE3\x81\x83"); |
| candidate = segment->add_candidate(); |
| // "ぃ" |
| candidate->value = "\xE3\x81\x83"; |
| |
| candidate = segment->add_candidate(); |
| // "ィ" |
| candidate->value = "\xE3\x82\xA3"; |
| |
| segment = segments->add_segment(); |
| // "け" |
| segment->set_key("\xE3\x81\x91"); |
| candidate = segment->add_candidate(); |
| // "家" |
| candidate->value = "\xE5\xAE\xB6"; |
| candidate = segment->add_candidate(); |
| // "け" |
| candidate->value = "\xE3\x81\x91"; |
| |
| FillT13Ns(segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(segments, true); |
| } |
| |
| static void InsertASCIISequence(const string text, |
| composer::Composer *composer) { |
| for (size_t i = 0; i < text.size(); ++i) { |
| commands::KeyEvent key; |
| key.set_key_code(text[i]); |
| composer->InsertCharacterKeyEvent(key); |
| } |
| } |
| |
| static void ExpectSameSessionConverter(const SessionConverter &lhs, |
| const SessionConverter &rhs) { |
| EXPECT_EQ(lhs.IsActive(), rhs.IsActive()); |
| EXPECT_EQ(IsCandidateListVisible(lhs), IsCandidateListVisible(rhs)); |
| EXPECT_EQ(GetSegmentIndex(lhs), GetSegmentIndex(rhs)); |
| |
| EXPECT_EQ(GetOperationPreferences(lhs).use_cascading_window, |
| GetOperationPreferences(rhs).use_cascading_window); |
| EXPECT_EQ(GetOperationPreferences(lhs).candidate_shortcuts, |
| GetOperationPreferences(rhs).candidate_shortcuts); |
| EXPECT_EQ(lhs.conversion_preferences().use_history, |
| rhs.conversion_preferences().use_history); |
| EXPECT_EQ(lhs.conversion_preferences().max_history_size, |
| rhs.conversion_preferences().max_history_size); |
| EXPECT_EQ(IsCandidateListVisible(lhs), |
| IsCandidateListVisible(rhs)); |
| |
| Segments segments_lhs, segments_rhs; |
| GetSegments(lhs, &segments_lhs); |
| GetSegments(rhs, &segments_rhs); |
| EXPECT_EQ(segments_lhs.segments_size(), |
| segments_rhs.segments_size()); |
| for (size_t i = 0; i < segments_lhs.segments_size(); ++i) { |
| Segment segment_lhs, segment_rhs; |
| segment_lhs.CopyFrom(segments_lhs.segment(i)); |
| segment_rhs.CopyFrom(segments_rhs.segment(i)); |
| EXPECT_EQ(segment_lhs.key(), segment_rhs.key()) << " i=" << i; |
| EXPECT_EQ(segment_lhs.segment_type(), |
| segment_rhs.segment_type()) << " i=" << i; |
| EXPECT_EQ(segment_lhs.candidates_size(), segment_rhs.candidates_size()); |
| } |
| |
| const CandidateList &candidate_list_lhs = GetCandidateList(lhs); |
| const CandidateList &candidate_list_rhs = GetCandidateList(rhs); |
| EXPECT_EQ(candidate_list_lhs.name(), candidate_list_rhs.name()); |
| EXPECT_EQ(candidate_list_lhs.page_size(), candidate_list_rhs.page_size()); |
| EXPECT_EQ(candidate_list_lhs.size(), candidate_list_rhs.size()); |
| EXPECT_EQ(candidate_list_lhs.last_index(), candidate_list_rhs.last_index()); |
| EXPECT_EQ(candidate_list_lhs.focused_id(), candidate_list_rhs.focused_id()); |
| EXPECT_EQ(candidate_list_lhs.focused_index(), |
| candidate_list_rhs.focused_index()); |
| EXPECT_EQ(candidate_list_lhs.focused(), candidate_list_rhs.focused()); |
| |
| for (int i = 0; i < candidate_list_lhs.size(); ++i) { |
| const Candidate &candidate_lhs = candidate_list_lhs.candidate(i); |
| const Candidate &candidate_rhs = candidate_list_rhs.candidate(i); |
| EXPECT_EQ(candidate_lhs.id(), candidate_rhs.id()); |
| EXPECT_EQ(candidate_lhs.attributes(), candidate_rhs.attributes()); |
| EXPECT_EQ(candidate_lhs.IsSubcandidateList(), |
| candidate_rhs.IsSubcandidateList()); |
| if (candidate_lhs.IsSubcandidateList()) { |
| EXPECT_EQ(candidate_lhs.subcandidate_list().size(), |
| candidate_rhs.subcandidate_list().size()); |
| } |
| } |
| |
| EXPECT_PROTO_EQ(GetResult(lhs), GetResult(rhs)); |
| EXPECT_PROTO_EQ(GetRequest(lhs), GetRequest(rhs)); |
| } |
| |
| static ::testing::AssertionResult ExpectSelectedCandidateIndices( |
| const char *, const char *, |
| const SessionConverter &converter, const vector<int> &expected) { |
| const vector<int> &actual = converter.selected_candidate_indices_; |
| |
| if (expected.size() != actual.size()) { |
| return ::testing::AssertionFailure() |
| << "Indices size mismatch.\n" |
| << "Expected: " << expected.size() << "\n" |
| << "Actual: " << actual.size(); |
| } |
| |
| for (size_t i = 0; i < expected.size(); ++i) { |
| if (expected[i] != actual[i]) { |
| return ::testing::AssertionFailure() |
| << "Index mismatch.\n" |
| << "Expected: " << expected[i] << "\n" |
| << "Actual: " << actual[i]; |
| } |
| } |
| |
| return ::testing::AssertionSuccess(); |
| } |
| |
| static void SetCommandCandidate( |
| Segments *segments, int segment_index, int canidate_index, |
| Segment::Candidate::Command command) { |
| segments->mutable_conversion_segment(segment_index) |
| ->mutable_candidate(canidate_index)->attributes |
| |= Segment::Candidate::COMMAND_CANDIDATE; |
| segments->mutable_conversion_segment(segment_index) |
| ->mutable_candidate(canidate_index)->command = command; |
| } |
| |
| scoped_ptr<ConverterMock> convertermock_; |
| |
| scoped_ptr<composer::Composer> composer_; |
| scoped_ptr<composer::Table> table_; |
| scoped_ptr<Request> mobile_request_; |
| const Request default_request_; |
| mozc::usage_stats::scoped_usage_stats_enabler usage_stats_enabler_; |
| }; |
| |
| #define EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, indices) \ |
| EXPECT_PRED_FORMAT2(ExpectSelectedCandidateIndices, converter, indices); |
| |
| TEST_F(SessionConverterTest, Convert) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| vector<int> expected_indices; |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| composer_->InsertCharacterPreedit(kChars_Aiueo); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| ASSERT_TRUE(converter.IsActive()); |
| expected_indices.push_back(0); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| EXPECT_EQ(commands::Preedit::Segment::HIGHLIGHT, |
| conversion.segment(0).annotation()); |
| EXPECT_EQ(kChars_Aiueo, conversion.segment(0).value()); |
| EXPECT_EQ(kChars_Aiueo, conversion.segment(0).key()); |
| |
| // Converter should be active before submittion |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| |
| converter.Commit(*composer_, Context::default_instance()); |
| composer_->Reset(); |
| output.Clear(); |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| expected_indices.clear(); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| const commands::Result &result = output.result(); |
| EXPECT_EQ(kChars_Aiueo, result.value()); |
| EXPECT_EQ(kChars_Aiueo, result.key()); |
| |
| // Converter should be inactive after submittion |
| EXPECT_FALSE(converter.IsActive()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| EXPECT_COUNT_STATS("CommitFromConversion", 1); |
| EXPECT_COUNT_STATS("ConversionCandidates0", 1); |
| } |
| |
| TEST_F(SessionConverterTest, ConvertWithSpellingCorrection) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| segments.mutable_conversion_segment(0)->mutable_candidate(0)->attributes |= |
| Segment::Candidate::SPELLING_CORRECTION; |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| composer_->InsertCharacterPreedit(kChars_Aiueo); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| ASSERT_TRUE(converter.IsActive()); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| } |
| |
| TEST_F(SessionConverterTest, ConvertToTransliteration) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetAiueo(&segments); |
| |
| composer_->InsertCharacterKeyAndPreedit("aiueo", kChars_Aiueo); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::HALF_ASCII)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| { // Check the conversion #1 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| EXPECT_EQ("aiueo", conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::HALF_ASCII)); |
| { // Check the conversion #2 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| EXPECT_EQ("AIUEO", conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::FULL_ASCII)); |
| { // Check the conversion #3 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| EXPECT_EQ("\xEF\xBC\xA1\xEF\xBC\xA9\xEF\xBC\xB5\xEF\xBC\xA5\xEF\xBC\xAF", |
| conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| converter.Commit(*composer_, Context::default_instance()); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| EXPECT_COUNT_STATS("CommitFromConversion", 1); |
| EXPECT_COUNT_STATS("ConversionCandidates0", 1); |
| } |
| |
| TEST_F(SessionConverterTest, ConvertToTransliterationWithMultipleSegments) { |
| Segments segments; |
| InitConverterWithLike(&segments); |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| |
| // Convert |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| expected_indices.push_back(0); |
| { // Check the conversion #1 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(2, conversion.segment_size()); |
| // "ぃ" |
| EXPECT_EQ("\xE3\x81\x83", conversion.segment(0).value()); |
| // "家" |
| EXPECT_EQ("\xE5\xAE\xB6", conversion.segment(1).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| // Convert to half-width alphanumeric. |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::HALF_ASCII)); |
| { // Check the conversion #2 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(2, conversion.segment_size()); |
| EXPECT_EQ("li", conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, ConvertToTransliterationWithoutCascadigWindow) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { |
| Segment *segment; |
| Segment::Candidate *candidate; |
| segment = segments.add_segment(); |
| segment->set_key("dvd"); |
| candidate = segment->add_candidate(); |
| candidate->value = "dvd"; |
| candidate = segment->add_candidate(); |
| candidate->value = "DVD"; |
| } |
| { // Set OperationPreferences |
| OperationPreferences preferences; |
| preferences.use_cascading_window = false; |
| preferences.candidate_shortcuts = ""; |
| converter.SetOperationPreferences(preferences); |
| } |
| |
| // "dvd" |
| composer_->InsertCharacterKeyAndPreedit( |
| "dvd", "\xEF\xBD\x84\xEF\xBD\x96\xEF\xBD\x84"); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::FULL_ASCII)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| { // Check the conversion #1 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "dvd" |
| EXPECT_EQ("\xEF\xBD\x84\xEF\xBD\x96\xEF\xBD\x84", |
| conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::FULL_ASCII)); |
| { // Check the conversion #2 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "DVD" |
| EXPECT_EQ("\xEF\xBC\xA4\xEF\xBC\xB6\xEF\xBC\xA4", |
| conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::FULL_ASCII)); |
| { // Check the conversion #3 |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "Dvd" |
| EXPECT_EQ("\xEF\xBC\xA4\xEF\xBD\x96\xEF\xBD\x84", |
| conversion.segment(0).value()); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, MultiSegmentsConversion) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetKamaboko(&segments); |
| const string kKamabokono = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| const string kInbou = |
| "\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"; |
| |
| // Test for conversion |
| composer_->InsertCharacterPreedit(kKamabokono + kInbou); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| expected_indices.push_back(0); |
| { |
| // "かまぼこのいんぼう" |
| EXPECT_EQ(0, GetSegmentIndex(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(2, conversion.segment_size()); |
| EXPECT_EQ(commands::Preedit::Segment::HIGHLIGHT, |
| conversion.segment(0).annotation()); |
| EXPECT_EQ(kKamabokono, conversion.segment(0).key()); |
| EXPECT_EQ(kKamabokono, conversion.segment(0).value()); |
| |
| EXPECT_EQ(commands::Preedit::Segment::UNDERLINE, |
| conversion.segment(1).annotation()); |
| EXPECT_EQ(kInbou, conversion.segment(1).key()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", conversion.segment(1).value()); |
| } |
| |
| // Test for candidates [CandidateNext] |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.CandidateNext(*composer_); |
| expected_indices[0] += 1; |
| { |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| // Test for candidates [CandidatePrev] |
| converter.CandidatePrev(); |
| expected_indices[0] -= 1; |
| { |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_EQ(0, GetSegmentIndex(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(0, candidates.position()); |
| EXPECT_EQ(kKamabokono, candidates.candidate(0).value()); |
| // "カマボコの" |
| EXPECT_EQ("\xe3\x82\xab\xe3\x83\x9e\xe3\x83\x9c\xe3\x82\xb3\xe3\x81\xae", |
| candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| } |
| |
| // Test for segment motion. [SegmentFocusRight] |
| converter.SegmentFocusRight(); |
| { |
| EXPECT_EQ(1, GetSegmentIndex(converter)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SetCandidateListVisible(true); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(5, candidates.position()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", |
| candidates.candidate(0).value()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", |
| candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| } |
| |
| // Test for segment motion. [SegmentFocusLeft] |
| converter.SegmentFocusLeft(); |
| { |
| EXPECT_EQ(0, GetSegmentIndex(converter)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SetCandidateListVisible(true); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(0, candidates.position()); |
| EXPECT_EQ(kKamabokono, candidates.candidate(0).value()); |
| // "カマボコの" |
| EXPECT_EQ("\xe3\x82\xab\xe3\x83\x9e\xe3\x83\x9c\xe3\x82\xb3\xe3\x81\xae", |
| candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| } |
| |
| // Test for segment motion. [SegmentFocusLeft] at the head of segments. |
| // http://b/2990134 |
| // Focus changing at the tail of segments to right, |
| // and at the head of segments to left, should work. |
| converter.SegmentFocusLeft(); |
| { |
| EXPECT_EQ(1, GetSegmentIndex(converter)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SetCandidateListVisible(true); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(5, candidates.position()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", |
| candidates.candidate(0).value()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", |
| candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| } |
| |
| // Test for segment motion. [SegmentFocusRight] at the tail of segments. |
| // http://b/2990134 |
| // Focus changing at the tail of segments to right, |
| // and at the head of segments to left, should work. |
| converter.SegmentFocusRight(); |
| { |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SetCandidateListVisible(true); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| EXPECT_EQ(0, GetSegmentIndex(converter)); |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(0, candidates.position()); |
| EXPECT_EQ(kKamabokono, candidates.candidate(0).value()); |
| // "カマボコの" |
| EXPECT_EQ("\xe3\x82\xab\xe3\x83\x9e\xe3\x83\x9c\xe3\x82\xb3\xe3\x81\xae", |
| candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| } |
| |
| // Test for candidate motion. [CandidateNext] |
| converter.SegmentFocusRight(); // Focus to the last segment. |
| EXPECT_EQ(1, GetSegmentIndex(converter)); |
| converter.CandidateNext(*composer_); |
| expected_indices[1] += 1; |
| { |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(1, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(5, candidates.position()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", candidates.candidate(0).value()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(kKamabokono, conversion.segment(0).value()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", conversion.segment(1).value()); |
| } |
| |
| // Test for segment motion again [SegmentFocusLeftEdge] [SegmentFocusLast] |
| // The positions of "陰謀" and "印房" should be swapped. |
| { |
| Segments fixed_segments; |
| SetKamaboko(&fixed_segments); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // "陰謀" |
| ASSERT_EQ("\xe9\x99\xb0\xe8\xac\x80", |
| fixed_segments.segment(1).candidate(0).value); |
| // "印房" |
| ASSERT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", |
| fixed_segments.segment(1).candidate(1).value); |
| // swap the values. |
| fixed_segments.mutable_segment(1)->mutable_candidate(0)->value.swap( |
| fixed_segments.mutable_segment(1)->mutable_candidate(1)->value); |
| // "印房" |
| ASSERT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", |
| fixed_segments.segment(1).candidate(0).value); |
| // "陰謀" |
| ASSERT_EQ("\xe9\x99\xb0\xe8\xac\x80", |
| fixed_segments.segment(1).candidate(1).value); |
| convertermock_->SetCommitSegmentValue(&fixed_segments, true); |
| } |
| converter.SegmentFocusLeftEdge(); |
| { |
| EXPECT_EQ(0, GetSegmentIndex(converter)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SegmentFocusLast(); |
| EXPECT_EQ(1, GetSegmentIndex(converter)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| converter.SetCandidateListVisible(true); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(0, candidates.focused_index()); |
| EXPECT_EQ(3, candidates.size()); // two candidates + one t13n sub list. |
| EXPECT_EQ(5, candidates.position()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", candidates.candidate(0).value()); |
| |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", candidates.candidate(1).value()); |
| |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(2).value()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(kKamabokono, conversion.segment(0).value()); |
| // "印房" |
| EXPECT_EQ("\xe5\x8d\xb0\xe6\x88\xbf", conversion.segment(1).value()); |
| } |
| |
| converter.Commit(*composer_, Context::default_instance()); |
| expected_indices.clear(); |
| { |
| composer_->Reset(); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Result &result = output.result(); |
| // "かまぼこの印房" |
| EXPECT_EQ("\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae" |
| "\xe5\x8d\xb0\xe6\x88\xbf", |
| result.value()); |
| // "かまぼこのいんぼう" |
| EXPECT_EQ("\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae" |
| "\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86", |
| result.key()); |
| EXPECT_FALSE(converter.IsActive()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, Transliterations) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| // "く" |
| composer_->InsertCharacterKeyAndPreedit("h", "\xE3\x81\x8F"); |
| // "ま" |
| composer_->InsertCharacterKeyAndPreedit("J", "\xE3\x81\xBE"); |
| |
| Segments segments; |
| { // Initialize segments. |
| Segment *segment = segments.add_segment(); |
| // "くま" |
| segment->set_key("\xE3\x81\x8F\xE3\x81\xBE"); |
| // "クマー" |
| segment->add_candidate()->value = "\xE3\x82\xAF\xE3\x83\x9E\xE3\x83\xBC"; |
| } |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // Move to the t13n list. |
| converter.CandidateNext(*composer_); |
| expected_indices[0] = -1; |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(2, candidates.size()); // one candidate + one t13n sub list. |
| EXPECT_EQ(1, candidates.focused_index()); |
| // "そのほかの文字種"; |
| EXPECT_EQ("\xe3\x81\x9d\xe3\x81\xae\xe3\x81\xbb\xe3\x81\x8b\xe3\x81\xae" |
| "\xe6\x96\x87\xe5\xad\x97\xe7\xa8\xae", |
| candidates.candidate(1).value()); |
| |
| vector<string> t13ns; |
| composer_->GetTransliterations(&t13ns); |
| |
| EXPECT_TRUE(candidates.has_subcandidates()); |
| EXPECT_EQ(t13ns.size(), candidates.subcandidates().size()); |
| EXPECT_EQ(9, candidates.subcandidates().candidate_size()); |
| |
| for (size_t i = 0; i < candidates.subcandidates().candidate_size(); ++i) { |
| EXPECT_EQ(t13ns[i], candidates.subcandidates().candidate(i).value()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, T13NWithResegmentation) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| { |
| Segments segments; |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| CHECK(segment); |
| // "かまぼこの" |
| segment->set_key( |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"); |
| candidate = segment->add_candidate(); |
| CHECK(candidate); |
| // "かまぼこの" |
| candidate->value = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| |
| segment = segments.add_segment(); |
| CHECK(segment); |
| // "いんぼう" |
| segment->set_key("\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"); |
| candidate = segment->add_candidate(); |
| CHECK(candidate); |
| // "いんぼう" |
| candidate->value = |
| "\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"; |
| |
| InsertASCIISequence("kamabokonoinbou", composer_.get()); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| } |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| expected_indices.push_back(0); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // Test for segment motion. [SegmentFocusRight] |
| converter.SegmentFocusRight(); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| // Shrink segment |
| { |
| Segments segments; |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segments.Clear(); |
| segment = segments.add_segment(); |
| |
| // "かまぼこの" |
| segment->set_key( |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"); |
| candidate = segment->add_candidate(); |
| // "かまぼこの" |
| candidate->value = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| candidate = segment->add_candidate(); |
| // "カマボコの" |
| candidate->value = |
| "\xe3\x82\xab\xe3\x83\x9e\xe3\x83\x9c\xe3\x82\xb3\xe3\x81\xae"; |
| |
| segment = segments.add_segment(); |
| // "いんぼ" |
| segment->set_key("\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc"); |
| candidate = segment->add_candidate(); |
| // "インボ" |
| candidate->value = "\xe3\x82\xa4\xe3\x83\xb3\xe3\x83\x9c"; |
| |
| segment = segments.add_segment(); |
| // "う" |
| segment->set_key("\xe3\x81\x86"); |
| candidate = segment->add_candidate(); |
| // "ウ" |
| candidate->value = "\xe3\x82\xa6"; |
| |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetResizeSegment1(&segments, true); |
| } |
| converter.SegmentWidthShrink(*composer_); |
| expected_indices.push_back(0); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // Convert to half katakana. Expected index should be 0. |
| converter.ConvertToTransliteration(*composer_, |
| transliteration::HALF_KATAKANA); |
| expected_indices[0] = 0; |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| const commands::Preedit &preedit = output.preedit(); |
| EXPECT_EQ(3, preedit.segment_size()); |
| // "インボ" |
| EXPECT_EQ("\xef\xbd\xb2\xef\xbe\x9d\xef\xbe\x8e\xef\xbe\x9e", |
| preedit.segment(1).value()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, ConvertToHalfWidth) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| vector<int> expected_indices; |
| // "あ" |
| composer_->InsertCharacterKeyAndPreedit("a", "\xE3\x81\x82"); |
| // "b" |
| composer_->InsertCharacterKeyAndPreedit("b", "\xEF\xBD\x82"); |
| // "c" |
| composer_->InsertCharacterKeyAndPreedit("c", "\xEF\xBD\x83"); |
| |
| Segments segments; |
| { // Initialize segments. |
| Segment *segment = segments.add_segment(); |
| // "あbc" |
| segment->set_key("\xE3\x81\x82\xEF\xBD\x82\xEF\xBD\x83"); |
| // "あべし" |
| segment->add_candidate()->value = "\xE3\x81\x82\xE3\x81\xB9\xE3\x81\x97"; |
| } |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_)); |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "アbc" |
| EXPECT_EQ("\xEF\xBD\xB1\x62\x63", conversion.segment(0).value()); |
| } |
| |
| // Composition will be transliterated to "abc". |
| EXPECT_TRUE(converter.ConvertToTransliteration(*composer_, |
| transliteration::FULL_ASCII)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "abc" |
| EXPECT_EQ("\xEF\xBD\x81\xEF\xBD\x82\xEF\xBD\x83", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "abc" |
| EXPECT_EQ("abc", conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "ABC" |
| EXPECT_EQ("ABC", conversion.segment(0).value()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, ConvertToHalfWidth_2) { |
| // http://b/2517514 |
| // ConvertToHalfWidth converts punctuations differently w/ or w/o kana. |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| // "q" |
| composer_->InsertCharacterKeyAndPreedit("q", "\xef\xbd\x91"); |
| // "、" |
| composer_->InsertCharacterKeyAndPreedit(",", "\xe3\x80\x81"); |
| // "。" |
| composer_->InsertCharacterKeyAndPreedit(".", "\xe3\x80\x82"); |
| |
| Segments segments; |
| { // Initialize segments. |
| Segment *segment = segments.add_segment(); |
| // "q、。" |
| segment->set_key("\xef\xbd\x91\xe3\x80\x81\xe3\x80\x82"); |
| segment->add_candidate()->value = "q,."; |
| // "q、。" |
| segment->add_candidate()->value = "q\xef\xbd\xa4\xef\xbd\xa1"; |
| } |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "q、。" |
| EXPECT_EQ("q\xef\xbd\xa4\xef\xbd\xa1", conversion.segment(0).value()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, SwitchKanaType) { |
| { // From composition mode. |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| // "あ" |
| composer_->InsertCharacterKeyAndPreedit("a", "\xE3\x81\x82"); |
| // "b" |
| composer_->InsertCharacterKeyAndPreedit("b", "\xEF\xBD\x82"); |
| // "c" |
| composer_->InsertCharacterKeyAndPreedit("c", "\xEF\xBD\x83"); |
| |
| Segments segments; |
| { // Initialize segments. |
| Segment *segment = segments.add_segment(); |
| // "あbc" |
| segment->set_key("\xE3\x81\x82\xEF\xBD\x82\xEF\xBD\x83"); |
| // "あべし" |
| segment->add_candidate()->value = "\xE3\x81\x82\xE3\x81\xB9\xE3\x81\x97"; |
| } |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "アbc" |
| EXPECT_EQ("\xE3\x82\xA2\xEF\xBD\x82\xEF\xBD\x83", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "アbc" |
| EXPECT_EQ("\xEF\xBD\xB1\x62\x63", conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "あbc" |
| EXPECT_EQ("\xE3\x81\x82\xEF\xBD\x82\xEF\xBD\x83", |
| conversion.segment(0).value()); |
| } |
| } |
| |
| { // From conversion mode |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| composer_->EditErase(); |
| // "か" |
| composer_->InsertCharacterKeyAndPreedit("ka", "\xE3\x81\x8B"); |
| // "ん" |
| composer_->InsertCharacterKeyAndPreedit("n", "\xE3\x82\x93"); |
| // "じ" |
| composer_->InsertCharacterKeyAndPreedit("ji", "\xE3\x81\x98"); |
| |
| Segments segments; |
| { // Initialize segments. |
| Segment *segment = segments.add_segment(); |
| // "かんじ" |
| segment->set_key("\xE3\x81\x8B\xE3\x82\x93\xE3\x81\x98"); |
| // "漢字" |
| segment->add_candidate()->value = "\xE6\xBC\xA2\xE5\xAD\x97"; |
| } |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Make sure the output |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "漢字" |
| EXPECT_EQ("\xE6\xBC\xA2\xE5\xAD\x97", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "かんじ" |
| EXPECT_EQ("\xE3\x81\x8B\xE3\x82\x93\xE3\x81\x98", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "カンジ" |
| EXPECT_EQ("\xE3\x82\xAB\xE3\x83\xB3\xE3\x82\xB8", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "カンジ" |
| EXPECT_EQ("\xEF\xBD\xB6\xEF\xBE\x9D\xEF\xBD\xBC\xEF\xBE\x9E", |
| conversion.segment(0).value()); |
| } |
| |
| EXPECT_TRUE(converter.SwitchKanaType(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "かんじ" |
| EXPECT_EQ("\xE3\x81\x8B\xE3\x82\x93\xE3\x81\x98", |
| conversion.segment(0).value()); |
| } |
| } |
| } |
| |
| TEST_F(SessionConverterTest, CommitFirstSegment) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetKamaboko(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| // "かまぼこの" |
| const string kKamabokono = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| // "いんぼう" |
| const string kInbou = |
| "\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"; |
| |
| // "かまぼこのいんぼう" |
| composer_->InsertCharacterPreedit(kKamabokono + kInbou); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| expected_indices.push_back(0); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the conversion. |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| // "かまぼこの" |
| EXPECT_EQ(kKamabokono, conversion.segment(0).value()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", conversion.segment(1).value()); |
| } |
| |
| // "カマボコの陰謀" |
| converter.CandidateNext(*composer_); |
| expected_indices[0] += 1; |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the conversion. |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Preedit &conversion = output.preedit(); |
| // "カマボコの" |
| EXPECT_EQ("\xE3\x82\xAB\xE3\x83\x9E\xE3\x83\x9C\xE3\x82\xB3\xE3\x81\xAE", |
| conversion.segment(0).value()); |
| // "陰謀" |
| EXPECT_EQ("\xe9\x99\xb0\xe8\xac\x80", conversion.segment(1).value()); |
| } |
| |
| { // Initialization of SetCommitSegments. |
| Segments segments_after_submit; |
| Segment *segment = segments_after_submit.add_segment(); |
| // "いんぼう" |
| segment->set_key("\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"); |
| segment->add_candidate()->value = "\xe9\x99\xb0\xe8\xac\x80"; // "陰謀" |
| segment->add_candidate()->value = "\xe5\x8d\xb0\xe6\x88\xbf"; // "印房" |
| convertermock_->SetCommitSegments(&segments_after_submit, true); |
| } |
| size_t size; |
| converter.CommitFirstSegment(*composer_, Context::default_instance(), &size); |
| expected_indices.erase(expected_indices.begin(), |
| expected_indices.begin() + 1); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| // "かまぼこの" |
| EXPECT_EQ(Util::CharsLen(kKamabokono), size); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| EXPECT_COUNT_STATS("CommitFromConversion", 1); |
| EXPECT_STATS_NOT_EXIST("ConversionCandidates0"); |
| EXPECT_COUNT_STATS("ConversionCandidates1", 1); |
| } |
| |
| TEST_F(SessionConverterTest, CommitHeadToFocusedSegments) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| // "いべりこ" |
| const string kIberiko = "\xE3\x81\x84\xE3\x81\xB9\xE3\x82\x8A\xE3\x81\x93"; |
| // "ねこを" |
| const string kNekowo = "\xE3\x81\xAD\xE3\x81\x93\xE3\x82\x92"; |
| // "いただいた" |
| const string kItadaita = "\xE3\x81\x84\xE3\x81\x9F\xE3\x81" |
| "\xA0\xE3\x81\x84\xE3\x81\x9F"; |
| { // Three segments as the result of conversion. |
| Segments segments; |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segment = segments.add_segment(); |
| segment->set_key(kIberiko); |
| candidate = segment->add_candidate(); |
| // "イベリコ" |
| candidate->value = "\xE3\x82\xA4\xE3\x83\x99\xE3\x83\xAA\xE3\x82\xB3"; |
| |
| segment = segments.add_segment(); |
| segment->set_key(kNekowo); |
| candidate = segment->add_candidate(); |
| // "猫を" |
| candidate->value = "\xE7\x8C\xAB\xE3\x82\x92"; |
| |
| segment = segments.add_segment(); |
| segment->set_key(kItadaita); |
| candidate = segment->add_candidate(); |
| // "頂いた" |
| candidate->value = "\xE9\xA0\x82\xE3\x81\x84\xE3\x81\x9F"; |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| } |
| |
| composer_->InsertCharacterPreedit(kIberiko + kNekowo + kItadaita); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| // Here [イベリコ]|猫を|頂いた |
| |
| converter.SegmentFocusRight(); |
| // Here イベリコ|[猫を]|頂いた |
| |
| { // Initialization of SetCommitSegments. |
| Segments segments; |
| Segment *segment; |
| Segment::Candidate *candidate; |
| |
| segment = segments.add_segment(); |
| segment->set_key(kItadaita); |
| candidate = segment->add_candidate(); |
| // "頂いた" |
| candidate->value = "\xE9\xA0\x82\xE3\x81\x84\xE3\x81\x9F"; |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| convertermock_->SetCommitSegments(&segments, true); |
| } |
| size_t size; |
| converter.CommitHeadToFocusedSegments(*composer_, |
| Context::default_instance(), &size); |
| // Here 頂いた |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_EQ(Util::CharsLen(kIberiko + kNekowo), size); |
| EXPECT_TRUE(converter.IsActive()); |
| } |
| |
| TEST_F(SessionConverterTest, CommitHeadToFocusedSegments_atLastSegment) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetKamaboko(&segments); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| // "かまぼこの" |
| const string kKamabokono = |
| "\xe3\x81\x8b\xe3\x81\xbe\xe3\x81\xbc\xe3\x81\x93\xe3\x81\xae"; |
| // "いんぼう" |
| const string kInbou = |
| "\xe3\x81\x84\xe3\x82\x93\xe3\x81\xbc\xe3\x81\x86"; |
| |
| // "かまぼこのいんぼう" |
| composer_->InsertCharacterPreedit(kKamabokono + kInbou); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| // Here [かまぼこの]|陰謀 |
| |
| converter.SegmentFocusRight(); |
| // Here かまぼこの|[陰謀] |
| |
| { // Initialization of SetCommitSegments. |
| Segments segments_after_submit; |
| convertermock_->SetCommitSegments(&segments_after_submit, true); |
| } |
| size_t size; |
| // All the segments should be committed. |
| converter.CommitHeadToFocusedSegments(*composer_, |
| Context::default_instance(), &size); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_EQ(0, size); |
| EXPECT_FALSE(converter.IsActive()); |
| } |
| |
| TEST_F(SessionConverterTest, CommitPreedit) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| vector<int> expected_indices; |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| composer_->InsertCharacterPreedit(kChars_Aiueo); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| converter.CommitPreedit(*composer_, Context::default_instance()); |
| composer_->Reset(); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the result |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Result &result = output.result(); |
| EXPECT_EQ(kChars_Aiueo, result.value()); |
| EXPECT_EQ(kChars_Aiueo, result.key()); |
| } |
| EXPECT_FALSE(converter.IsActive()); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| EXPECT_COUNT_STATS("CommitFromComposition", 1); |
| } |
| |
| TEST_F(SessionConverterTest, CommitSuggestionByIndex) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずくす" |
| candidate->value = kChars_Mozukusu; |
| candidate->key = kChars_Mozukusu; |
| candidate->content_key = kChars_Mozukusu; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->key = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| } |
| composer_->InsertCharacterPreedit(kChars_Mo); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Preedit &preedit = output.preedit(); |
| EXPECT_EQ(1, preedit.segment_size()); |
| EXPECT_EQ(kChars_Mo, preedit.segment(0).value()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(2, candidates.size()); |
| EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value()); |
| EXPECT_FALSE(candidates.has_focused_index()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| // FinishConversion is expected to return empty Segments. |
| convertermock_->SetFinishConversion( |
| scoped_ptr<Segments>(new Segments).get(), true); |
| |
| size_t committed_key_size = 0; |
| converter.CommitSuggestionByIndex(1, *composer_.get(), |
| Context::default_instance(), |
| &committed_key_size); |
| expected_indices.clear(); |
| composer_->Reset(); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_FALSE(converter.IsActive()); |
| EXPECT_EQ(SessionConverter::kConsumedAllCharacters, committed_key_size); |
| |
| { // Check the result |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Result &result = output.result(); |
| EXPECT_EQ(kChars_Momonga, result.value()); |
| EXPECT_EQ(kChars_Momonga, result.key()); |
| EXPECT_EQ(SessionConverterInterface::COMPOSITION, GetState(converter)); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| } |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| // Suggestion is counted as Prediction |
| EXPECT_COUNT_STATS("CommitFromPrediction", 1); |
| EXPECT_COUNT_STATS("PredictionCandidates1", 1); |
| } |
| |
| TEST_F(SessionConverterTest, CommitSuggestionById) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずくす" |
| candidate->value = kChars_Mozukusu; |
| candidate->key = kChars_Mozukusu; |
| candidate->content_key = kChars_Mozukusu; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->key = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| } |
| composer_->InsertCharacterPreedit(kChars_Mo); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // FinishConversion is expected to return empty Segments. |
| convertermock_->SetFinishConversion( |
| scoped_ptr<Segments>(new Segments).get(), true); |
| |
| const int kCandidateIndex = 1; |
| size_t committed_key_size = 0; |
| convertermock_->SetCommitSegmentValue(&segments, true); |
| converter.CommitSuggestionById(kCandidateIndex, *composer_.get(), |
| Context::default_instance(), |
| &committed_key_size); |
| expected_indices.clear(); |
| composer_->Reset(); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_FALSE(converter.IsActive()); |
| EXPECT_EQ(SessionConverter::kConsumedAllCharacters, committed_key_size); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the result |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Result &result = output.result(); |
| EXPECT_EQ(kChars_Momonga, result.value()); |
| EXPECT_EQ(kChars_Momonga, result.key()); |
| EXPECT_EQ(SessionConverterInterface::COMPOSITION, GetState(converter)); |
| } |
| // Check the converter's internal state |
| Segments committed_segments; |
| size_t segment_index; |
| int candidate_index; |
| convertermock_->GetCommitSegmentValue( |
| &committed_segments, &segment_index, &candidate_index); |
| EXPECT_EQ(0, segment_index); |
| EXPECT_EQ(kCandidateIndex, candidate_index); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| // Suggestion is counted as Prediction. |
| EXPECT_COUNT_STATS("CommitFromPrediction", 1); |
| EXPECT_COUNT_STATS("PredictionCandidates" + |
| NumberUtil::SimpleItoa(kCandidateIndex), |
| 1); |
| } |
| |
| TEST_F(SessionConverterTest, PartialSuggestion) { |
| SessionConverter converter(convertermock_.get(), mobile_request_.get()); |
| Segments segments1, segments2; |
| Segments suggestion_segments; |
| // "ここで" |
| const string kChars_Kokode = |
| "\xe3\x81\x93\xe3\x81\x93\xe3\x81\xa7"; |
| // "はきものを" |
| const string kChars_Hakimonowo = |
| "\xe3\x81\xaf\xe3\x81\x8d\xe3\x82\x82\xe3\x81\xae\xe3\x82\x92"; |
| |
| { // Initialize mock segments for partial suggestion |
| segments1.set_request_type(Segments::PARTIAL_SUGGESTION); |
| Segment *segment = segments1.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kChars_Kokode); |
| candidate = segment->add_candidate(); |
| // "此処で" |
| candidate->value = "\xe6\xad\xa4\xe5\x87\xa6\xe3\x81\xa7\xe3\x81\xaf"; |
| candidate->key = kChars_Kokode; |
| candidate->content_key = kChars_Kokode; |
| candidate->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED; |
| candidate->consumed_key_size = Util::CharsLen(kChars_Kokode); |
| } |
| |
| // Suggestion that matches to the same key by its prefix. |
| // Should not be used by partial suggestion. |
| { |
| suggestion_segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = suggestion_segments.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kChars_Kokode); |
| candidate = segment->add_candidate(); |
| // "ここでは着物を" |
| candidate->value = "\xe3\x81\x93\xe3\x81\x93\xe3\x81\xa7\xe3\x81" |
| "\xaf\xe7\x9d\x80\xe7\x89\xa9\xe3\x82\x92"; |
| // "ここではきものを" |
| candidate->key = "\xe3\x81\x93\xe3\x81\x93\xe3\x81\xa7\xe3\x81" |
| "\xaf\xe3\x81\x8d\xe3\x82\x82\xe3\x81\xae\xe3\x82\x92"; |
| candidate->content_key = candidate->key; |
| candidate = segment->add_candidate(); |
| } |
| |
| { // Initialize mock segments for suggestion |
| segments2.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments2.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kChars_Hakimonowo); |
| candidate = segment->add_candidate(); |
| // "履物を" |
| candidate->value = "\xe6\xad\xa4\xe5\x87\xa6\xe3\x81\xa7\xe3\x81\xaf"; |
| candidate->key = kChars_Hakimonowo; |
| candidate->content_key = kChars_Hakimonowo; |
| } |
| |
| // "ここではきものを|" ("|" is cursor position) |
| composer_->InsertCharacterPreedit(kChars_Kokode + kChars_Hakimonowo); |
| composer_->MoveCursorToEnd(); |
| // Suggestion for "ここではきものを". Not partial suggestion. |
| convertermock_->SetStartSuggestionForRequest(&suggestion_segments, true); |
| convertermock_->SetStartPartialSuggestion(&suggestion_segments, false); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // "|ここではきものを" ("|" is cursor position) |
| composer_->MoveCursorTo(0); |
| |
| // Suggestion for "ここではきものを". Not partial suggestion. |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // "ここで|はきものを" ("|" is cursor position) |
| composer_->MoveCursorTo(3); |
| |
| // Partial Suggestion for "ここで" |
| convertermock_->SetStartSuggestionForRequest(&segments1, false); |
| convertermock_->SetStartPartialSuggestion(&segments1, false); |
| convertermock_->SetStartPartialSuggestionForRequest(&segments1, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // commit partial suggestion |
| size_t committed_key_size = 0; |
| convertermock_->SetStartSuggestionForRequest(&segments2, true); |
| convertermock_->SetStartPartialSuggestion(&segments2, false); |
| converter.CommitSuggestionById(0, *composer_.get(), |
| Context::default_instance(), |
| &committed_key_size); |
| EXPECT_EQ(Util::CharsLen(kChars_Kokode), committed_key_size); |
| // Indices should be {0} since there is another segment. |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the result |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| |
| const commands::Result &result = output.result(); |
| // "此処で" |
| EXPECT_EQ("\xe6\xad\xa4\xe5\x87\xa6\xe3\x81\xa7\xe3\x81\xaf", |
| result.value()); |
| EXPECT_EQ(kChars_Kokode, result.key()); |
| EXPECT_EQ(SessionConverterInterface::SUGGESTION, GetState(converter)); |
| } |
| // Check the converter's internal state |
| Segments committed_segments; |
| size_t segment_index; |
| int candidate_index; |
| string current_segment_key; |
| string new_segment_key; |
| convertermock_->GetCommitPartialSuggestionSegmentValue(&committed_segments, |
| &segment_index, |
| &candidate_index, |
| ¤t_segment_key, |
| &new_segment_key); |
| EXPECT_EQ(0, segment_index); |
| EXPECT_EQ(0, candidate_index); |
| EXPECT_EQ(kChars_Kokode, current_segment_key); |
| EXPECT_EQ(kChars_Hakimonowo, new_segment_key); |
| |
| EXPECT_COUNT_STATS("Commit", 1); |
| // Suggestion is counted as Prediction. |
| EXPECT_COUNT_STATS("CommitFromPrediction", 1); |
| EXPECT_COUNT_STATS("PredictionCandidates0", 1); |
| } |
| |
| TEST_F(SessionConverterTest, SuggestAndPredict) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずくす" |
| candidate->value = kChars_Mozukusu; |
| candidate->content_key = kChars_Mozukusu; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| } |
| composer_->InsertCharacterPreedit(kChars_Mo); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| vector<int> expected_indices; |
| expected_indices.push_back(0); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| EXPECT_TRUE(output.candidates().has_footer()); |
| #if defined(CHANNEL_DEV) && defined(GOOGLE_JAPANESE_INPUT_BUILD) |
| EXPECT_FALSE(output.candidates().footer().has_label()); |
| EXPECT_TRUE(output.candidates().footer().has_sub_label()); |
| #else // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD |
| EXPECT_TRUE(output.candidates().footer().has_label()); |
| EXPECT_FALSE(output.candidates().footer().has_sub_label()); |
| #endif // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD |
| EXPECT_FALSE(output.candidates().footer().index_visible()); |
| EXPECT_FALSE(output.candidates().footer().logo_visible()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(2, candidates.size()); |
| // "もずくす" |
| EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value()); |
| EXPECT_FALSE(candidates.has_focused_index()); |
| } |
| |
| segments.Clear(); |
| { // Initialize mock segments for prediction |
| segments.set_request_type(Segments::PREDICTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずく" |
| candidate->value = kChars_Mozuku; |
| candidate->content_key = kChars_Mozuku; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| candidate = segment->add_candidate(); |
| // "モンドリアン" |
| candidate->value = "\xE3\x83\xA2\xE3\x83\xB3\xE3\x83\x89" |
| "\xE3\x83\xAA\xE3\x82\xA2\xE3\x83\xB3"; |
| // "もんどりあん" |
| candidate->content_key = "\xE3\x82\x82\xE3\x82\x93\xE3\x81\xA9" |
| "\xE3\x82\x8A\xE3\x81\x82\xE3\x82\x93"; |
| } |
| |
| // Prediction |
| convertermock_->SetStartPredictionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Predict(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| // If there are suggestion results, the Prediction is not triggered. |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| EXPECT_FALSE(output.candidates().footer().has_label()); |
| EXPECT_TRUE(output.candidates().footer().index_visible()); |
| EXPECT_TRUE(output.candidates().footer().logo_visible()); |
| |
| // Check the conversion |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| EXPECT_EQ(kChars_Mozukusu, conversion.segment(0).value()); |
| |
| // Check the candidate list |
| const commands::Candidates &candidates = output.candidates(); |
| // Candidates should be the same as suggestion |
| EXPECT_EQ(2, candidates.size()); |
| EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value()); |
| EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value()); |
| EXPECT_TRUE(candidates.has_focused_index()); |
| EXPECT_EQ(0, candidates.focused_index()); |
| } |
| |
| // Prediction is called |
| converter.CandidateNext(*composer_); |
| converter.CandidateNext(*composer_); |
| expected_indices[0] += 2; |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| const commands::Candidates &candidates = output.candidates(); |
| // Candidates should be merged with the previous suggestions. |
| EXPECT_EQ(4, candidates.size()); |
| // "もずくす" |
| EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value()); |
| // "ももんが" |
| EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value()); |
| // "もずく" |
| EXPECT_EQ(kChars_Mozuku, candidates.candidate(2).value()); |
| // "モンドリアン" |
| EXPECT_EQ("\xE3\x83\xA2\xE3\x83\xB3\xE3\x83\x89" |
| "\xE3\x83\xAA\xE3\x82\xA2\xE3\x83\xB3", |
| candidates.candidate(3).value()); |
| EXPECT_TRUE(candidates.has_focused_index()); |
| } |
| |
| // Select to "モンドリアン". |
| converter.CandidateNext(*composer_); |
| expected_indices[0] += 1; |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| converter.Commit(*composer_, Context::default_instance()); |
| composer_->Reset(); |
| expected_indices.clear(); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { // Check the submitted value |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_TRUE(output.has_result()); |
| EXPECT_FALSE(output.has_preedit()); |
| EXPECT_FALSE(output.has_candidates()); |
| |
| const commands::Result &result = output.result(); |
| // "モンドリアン" |
| EXPECT_EQ("\xE3\x83\xA2\xE3\x83\xB3\xE3\x83\x89" |
| "\xE3\x83\xAA\xE3\x82\xA2\xE3\x83\xB3", |
| result.value()); |
| // "もんどりあん" |
| EXPECT_EQ("\xE3\x82\x82\xE3\x82\x93\xE3\x81\xA9" |
| "\xE3\x82\x8A\xE3\x81\x82\xE3\x82\x93", |
| result.key()); |
| } |
| |
| segments.Clear(); |
| { // Initialize mock segments for prediction |
| segments.set_request_type(Segments::PREDICTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずく" |
| candidate->value = kChars_Mozuku; |
| candidate->content_key = kChars_Mozuku; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| candidate = segment->add_candidate(); |
| // "モンドリアン" |
| candidate->value = "\xE3\x83\xA2\xE3\x83\xB3\xE3\x83\x89" |
| "\xE3\x83\xAA\xE3\x82\xA2\xE3\x83\xB3"; |
| // "もんどりあん" |
| candidate->content_key = "\xE3\x82\x82\xE3\x82\x93\xE3\x81\xA9" |
| "\xE3\x82\x8A\xE3\x81\x82\xE3\x82\x93"; |
| } |
| |
| // Prediction without suggestion. |
| convertermock_->SetStartPredictionForRequest(&segments, true); |
| expected_indices.push_back(0); |
| EXPECT_TRUE(converter.Predict(*composer_)); |
| EXPECT_TRUE(converter.IsActive()); |
| EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices); |
| |
| { |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| EXPECT_FALSE(output.has_result()); |
| EXPECT_TRUE(output.has_preedit()); |
| EXPECT_TRUE(output.has_candidates()); |
| |
| // Check the conversion |
| const commands::Preedit &conversion = output.preedit(); |
| EXPECT_EQ(1, conversion.segment_size()); |
| // "もずく" |
| EXPECT_EQ(kChars_Mozuku, conversion.segment(0).value()); |
| |
| // Check the candidate list |
| const commands::Candidates &candidates = output.candidates(); |
| // Candidates should NOT be merged with the previous suggestions. |
| EXPECT_EQ(3, candidates.size()); |
| // "もずく" |
| EXPECT_EQ(kChars_Mozuku, candidates.candidate(0).value()); |
| // "ももんが" |
| EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value()); |
| // "モンドリアン" |
| EXPECT_EQ("\xE3\x83\xA2\xE3\x83\xB3\xE3\x83\x89" |
| "\xE3\x83\xAA\xE3\x82\xA2\xE3\x83\xB3", |
| candidates.candidate(2).value()); |
| EXPECT_TRUE(candidates.has_focused_index()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, SuppressSuggestionOnPasswordField) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずくす" |
| candidate->value = kChars_Mozukusu; |
| candidate->content_key = kChars_Mozukusu; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| } |
| composer_->InsertCharacterPreedit(kChars_Mo); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| // No candidates should be visible because we are on password field. |
| |
| ConversionPreferences conversion_preferences = |
| converter.conversion_preferences(); |
| conversion_preferences.request_suggestion = false; |
| EXPECT_FALSE(converter.SuggestWithPreferences( |
| *composer_, conversion_preferences)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_FALSE(converter.IsActive()); |
| } |
| |
| TEST_F(SessionConverterTest, SuppressSuggestion) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| // "も" |
| segment->set_key(kChars_Mo); |
| candidate = segment->add_candidate(); |
| // "もずくす" |
| candidate->value = kChars_Mozukusu; |
| candidate->content_key = kChars_Mozukusu; |
| candidate = segment->add_candidate(); |
| // "ももんが" |
| candidate->value = kChars_Momonga; |
| candidate->content_key = kChars_Momonga; |
| } |
| composer_->SetInputFieldType(Context::PASSWORD); |
| composer_->InsertCharacterPreedit(kChars_Mo); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| // No candidates should be visible because we are on password field. |
| EXPECT_FALSE(converter.Suggest(*composer_)); |
| EXPECT_FALSE(IsCandidateListVisible(converter)); |
| EXPECT_FALSE(converter.IsActive()); |
| } |
| |
| TEST_F(SessionConverterTest, ExpandPartialSuggestion) { |
| SessionConverter converter(convertermock_.get(), mobile_request_.get()); |
| |
| const char *kSuggestionValues[] = { |
| "S0", |
| "S1", |
| "S2", |
| }; |
| const char *kPredictionValues[] = { |
| "P0", |
| "P1", |
| "P2", |
| // Duplicate entry. Any dupulication should not exist |
| // in the candidate list. |
| "S1", |
| "P3", |
| }; |
| const char kPredictionKey[] = "left"; |
| const char kSuffixKey[] = "right"; |
| const int kDupulicationIndex = 3; |
| |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::PARTIAL_SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kPredictionKey); |
| for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) { |
| candidate = segment->add_candidate(); |
| candidate->value = kSuggestionValues[i]; |
| candidate->content_key = kPredictionKey; |
| } |
| } |
| composer_->InsertCharacterPreedit( |
| string(kPredictionKey) + string(kSuffixKey)); |
| composer_->MoveCursorTo(Util::CharsLen(string(kPredictionKey))); |
| |
| // Partial Suggestion |
| convertermock_->SetStartPartialSuggestionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| |
| segments.Clear(); |
| { |
| // Initialize mock segments for partial prediction for expanding suggestion. |
| segments.set_request_type(Segments::PARTIAL_PREDICTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kPredictionKey); |
| for (size_t i = 0; i < arraysize(kPredictionValues); ++i) { |
| candidate = segment->add_candidate(); |
| candidate->value = kPredictionValues[i]; |
| candidate->content_key = kPredictionKey; |
| } |
| } |
| // Expand suggestion candidate (cursor == HEAD) |
| composer_->MoveCursorTo(0); |
| convertermock_->SetStartPartialPrediction(&segments, false); |
| convertermock_->SetStartPredictionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ExpandSuggestion(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| |
| // Expand suggestion candidate (cursor == TAIL) |
| composer_->MoveCursorTo(composer_->GetLength()); |
| convertermock_->SetStartPredictionForRequest(&segments, false); |
| convertermock_->SetStartPartialPrediction(&segments, false); |
| convertermock_->SetStartPartialPredictionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ExpandSuggestion(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(commands::SUGGESTION, candidates.category()); |
| EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category()); |
| // -1 is for duplicate entry. |
| EXPECT_EQ(arraysize(kSuggestionValues) + arraysize(kPredictionValues) - 1, |
| candidates.size()); |
| size_t i; |
| for (i = 0; i < arraysize(kSuggestionValues); ++i) { |
| EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value()); |
| } |
| |
| // -1 is for duplicate entry. |
| for (; i < candidates.size(); ++i) { |
| size_t index_in_prediction = i - arraysize(kSuggestionValues); |
| if (index_in_prediction >= kDupulicationIndex) { |
| ++index_in_prediction; |
| } |
| EXPECT_EQ(kPredictionValues[index_in_prediction], |
| candidates.candidate(i).value()); |
| } |
| } |
| } |
| |
| TEST_F(SessionConverterTest, ExpandSuggestion) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| |
| const char *kSuggestionValues[] = { |
| "S0", |
| "S1", |
| "S2", |
| }; |
| const char *kPredictionValues[] = { |
| "P0", |
| "P1", |
| "P2", |
| // Duplicate entry. Any dupulication should not exist |
| // in the candidate list. |
| "S1", |
| "P3", |
| }; |
| const char kKey[] = "key"; |
| const int kDupulicationIndex = 3; |
| |
| Segments segments; |
| { // Initialize mock segments for suggestion |
| segments.set_request_type(Segments::SUGGESTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kKey); |
| for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) { |
| candidate = segment->add_candidate(); |
| candidate->value = kSuggestionValues[i]; |
| candidate->content_key = kKey; |
| } |
| } |
| composer_->InsertCharacterPreedit(kKey); |
| |
| // Suggestion |
| convertermock_->SetStartSuggestionForRequest(&segments, true); |
| EXPECT_TRUE(converter.Suggest(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(commands::SUGGESTION, candidates.category()); |
| EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category()); |
| EXPECT_EQ(arraysize(kSuggestionValues), candidates.size()); |
| for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) { |
| EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value()); |
| } |
| } |
| |
| segments.Clear(); |
| { // Initialize mock segments for prediction (== expanding suggestion) |
| segments.set_request_type(Segments::PREDICTION); |
| Segment *segment = segments.add_segment(); |
| Segment::Candidate *candidate; |
| segment->set_key(kKey); |
| for (size_t i = 0; i < arraysize(kPredictionValues); ++i) { |
| candidate = segment->add_candidate(); |
| candidate->value = kPredictionValues[i]; |
| candidate->content_key = kKey; |
| } |
| } |
| // Expand suggestion candidate |
| convertermock_->SetStartPredictionForRequest(&segments, true); |
| EXPECT_TRUE(converter.ExpandSuggestion(*composer_)); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| EXPECT_TRUE(converter.IsActive()); |
| { // Check the candidate list |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
| const commands::Candidates &candidates = output.candidates(); |
| EXPECT_EQ(commands::SUGGESTION, candidates.category()); |
| EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category()); |
| // -1 is for duplicate entry. |
| EXPECT_EQ(arraysize(kSuggestionValues) + arraysize(kPredictionValues) - 1, |
| candidates.size()); |
| size_t i; |
| for (i = 0; i < arraysize(kSuggestionValues); ++i) { |
| EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value()); |
| } |
| |
| // -1 is for duplicate entry. |
| for (; i < candidates.size(); ++i) { |
| size_t index_in_prediction = i - arraysize(kSuggestionValues); |
| if (index_in_prediction >= kDupulicationIndex) { |
| ++index_in_prediction; |
| } |
| EXPECT_EQ(kPredictionValues[index_in_prediction], |
| candidates.candidate(i).value()); |
| } |
| } |
| } |
| |
| TEST_F(SessionConverterTest, AppendCandidateList) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| SetState(SessionConverterInterface::CONVERSION, &converter); |
| OperationPreferences operation_preferences = |
| GetOperationPreferences(converter); |
| operation_preferences.use_cascading_window = true; |
| converter.SetOperationPreferences(operation_preferences); |
| Segments segments; |
| |
| { |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| |
| SetSegments(segments, &converter); |
| AppendCandidateList(&converter); |
| const CandidateList &candidate_list = GetCandidateList(converter); |
| // 3 == hiragana cand, katakana cand and sub candidate list. |
| EXPECT_EQ(3, candidate_list.size()); |
| EXPECT_TRUE(candidate_list.focused()); |
| size_t sub_cand_list_count = 0; |
| for (size_t i = 0; i < candidate_list.size(); ++i) { |
| if (candidate_list.candidate(i).IsSubcandidateList()) { |
| ++sub_cand_list_count; |
| } |
| } |
| // Sub candidate list for T13N. |
| EXPECT_EQ(1, sub_cand_list_count); |
| } |
| { |
| Segment *segment = segments.mutable_conversion_segment(0); |
| Segment::Candidate *candidate = segment->add_candidate(); |
| // "あいうえお_2" |
| candidate->value = |
| "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a_2"; |
| // New meta candidates. |
| // They should be ignored. |
| vector<Segment::Candidate> *meta_candidates = |
| segment->mutable_meta_candidates(); |
| meta_candidates->clear(); |
| meta_candidates->resize(1); |
| meta_candidates->at(0).Init(); |
| meta_candidates->at(0).value = "t13nValue"; |
| meta_candidates->at(0).content_value = "t13nValue"; |
| meta_candidates->at(0).content_key = segment->key(); |
| |
| SetSegments(segments, &converter); |
| AppendCandidateList(&converter); |
| const CandidateList &candidate_list = GetCandidateList(converter); |
| // 4 == hiragana cand, katakana cand, hiragana cand2 |
| // and sub candidate list. |
| EXPECT_EQ(4, candidate_list.size()); |
| EXPECT_TRUE(candidate_list.focused()); |
| size_t sub_cand_list_count = 0; |
| set<int> id_set; |
| for (size_t i = 0; i < candidate_list.size(); ++i) { |
| if (candidate_list.candidate(i).IsSubcandidateList()) { |
| ++sub_cand_list_count; |
| } else { |
| // No duplicate ids are expected. |
| int id = candidate_list.candidate(i).id(); |
| // EXPECT_EQ(iterator, iterator) might cause compile error in specific |
| // environment. |
| EXPECT_TRUE(id_set.end() == id_set.find(id)); |
| id_set.insert(id); |
| } |
| } |
| // Sub candidate list shouldn't be duplicated. |
| EXPECT_EQ(1, sub_cand_list_count); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, AppendCandidateListForRequestTypes) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| SetState(SessionConverterInterface::SUGGESTION, &converter); |
| Segments segments; |
| |
| { |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| segments.set_request_type(Segments::SUGGESTION); |
| SetSegments(segments, &converter); |
| AppendCandidateList(&converter); |
| const CandidateList &candidate_list = GetCandidateList(converter); |
| EXPECT_FALSE(candidate_list.focused()); |
| } |
| |
| segments.Clear(); |
| { |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| segments.set_request_type(Segments::PARTIAL_SUGGESTION); |
| SetSegments(segments, &converter); |
| AppendCandidateList(&converter); |
| const CandidateList &candidate_list = GetCandidateList(converter); |
| EXPECT_FALSE(candidate_list.focused()); |
| } |
| |
| segments.Clear(); |
| { |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| segments.set_request_type(Segments::PARTIAL_PREDICTION); |
| SetSegments(segments, &converter); |
| AppendCandidateList(&converter); |
| const CandidateList &candidate_list = GetCandidateList(converter); |
| EXPECT_FALSE(candidate_list.focused()); |
| } |
| } |
| |
| TEST_F(SessionConverterTest, ReloadConfig) { |
| SessionConverter converter(convertermock_.get(), &default_request_); |
| Segments segments; |
| SetAiueo(&segments); |
| FillT13Ns(&segments, composer_.get()); |
| convertermock_->SetStartConversionForRequest(&segments, true); |
| |
| composer_->InsertCharacterPreedit("aiueo"); |
| EXPECT_TRUE(converter.Convert(*composer_)); |
| converter.SetCandidateListVisible(true); |
| |
| { // Set OperationPreferences |
| const char kShortcut123456789[] = "123456789"; |
| OperationPreferences preferences; |
| preferences.use_cascading_window = false; |
| preferences.candidate_shortcuts = kShortcut123456789; |
| converter.SetOperationPreferences(preferences); |
| EXPECT_TRUE(IsCandidateListVisible(converter)); |
| } |
| { // Check the config update |
| commands::Output output; |
| converter.FillOutput(*composer_, &output); |
|