1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 // Tests for session converter.
31 //
32 // Note that we have a lot of tests which assume that the converter fills
33 // T13Ns. If you want to add test case related to T13Ns, please make sure
34 // you set T13Ns to the result for a mock converter.
35 
36 #include "session/session_converter.h"
37 
38 #include <memory>
39 #include <set>
40 #include <string>
41 #include <vector>
42 
43 #include "base/logging.h"
44 #include "base/system_util.h"
45 #include "base/util.h"
46 #include "composer/composer.h"
47 #include "composer/table.h"
48 #include "config/config_handler.h"
49 #include "converter/converter_mock.h"
50 #include "converter/segments.h"
51 #include "data_manager/testing/mock_data_manager.h"
52 #include "protocol/candidates.pb.h"
53 #include "protocol/commands.pb.h"
54 #include "protocol/config.pb.h"
55 #include "session/internal/candidate_list.h"
56 #include "session/internal/keymap.h"
57 #include "session/request_test_util.h"
58 #include "testing/base/public/googletest.h"
59 #include "testing/base/public/gunit.h"
60 #include "testing/base/public/testing_util.h"
61 #include "transliteration/transliteration.h"
62 #include "usage_stats/usage_stats.h"
63 #include "usage_stats/usage_stats_testing_util.h"
64 
65 namespace mozc {
66 namespace session {
67 
68 using ::mozc::commands::Context;
69 using ::mozc::commands::Request;
70 using ::mozc::commands::RequestForUnitTest;
71 using ::mozc::config::Config;
72 
73 static const char kChars_Aiueo[] = "あいうえお";
74 static const char kChars_Mo[] = "も";
75 static const char kChars_Mozuku[] = "もずく";
76 static const char kChars_Mozukusu[] = "もずくす";
77 static const char kChars_Momonga[] = "ももんが";
78 
79 class SessionConverterTest : public ::testing::Test {
80  protected:
81   // Workaround for C2512 error (no default appropriate constructor) on MSVS.
SessionConverterTest()82   SessionConverterTest() {}
~SessionConverterTest()83   ~SessionConverterTest() override {}
84 
SetUp()85   void SetUp() override {
86     convertermock_.reset(new ConverterMock());
87     SystemUtil::SetUserProfileDirectory(FLAGS_test_tmpdir);
88     mozc::usage_stats::UsageStats::ClearAllStatsForTest();
89 
90     config_.reset(new Config);
91     config_->set_use_cascading_window(true);
92     request_.reset(new Request);
93 
94     table_.reset(new composer::Table);
95     table_->InitializeWithRequestAndConfig(*request_, *config_,
96                                            mock_data_manager_);
97     composer_.reset(
98         new composer::Composer(table_.get(), request_.get(), config_.get()));
99   }
100 
TearDown()101   void TearDown() override {
102     table_.reset();
103     composer_.reset();
104 
105     mozc::usage_stats::UsageStats::ClearAllStatsForTest();
106   }
107 
GetSegments(const SessionConverter & converter,Segments * dest)108   static void GetSegments(const SessionConverter &converter, Segments *dest) {
109     CHECK(dest);
110     dest->CopyFrom(*converter.segments_.get());
111   }
112 
SetSegments(const Segments & src,SessionConverter * converter)113   static void SetSegments(const Segments &src, SessionConverter *converter) {
114     CHECK(converter);
115     converter->segments_->CopyFrom(src);
116   }
117 
GetResult(const SessionConverter & converter)118   static const commands::Result &GetResult(const SessionConverter &converter) {
119     return *converter.result_;
120   }
121 
GetCandidateList(const SessionConverter & converter)122   static const CandidateList &GetCandidateList(
123       const SessionConverter &converter) {
124     return *converter.candidate_list_;
125   }
126 
GetState(const SessionConverter & converter)127   static SessionConverterInterface::State GetState(
128       const SessionConverter &converter) {
129     return converter.state_;
130   }
131 
SetState(SessionConverterInterface::State state,SessionConverter * converter)132   static void SetState(SessionConverterInterface::State state,
133                        SessionConverter *converter) {
134     converter->state_ = state;
135   }
136 
GetSegmentIndex(const SessionConverter & converter)137   static size_t GetSegmentIndex(const SessionConverter &converter) {
138     return converter.segment_index_;
139   }
140 
IsCandidateListVisible(const SessionConverter & converter)141   static bool IsCandidateListVisible(const SessionConverter &converter) {
142     return converter.candidate_list_visible_;
143   }
144 
GetRequest(const SessionConverter & converter)145   static const commands::Request &GetRequest(
146       const SessionConverter &converter) {
147     return *converter.request_;
148   }
149 
GetPreedit(const SessionConverter & converter,size_t index,size_t size,string * conversion)150   static void GetPreedit(const SessionConverter &converter, size_t index,
151                          size_t size, string *conversion) {
152     converter.GetPreedit(index, size, conversion);
153   }
154 
GetConversion(const SessionConverter & converter,size_t index,size_t size,string * conversion)155   static void GetConversion(const SessionConverter &converter, size_t index,
156                             size_t size, string *conversion) {
157     converter.GetConversion(index, size, conversion);
158   }
159 
AppendCandidateList(SessionConverter * converter)160   static void AppendCandidateList(SessionConverter *converter) {
161     converter->AppendCandidateList();
162   }
163 
164   // set result for "あいうえお"
SetAiueo(Segments * segments)165   static void SetAiueo(Segments *segments) {
166     segments->Clear();
167     Segment *segment;
168     Segment::Candidate *candidate;
169 
170     segment = segments->add_segment();
171     segment->set_key("あいうえお");
172     candidate = segment->add_candidate();
173     candidate->key = "あいうえお";
174     candidate->value = candidate->key;
175     candidate = segment->add_candidate();
176     candidate->key = "あいうえお";
177     candidate->value = "アイウエオ";
178   }
179 
180   // set result for "かまぼこのいんぼう"
SetKamaboko(Segments * segments)181   static void SetKamaboko(Segments *segments) {
182     Segment *segment;
183     Segment::Candidate *candidate;
184 
185     segments->Clear();
186     segment = segments->add_segment();
187 
188     segment->set_key("かまぼこの");
189     candidate = segment->add_candidate();
190     candidate->value = "かまぼこの";
191     candidate = segment->add_candidate();
192     candidate->value = "カマボコの";
193     segment = segments->add_segment();
194     segment->set_key("いんぼう");
195     candidate = segment->add_candidate();
196     candidate->value = "陰謀";
197     candidate = segment->add_candidate();
198     candidate->value = "印房";
199 
200     // Set dummy T13Ns
201     std::vector<Segment::Candidate> *meta_candidates =
202         segment->mutable_meta_candidates();
203     meta_candidates->resize(transliteration::NUM_T13N_TYPES);
204     for (size_t i = 0; i < transliteration::NUM_T13N_TYPES; ++i) {
205       meta_candidates->at(i).Init();
206       meta_candidates->at(i).value = segment->key();
207       meta_candidates->at(i).content_value = segment->key();
208       meta_candidates->at(i).content_key = segment->key();
209     }
210   }
211 
212   // set T13N candidates to segments using composer
FillT13Ns(Segments * segments,const composer::Composer * composer)213   static void FillT13Ns(Segments *segments,
214                         const composer::Composer *composer) {
215     size_t composition_pos = 0;
216     for (size_t i = 0; i < segments->conversion_segments_size(); ++i) {
217       Segment *segment = segments->mutable_conversion_segment(i);
218       CHECK(segment);
219       const size_t composition_len = Util::CharsLen(segment->key());
220       std::vector<string> t13ns;
221       composer->GetSubTransliterations(
222           composition_pos, composition_len, &t13ns);
223       std::vector<Segment::Candidate> *meta_candidates =
224           segment->mutable_meta_candidates();
225       meta_candidates->resize(transliteration::NUM_T13N_TYPES);
226       for (size_t j = 0; j < transliteration::NUM_T13N_TYPES; ++j) {
227         meta_candidates->at(j).Init();
228         meta_candidates->at(j).value = t13ns[j];
229         meta_candidates->at(j).content_value = t13ns[j];
230         meta_candidates->at(j).content_key = segment->key();
231       }
232       composition_pos += composition_len;
233     }
234   }
235 
236   // set result for "like"
InitConverterWithLike(Segments * segments)237   void InitConverterWithLike(Segments *segments) {
238     composer_->InsertCharacterKeyAndPreedit("li", "ぃ");
239     composer_->InsertCharacterKeyAndPreedit("ke", "け");
240 
241     Segment *segment;
242     Segment::Candidate *candidate;
243 
244     segments->Clear();
245     segment = segments->add_segment();
246 
247     segment->set_key("ぃ");
248     candidate = segment->add_candidate();
249     candidate->value = "ぃ";
250 
251     candidate = segment->add_candidate();
252     candidate->value = "ィ";
253 
254     segment = segments->add_segment();
255     segment->set_key("け");
256     candidate = segment->add_candidate();
257     candidate->value = "家";
258     candidate = segment->add_candidate();
259     candidate->value = "け";
260 
261     FillT13Ns(segments, composer_.get());
262     convertermock_->SetStartConversionForRequest(segments, true);
263   }
264 
InsertASCIISequence(const string text,composer::Composer * composer)265   static void InsertASCIISequence(const string text,
266                                   composer::Composer *composer) {
267     for (size_t i = 0; i < text.size(); ++i) {
268       commands::KeyEvent key;
269       key.set_key_code(text[i]);
270       composer->InsertCharacterKeyEvent(key);
271     }
272   }
273 
ExpectSameSessionConverter(const SessionConverter & lhs,const SessionConverter & rhs)274   static void ExpectSameSessionConverter(const SessionConverter &lhs,
275                                          const SessionConverter &rhs) {
276     EXPECT_EQ(lhs.IsActive(), rhs.IsActive());
277     EXPECT_EQ(IsCandidateListVisible(lhs), IsCandidateListVisible(rhs));
278     EXPECT_EQ(GetSegmentIndex(lhs), GetSegmentIndex(rhs));
279 
280     EXPECT_EQ(lhs.conversion_preferences().use_history,
281               rhs.conversion_preferences().use_history);
282     EXPECT_EQ(lhs.conversion_preferences().max_history_size,
283               rhs.conversion_preferences().max_history_size);
284     EXPECT_EQ(IsCandidateListVisible(lhs),
285               IsCandidateListVisible(rhs));
286 
287     Segments segments_lhs, segments_rhs;
288     GetSegments(lhs, &segments_lhs);
289     GetSegments(rhs, &segments_rhs);
290     EXPECT_EQ(segments_lhs.segments_size(),
291               segments_rhs.segments_size());
292     for (size_t i = 0; i < segments_lhs.segments_size(); ++i) {
293       Segment segment_lhs, segment_rhs;
294       segment_lhs.CopyFrom(segments_lhs.segment(i));
295       segment_rhs.CopyFrom(segments_rhs.segment(i));
296       EXPECT_EQ(segment_lhs.key(), segment_rhs.key()) << " i=" << i;
297       EXPECT_EQ(segment_lhs.segment_type(),
298                 segment_rhs.segment_type()) << " i=" << i;
299       EXPECT_EQ(segment_lhs.candidates_size(), segment_rhs.candidates_size());
300     }
301 
302     const CandidateList &candidate_list_lhs = GetCandidateList(lhs);
303     const CandidateList &candidate_list_rhs = GetCandidateList(rhs);
304     EXPECT_EQ(candidate_list_lhs.name(), candidate_list_rhs.name());
305     EXPECT_EQ(candidate_list_lhs.page_size(), candidate_list_rhs.page_size());
306     EXPECT_EQ(candidate_list_lhs.size(), candidate_list_rhs.size());
307     EXPECT_EQ(candidate_list_lhs.last_index(), candidate_list_rhs.last_index());
308     EXPECT_EQ(candidate_list_lhs.focused_id(), candidate_list_rhs.focused_id());
309     EXPECT_EQ(candidate_list_lhs.focused_index(),
310               candidate_list_rhs.focused_index());
311     EXPECT_EQ(candidate_list_lhs.focused(), candidate_list_rhs.focused());
312 
313     for (int i = 0; i < candidate_list_lhs.size(); ++i) {
314       const Candidate &candidate_lhs = candidate_list_lhs.candidate(i);
315       const Candidate &candidate_rhs = candidate_list_rhs.candidate(i);
316       EXPECT_EQ(candidate_lhs.id(), candidate_rhs.id());
317       EXPECT_EQ(candidate_lhs.attributes(), candidate_rhs.attributes());
318       EXPECT_EQ(candidate_lhs.IsSubcandidateList(),
319                 candidate_rhs.IsSubcandidateList());
320       if (candidate_lhs.IsSubcandidateList()) {
321         EXPECT_EQ(candidate_lhs.subcandidate_list().size(),
322                   candidate_rhs.subcandidate_list().size());
323       }
324     }
325 
326     EXPECT_PROTO_EQ(GetResult(lhs), GetResult(rhs));
327     EXPECT_PROTO_EQ(GetRequest(lhs), GetRequest(rhs));
328   }
329 
ExpectSelectedCandidateIndices(const char *,const char *,const SessionConverter & converter,const std::vector<int> & expected)330   static ::testing::AssertionResult ExpectSelectedCandidateIndices(
331       const char *, const char *,
332       const SessionConverter &converter, const std::vector<int> &expected) {
333     const std::vector<int> &actual = converter.selected_candidate_indices_;
334 
335     if (expected.size() != actual.size()) {
336       return ::testing::AssertionFailure()
337           << "Indices size mismatch.\n"
338           << "Expected: " << expected.size() << "\n"
339           << "Actual:   " << actual.size();
340     }
341 
342     for (size_t i = 0; i < expected.size(); ++i) {
343       if (expected[i] != actual[i]) {
344         return ::testing::AssertionFailure()
345             << "Index mismatch.\n"
346             << "Expected: " << expected[i] << "\n"
347             << "Actual:   " << actual[i];
348       }
349     }
350 
351     return ::testing::AssertionSuccess();
352   }
353 
SetCommandCandidate(Segments * segments,int segment_index,int canidate_index,Segment::Candidate::Command command)354   static void SetCommandCandidate(
355       Segments *segments, int segment_index, int canidate_index,
356       Segment::Candidate::Command command) {
357     segments->mutable_conversion_segment(segment_index)
358         ->mutable_candidate(canidate_index)->attributes
359             |= Segment::Candidate::COMMAND_CANDIDATE;
360     segments->mutable_conversion_segment(segment_index)
361         ->mutable_candidate(canidate_index)->command = command;
362   }
363 
364   std::unique_ptr<ConverterMock> convertermock_;
365 
366   std::unique_ptr<composer::Composer> composer_;
367   std::unique_ptr<composer::Table> table_;
368   std::unique_ptr<Request> request_;
369   std::unique_ptr<Config> config_;
370   mozc::usage_stats::scoped_usage_stats_enabler usage_stats_enabler_;
371 
372  private:
373   const testing::MockDataManager mock_data_manager_;
374 };
375 
376 #define EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, indices) \
377   EXPECT_PRED_FORMAT2(ExpectSelectedCandidateIndices, converter, indices);
378 
TEST_F(SessionConverterTest,Convert)379 TEST_F(SessionConverterTest, Convert) {
380   SessionConverter converter(
381       convertermock_.get(), request_.get(), config_.get());
382   Segments segments;
383   SetAiueo(&segments);
384   FillT13Ns(&segments, composer_.get());
385   convertermock_->SetStartConversionForRequest(&segments, true);
386   std::vector<int> expected_indices;
387   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
388 
389   composer_->InsertCharacterPreedit(kChars_Aiueo);
390   EXPECT_TRUE(converter.Convert(*composer_));
391   ASSERT_TRUE(converter.IsActive());
392   expected_indices.push_back(0);
393   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
394 
395   commands::Output output;
396   converter.FillOutput(*composer_, &output);
397   EXPECT_FALSE(output.has_result());
398   EXPECT_TRUE(output.has_preedit());
399   EXPECT_FALSE(output.has_candidates());
400 
401   const commands::Preedit &conversion = output.preedit();
402   EXPECT_EQ(1, conversion.segment_size());
403   EXPECT_EQ(commands::Preedit::Segment::HIGHLIGHT,
404             conversion.segment(0).annotation());
405   EXPECT_EQ(kChars_Aiueo, conversion.segment(0).value());
406   EXPECT_EQ(kChars_Aiueo, conversion.segment(0).key());
407 
408   // Converter should be active before submittion
409   EXPECT_TRUE(converter.IsActive());
410   EXPECT_FALSE(IsCandidateListVisible(converter));
411 
412   converter.Commit(*composer_, Context::default_instance());
413   composer_->Reset();
414   output.Clear();
415   converter.FillOutput(*composer_, &output);
416   EXPECT_TRUE(output.has_result());
417   EXPECT_FALSE(output.has_preedit());
418   EXPECT_FALSE(output.has_candidates());
419   expected_indices.clear();
420   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
421 
422   const commands::Result &result = output.result();
423   EXPECT_EQ(kChars_Aiueo, result.value());
424   EXPECT_EQ(kChars_Aiueo, result.key());
425 
426   // Converter should be inactive after submittion
427   EXPECT_FALSE(converter.IsActive());
428   EXPECT_FALSE(IsCandidateListVisible(converter));
429 
430   EXPECT_COUNT_STATS("Commit", 1);
431   EXPECT_COUNT_STATS("CommitFromConversion", 1);
432   EXPECT_COUNT_STATS("ConversionCandidates0", 1);
433 }
434 
TEST_F(SessionConverterTest,ConvertWithSpellingCorrection)435 TEST_F(SessionConverterTest, ConvertWithSpellingCorrection) {
436   SessionConverter converter(
437       convertermock_.get(), request_.get(), config_.get());
438   Segments segments;
439   SetAiueo(&segments);
440   FillT13Ns(&segments, composer_.get());
441   segments.mutable_conversion_segment(0)->mutable_candidate(0)->attributes |=
442       Segment::Candidate::SPELLING_CORRECTION;
443   convertermock_->SetStartConversionForRequest(&segments, true);
444 
445   composer_->InsertCharacterPreedit(kChars_Aiueo);
446   EXPECT_TRUE(converter.Convert(*composer_));
447   ASSERT_TRUE(converter.IsActive());
448   EXPECT_TRUE(IsCandidateListVisible(converter));
449 }
450 
TEST_F(SessionConverterTest,ConvertToTransliteration)451 TEST_F(SessionConverterTest, ConvertToTransliteration) {
452   SessionConverter converter(
453       convertermock_.get(), request_.get(), config_.get());
454   Segments segments;
455   SetAiueo(&segments);
456 
457   composer_->InsertCharacterKeyAndPreedit("aiueo", kChars_Aiueo);
458   FillT13Ns(&segments, composer_.get());
459   convertermock_->SetStartConversionForRequest(&segments, true);
460 
461   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
462                                                  transliteration::HALF_ASCII));
463   std::vector<int> expected_indices;
464   expected_indices.push_back(0);
465   {  // Check the conversion #1
466     commands::Output output;
467     converter.FillOutput(*composer_, &output);
468     EXPECT_FALSE(output.has_result());
469     EXPECT_TRUE(output.has_preedit());
470     EXPECT_FALSE(output.has_candidates());
471 
472     const commands::Preedit &conversion = output.preedit();
473     EXPECT_EQ(1, conversion.segment_size());
474     EXPECT_EQ("aiueo", conversion.segment(0).value());
475     EXPECT_FALSE(IsCandidateListVisible(converter));
476     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
477   }
478 
479   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
480                                                  transliteration::HALF_ASCII));
481   {  // Check the conversion #2
482     commands::Output output;
483     converter.FillOutput(*composer_, &output);
484     EXPECT_FALSE(output.has_result());
485     EXPECT_TRUE(output.has_preedit());
486     EXPECT_FALSE(output.has_candidates());
487 
488     const commands::Preedit &conversion = output.preedit();
489     EXPECT_EQ(1, conversion.segment_size());
490     EXPECT_EQ("AIUEO", conversion.segment(0).value());
491     EXPECT_FALSE(IsCandidateListVisible(converter));
492     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
493   }
494 
495   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
496                                                  transliteration::FULL_ASCII));
497   {  // Check the conversion #3
498     commands::Output output;
499     converter.FillOutput(*composer_, &output);
500     EXPECT_FALSE(output.has_result());
501     EXPECT_TRUE(output.has_preedit());
502     EXPECT_FALSE(output.has_candidates());
503 
504     const commands::Preedit &conversion = output.preedit();
505     EXPECT_EQ(1, conversion.segment_size());
506     EXPECT_EQ("AIUEO", conversion.segment(0).value());
507     EXPECT_FALSE(IsCandidateListVisible(converter));
508     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
509   }
510 
511   converter.Commit(*composer_, Context::default_instance());
512 
513   EXPECT_COUNT_STATS("Commit", 1);
514   EXPECT_COUNT_STATS("CommitFromConversion", 1);
515   EXPECT_COUNT_STATS("ConversionCandidates0", 1);
516 }
517 
TEST_F(SessionConverterTest,ConvertToTransliterationWithMultipleSegments)518 TEST_F(SessionConverterTest, ConvertToTransliterationWithMultipleSegments) {
519   Segments segments;
520   InitConverterWithLike(&segments);
521   SessionConverter converter(
522       convertermock_.get(), request_.get(), config_.get());
523 
524   // Convert
525   EXPECT_TRUE(converter.Convert(*composer_));
526   std::vector<int> expected_indices;
527   expected_indices.push_back(0);
528   expected_indices.push_back(0);
529   {  // Check the conversion #1
530     commands::Output output;
531     converter.FillOutput(*composer_, &output);
532     EXPECT_FALSE(output.has_result());
533     EXPECT_TRUE(output.has_preedit());
534     EXPECT_FALSE(output.has_candidates());
535 
536     const commands::Preedit &conversion = output.preedit();
537     EXPECT_EQ(2, conversion.segment_size());
538     EXPECT_EQ("ぃ", conversion.segment(0).value());
539     EXPECT_EQ("家", conversion.segment(1).value());
540     EXPECT_FALSE(IsCandidateListVisible(converter));
541     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
542   }
543 
544   // Convert to half-width alphanumeric.
545   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
546                                                  transliteration::HALF_ASCII));
547   {  // Check the conversion #2
548     commands::Output output;
549     converter.FillOutput(*composer_, &output);
550     EXPECT_FALSE(output.has_result());
551     EXPECT_TRUE(output.has_preedit());
552     EXPECT_FALSE(output.has_candidates());
553 
554     const commands::Preedit &conversion = output.preedit();
555     EXPECT_EQ(2, conversion.segment_size());
556     EXPECT_EQ("li", conversion.segment(0).value());
557     EXPECT_FALSE(IsCandidateListVisible(converter));
558     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
559   }
560 }
561 
TEST_F(SessionConverterTest,ConvertToTransliterationWithoutCascadigWindow)562 TEST_F(SessionConverterTest, ConvertToTransliterationWithoutCascadigWindow) {
563   SessionConverter converter(
564       convertermock_.get(), request_.get(), config_.get());
565   Segments segments;
566   {
567     Segment *segment;
568     Segment::Candidate *candidate;
569     segment = segments.add_segment();
570     segment->set_key("dvd");
571     candidate = segment->add_candidate();
572     candidate->value = "dvd";
573     candidate = segment->add_candidate();
574     candidate->value = "DVD";
575   }
576   {  // Set OperationPreferences
577     converter.set_use_cascading_window(false);
578     converter.set_selection_shortcut(config::Config::NO_SHORTCUT);
579   }
580 
581   composer_->InsertCharacterKeyAndPreedit("dvd", "dvd");
582   FillT13Ns(&segments, composer_.get());
583   convertermock_->SetStartConversionForRequest(&segments, true);
584   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
585                                                  transliteration::FULL_ASCII));
586   std::vector<int> expected_indices;
587   expected_indices.push_back(0);
588   {  // Check the conversion #1
589     commands::Output output;
590     converter.FillOutput(*composer_, &output);
591     EXPECT_FALSE(output.has_result());
592     EXPECT_TRUE(output.has_preedit());
593     EXPECT_FALSE(output.has_candidates());
594 
595     const commands::Preedit &conversion = output.preedit();
596     EXPECT_EQ(1, conversion.segment_size());
597     EXPECT_EQ("dvd", conversion.segment(0).value());
598     EXPECT_FALSE(IsCandidateListVisible(converter));
599     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
600   }
601 
602   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
603                                                  transliteration::FULL_ASCII));
604   {  // Check the conversion #2
605     commands::Output output;
606     converter.FillOutput(*composer_, &output);
607     EXPECT_FALSE(output.has_result());
608     EXPECT_TRUE(output.has_preedit());
609     EXPECT_FALSE(output.has_candidates());
610 
611     const commands::Preedit &conversion = output.preedit();
612     EXPECT_EQ(1, conversion.segment_size());
613     EXPECT_EQ("DVD", conversion.segment(0).value());
614     EXPECT_FALSE(IsCandidateListVisible(converter));
615     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
616   }
617 
618   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
619                                                  transliteration::FULL_ASCII));
620   {  // Check the conversion #3
621     commands::Output output;
622     converter.FillOutput(*composer_, &output);
623     EXPECT_FALSE(output.has_result());
624     EXPECT_TRUE(output.has_preedit());
625     EXPECT_FALSE(output.has_candidates());
626 
627     const commands::Preedit &conversion = output.preedit();
628     EXPECT_EQ(1, conversion.segment_size());
629     EXPECT_EQ("Dvd", conversion.segment(0).value());
630     EXPECT_FALSE(IsCandidateListVisible(converter));
631     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
632   }
633 }
634 
TEST_F(SessionConverterTest,MultiSegmentsConversion)635 TEST_F(SessionConverterTest, MultiSegmentsConversion) {
636   SessionConverter converter(
637       convertermock_.get(), request_.get(), config_.get());
638   Segments segments;
639   SetKamaboko(&segments);
640   const string kKamabokono = "かまぼこの";
641   const string kInbou = "いんぼう";
642 
643   // Test for conversion
644   composer_->InsertCharacterPreedit(kKamabokono + kInbou);
645   FillT13Ns(&segments, composer_.get());
646   convertermock_->SetStartConversionForRequest(&segments, true);
647   EXPECT_TRUE(converter.Convert(*composer_));
648   std::vector<int> expected_indices;
649   expected_indices.push_back(0);
650   expected_indices.push_back(0);
651   {
652     EXPECT_EQ(0, GetSegmentIndex(converter));
653     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
654 
655     commands::Output output;
656     converter.FillOutput(*composer_, &output);
657     EXPECT_FALSE(output.has_result());
658     EXPECT_TRUE(output.has_preedit());
659     EXPECT_FALSE(output.has_candidates());
660 
661     const commands::Preedit &conversion = output.preedit();
662     EXPECT_EQ(2, conversion.segment_size());
663     EXPECT_EQ(commands::Preedit::Segment::HIGHLIGHT,
664               conversion.segment(0).annotation());
665     EXPECT_EQ(kKamabokono, conversion.segment(0).key());
666     EXPECT_EQ(kKamabokono, conversion.segment(0).value());
667 
668     EXPECT_EQ(commands::Preedit::Segment::UNDERLINE,
669               conversion.segment(1).annotation());
670     EXPECT_EQ(kInbou, conversion.segment(1).key());
671     EXPECT_EQ("陰謀", conversion.segment(1).value());
672   }
673 
674   // Test for candidates [CandidateNext]
675   EXPECT_FALSE(IsCandidateListVisible(converter));
676   converter.CandidateNext(*composer_);
677   expected_indices[0] += 1;
678   {
679     EXPECT_TRUE(IsCandidateListVisible(converter));
680     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
681   }
682 
683   // Test for candidates [CandidatePrev]
684   converter.CandidatePrev();
685   expected_indices[0] -= 1;
686   {
687     EXPECT_TRUE(IsCandidateListVisible(converter));
688     EXPECT_EQ(0, GetSegmentIndex(converter));
689     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
690     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
691 
692     commands::Output output;
693     converter.FillOutput(*composer_, &output);
694     EXPECT_FALSE(output.has_result());
695     EXPECT_TRUE(output.has_preedit());
696     EXPECT_TRUE(output.has_candidates());
697 
698     const commands::Candidates &candidates = output.candidates();
699     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
700     EXPECT_EQ(0, candidates.position());
701     EXPECT_EQ(kKamabokono, candidates.candidate(0).value());
702     EXPECT_EQ("カマボコの", candidates.candidate(1).value());
703     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
704   }
705 
706   // Test for segment motion. [SegmentFocusRight]
707   converter.SegmentFocusRight();
708   {
709     EXPECT_EQ(1, GetSegmentIndex(converter));
710     EXPECT_FALSE(IsCandidateListVisible(converter));
711     converter.SetCandidateListVisible(true);
712 
713     commands::Output output;
714     converter.FillOutput(*composer_, &output);
715     EXPECT_FALSE(output.has_result());
716     EXPECT_TRUE(output.has_preedit());
717     EXPECT_TRUE(output.has_candidates());
718     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
719 
720     const commands::Candidates &candidates = output.candidates();
721     EXPECT_EQ(0, candidates.focused_index());
722     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
723     EXPECT_EQ(5, candidates.position());
724     EXPECT_EQ("陰謀", candidates.candidate(0).value());
725     EXPECT_EQ("印房", candidates.candidate(1).value());
726     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
727   }
728 
729   // Test for segment motion. [SegmentFocusLeft]
730   converter.SegmentFocusLeft();
731   {
732     EXPECT_EQ(0, GetSegmentIndex(converter));
733     EXPECT_FALSE(IsCandidateListVisible(converter));
734     converter.SetCandidateListVisible(true);
735     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
736 
737     commands::Output output;
738     converter.FillOutput(*composer_, &output);
739     EXPECT_FALSE(output.has_result());
740     EXPECT_TRUE(output.has_preedit());
741     EXPECT_TRUE(output.has_candidates());
742 
743     const commands::Candidates &candidates = output.candidates();
744     EXPECT_EQ(0, candidates.focused_index());
745     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
746     EXPECT_EQ(0, candidates.position());
747     EXPECT_EQ(kKamabokono, candidates.candidate(0).value());
748     EXPECT_EQ("カマボコの", candidates.candidate(1).value());
749     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
750   }
751 
752   // Test for segment motion. [SegmentFocusLeft] at the head of segments.
753   // http://b/2990134
754   // Focus changing at the tail of segments to right,
755   // and at the head of segments to left, should work.
756   converter.SegmentFocusLeft();
757   {
758     EXPECT_EQ(1, GetSegmentIndex(converter));
759     EXPECT_FALSE(IsCandidateListVisible(converter));
760     converter.SetCandidateListVisible(true);
761     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
762 
763     commands::Output output;
764     converter.FillOutput(*composer_, &output);
765     EXPECT_FALSE(output.has_result());
766     EXPECT_TRUE(output.has_preedit());
767     EXPECT_TRUE(output.has_candidates());
768 
769     const commands::Candidates &candidates = output.candidates();
770     EXPECT_EQ(0, candidates.focused_index());
771     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
772     EXPECT_EQ(5, candidates.position());
773     EXPECT_EQ("陰謀", candidates.candidate(0).value());
774     EXPECT_EQ("印房", candidates.candidate(1).value());
775     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
776   }
777 
778   // Test for segment motion. [SegmentFocusRight] at the tail of segments.
779   // http://b/2990134
780   // Focus changing at the tail of segments to right,
781   // and at the head of segments to left, should work.
782   converter.SegmentFocusRight();
783   {
784     EXPECT_FALSE(IsCandidateListVisible(converter));
785     converter.SetCandidateListVisible(true);
786     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
787 
788     commands::Output output;
789     EXPECT_EQ(0, GetSegmentIndex(converter));
790     converter.FillOutput(*composer_, &output);
791     EXPECT_FALSE(output.has_result());
792     EXPECT_TRUE(output.has_preedit());
793     EXPECT_TRUE(output.has_candidates());
794 
795     const commands::Candidates &candidates = output.candidates();
796     EXPECT_EQ(0, candidates.focused_index());
797     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
798     EXPECT_EQ(0, candidates.position());
799     EXPECT_EQ(kKamabokono, candidates.candidate(0).value());
800     EXPECT_EQ("カマボコの", candidates.candidate(1).value());
801     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
802   }
803 
804   // Test for candidate motion. [CandidateNext]
805   converter.SegmentFocusRight();  // Focus to the last segment.
806   EXPECT_EQ(1, GetSegmentIndex(converter));
807   converter.CandidateNext(*composer_);
808   expected_indices[1] += 1;
809   {
810     EXPECT_TRUE(IsCandidateListVisible(converter));
811     commands::Output output;
812     converter.FillOutput(*composer_, &output);
813     EXPECT_FALSE(output.has_result());
814     EXPECT_TRUE(output.has_preedit());
815     EXPECT_TRUE(output.has_candidates());
816     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
817 
818     const commands::Candidates &candidates = output.candidates();
819     EXPECT_EQ(1, candidates.focused_index());
820     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
821     EXPECT_EQ(5, candidates.position());
822     EXPECT_EQ("陰謀", candidates.candidate(0).value());
823     EXPECT_EQ("印房", candidates.candidate(1).value());
824     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
825 
826     const commands::Preedit &conversion = output.preedit();
827     EXPECT_EQ(kKamabokono, conversion.segment(0).value());
828     EXPECT_EQ("印房", conversion.segment(1).value());
829   }
830 
831   // Test for segment motion again [SegmentFocusLeftEdge] [SegmentFocusLast]
832   // The positions of "陰謀" and "印房" should be swapped.
833   {
834     Segments fixed_segments;
835     SetKamaboko(&fixed_segments);
836     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
837 
838     ASSERT_EQ("陰謀",
839               fixed_segments.segment(1).candidate(0).value);
840     ASSERT_EQ("印房",
841               fixed_segments.segment(1).candidate(1).value);
842     // swap the values.
843     fixed_segments.mutable_segment(1)->mutable_candidate(0)->value.swap(
844         fixed_segments.mutable_segment(1)->mutable_candidate(1)->value);
845     ASSERT_EQ("印房",
846               fixed_segments.segment(1).candidate(0).value);
847     ASSERT_EQ("陰謀",
848               fixed_segments.segment(1).candidate(1).value);
849     convertermock_->SetCommitSegmentValue(&fixed_segments, true);
850   }
851   converter.SegmentFocusLeftEdge();
852   {
853     EXPECT_EQ(0, GetSegmentIndex(converter));
854     EXPECT_FALSE(IsCandidateListVisible(converter));
855     converter.SegmentFocusLast();
856     EXPECT_EQ(1, GetSegmentIndex(converter));
857     EXPECT_FALSE(IsCandidateListVisible(converter));
858     converter.SetCandidateListVisible(true);
859 
860     commands::Output output;
861     converter.FillOutput(*composer_, &output);
862     EXPECT_FALSE(output.has_result());
863     EXPECT_TRUE(output.has_preedit());
864     EXPECT_TRUE(output.has_candidates());
865 
866     const commands::Candidates &candidates = output.candidates();
867     EXPECT_EQ(0, candidates.focused_index());
868     EXPECT_EQ(3, candidates.size());  // two candidates + one t13n sub list.
869     EXPECT_EQ(5, candidates.position());
870     EXPECT_EQ("印房", candidates.candidate(0).value());
871 
872     EXPECT_EQ("陰謀", candidates.candidate(1).value());
873 
874     EXPECT_EQ("そのほかの文字種", candidates.candidate(2).value());
875 
876     const commands::Preedit &conversion = output.preedit();
877     EXPECT_EQ(kKamabokono, conversion.segment(0).value());
878     EXPECT_EQ("印房", conversion.segment(1).value());
879   }
880 
881   converter.Commit(*composer_, Context::default_instance());
882   expected_indices.clear();
883   {
884     composer_->Reset();
885     EXPECT_FALSE(IsCandidateListVisible(converter));
886     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
887 
888     commands::Output output;
889     converter.FillOutput(*composer_, &output);
890     EXPECT_TRUE(output.has_result());
891     EXPECT_FALSE(output.has_preedit());
892     EXPECT_FALSE(output.has_candidates());
893 
894     const commands::Result &result = output.result();
895     EXPECT_EQ("かまぼこの印房", result.value());
896     EXPECT_EQ("かまぼこのいんぼう", result.key());
897     EXPECT_FALSE(converter.IsActive());
898   }
899 }
900 
TEST_F(SessionConverterTest,Transliterations)901 TEST_F(SessionConverterTest, Transliterations) {
902   SessionConverter converter(
903       convertermock_.get(), request_.get(), config_.get());
904   composer_->InsertCharacterKeyAndPreedit("h", "く");
905   composer_->InsertCharacterKeyAndPreedit("J", "ま");
906 
907   Segments segments;
908   {  // Initialize segments.
909     Segment *segment = segments.add_segment();
910     segment->set_key("くま");
911     segment->add_candidate()->value = "クマー";
912   }
913   FillT13Ns(&segments, composer_.get());
914   convertermock_->SetStartConversionForRequest(&segments, true);
915   EXPECT_TRUE(converter.Convert(*composer_));
916   std::vector<int> expected_indices;
917   expected_indices.push_back(0);
918   EXPECT_FALSE(IsCandidateListVisible(converter));
919   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
920 
921   // Move to the t13n list.
922   converter.CandidateNext(*composer_);
923   expected_indices[0] = -1;
924   EXPECT_TRUE(IsCandidateListVisible(converter));
925   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
926 
927   commands::Output output;
928   converter.FillOutput(*composer_, &output);
929   EXPECT_FALSE(output.has_result());
930   EXPECT_TRUE(output.has_preedit());
931   EXPECT_TRUE(output.has_candidates());
932 
933   const commands::Candidates &candidates = output.candidates();
934   EXPECT_EQ(2, candidates.size());  // one candidate + one t13n sub list.
935   EXPECT_EQ(1, candidates.focused_index());
936   EXPECT_EQ("そのほかの文字種", candidates.candidate(1).value());
937 
938   std::vector<string> t13ns;
939   composer_->GetTransliterations(&t13ns);
940 
941   EXPECT_TRUE(candidates.has_subcandidates());
942   EXPECT_EQ(t13ns.size(), candidates.subcandidates().size());
943   EXPECT_EQ(9, candidates.subcandidates().candidate_size());
944 
945   for (size_t i = 0; i < candidates.subcandidates().candidate_size(); ++i) {
946     EXPECT_EQ(t13ns[i], candidates.subcandidates().candidate(i).value());
947   }
948 }
949 
TEST_F(SessionConverterTest,T13NWithResegmentation)950 TEST_F(SessionConverterTest, T13NWithResegmentation) {
951   SessionConverter converter(
952       convertermock_.get(), request_.get(), config_.get());
953   {
954     Segments segments;
955     Segment *segment = segments.add_segment();
956     Segment::Candidate *candidate;
957     CHECK(segment);
958     segment->set_key("かまぼこの");
959     candidate = segment->add_candidate();
960     CHECK(candidate);
961     candidate->value = "かまぼこの";
962 
963     segment = segments.add_segment();
964     CHECK(segment);
965     segment->set_key("いんぼう");
966     candidate = segment->add_candidate();
967     CHECK(candidate);
968     candidate->value = "いんぼう";
969 
970     InsertASCIISequence("kamabokonoinbou", composer_.get());
971     FillT13Ns(&segments, composer_.get());
972     convertermock_->SetStartConversionForRequest(&segments, true);
973   }
974   EXPECT_TRUE(converter.Convert(*composer_));
975   std::vector<int> expected_indices;
976   expected_indices.push_back(0);
977   expected_indices.push_back(0);
978   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
979 
980   // Test for segment motion. [SegmentFocusRight]
981   converter.SegmentFocusRight();
982   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
983   // Shrink segment
984   {
985     Segments segments;
986     Segment *segment;
987     Segment::Candidate *candidate;
988 
989     segments.Clear();
990     segment = segments.add_segment();
991 
992     segment->set_key("かまぼこの");
993     candidate = segment->add_candidate();
994     candidate->value = "かまぼこの";
995     candidate = segment->add_candidate();
996     candidate->value = "カマボコの";
997 
998     segment = segments.add_segment();
999     segment->set_key("いんぼ");
1000     candidate = segment->add_candidate();
1001     candidate->value = "インボ";
1002 
1003     segment = segments.add_segment();
1004     segment->set_key("う");
1005     candidate = segment->add_candidate();
1006     candidate->value = "ウ";
1007 
1008     FillT13Ns(&segments, composer_.get());
1009     convertermock_->SetResizeSegment1(&segments, true);
1010   }
1011   converter.SegmentWidthShrink(*composer_);
1012   expected_indices.push_back(0);
1013   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1014 
1015   // Convert to half katakana. Expected index should be 0.
1016   converter.ConvertToTransliteration(*composer_,
1017                                      transliteration::HALF_KATAKANA);
1018   expected_indices[0] = 0;
1019   {
1020     commands::Output output;
1021     converter.FillOutput(*composer_, &output);
1022     EXPECT_FALSE(output.has_result());
1023     EXPECT_TRUE(output.has_preedit());
1024     const commands::Preedit &preedit = output.preedit();
1025     EXPECT_EQ(3, preedit.segment_size());
1026     EXPECT_EQ("インボ",
1027               preedit.segment(1).value());
1028     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1029   }
1030 }
1031 
TEST_F(SessionConverterTest,ConvertToHalfWidth)1032 TEST_F(SessionConverterTest, ConvertToHalfWidth) {
1033   SessionConverter converter(
1034       convertermock_.get(), request_.get(), config_.get());
1035   std::vector<int> expected_indices;
1036   composer_->InsertCharacterKeyAndPreedit("a", "あ");
1037   composer_->InsertCharacterKeyAndPreedit("b", "b");
1038   composer_->InsertCharacterKeyAndPreedit("c", "c");
1039 
1040   Segments segments;
1041   {  // Initialize segments.
1042     Segment *segment = segments.add_segment();
1043     segment->set_key("あbc");
1044     segment->add_candidate()->value = "あべし";
1045   }
1046   FillT13Ns(&segments, composer_.get());
1047   convertermock_->SetStartConversionForRequest(&segments, true);
1048   EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_));
1049   expected_indices.push_back(0);
1050   EXPECT_FALSE(IsCandidateListVisible(converter));
1051   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1052 
1053   {  // Make sure the output
1054     commands::Output output;
1055     converter.FillOutput(*composer_, &output);
1056     EXPECT_FALSE(output.has_result());
1057     EXPECT_TRUE(output.has_preedit());
1058     EXPECT_FALSE(output.has_candidates());
1059 
1060     const commands::Preedit &conversion = output.preedit();
1061     EXPECT_EQ(1, conversion.segment_size());
1062     EXPECT_EQ("アbc", conversion.segment(0).value());
1063   }
1064 
1065   // Composition will be transliterated to "abc".
1066   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
1067                                                  transliteration::FULL_ASCII));
1068   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1069   {  // Make sure the output
1070     commands::Output output;
1071     converter.FillOutput(*composer_, &output);
1072     EXPECT_FALSE(output.has_result());
1073     EXPECT_TRUE(output.has_preedit());
1074     EXPECT_FALSE(output.has_candidates());
1075 
1076     const commands::Preedit &conversion = output.preedit();
1077     EXPECT_EQ(1, conversion.segment_size());
1078     EXPECT_EQ("abc",
1079               conversion.segment(0).value());
1080   }
1081 
1082   EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_));
1083   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1084   EXPECT_FALSE(IsCandidateListVisible(converter));
1085   {  // Make sure the output
1086     commands::Output output;
1087     converter.FillOutput(*composer_, &output);
1088     EXPECT_FALSE(output.has_result());
1089     EXPECT_TRUE(output.has_preedit());
1090     EXPECT_FALSE(output.has_candidates());
1091 
1092     const commands::Preedit &conversion = output.preedit();
1093     EXPECT_EQ(1, conversion.segment_size());
1094     EXPECT_EQ("abc", conversion.segment(0).value());
1095   }
1096 
1097   EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_));
1098   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1099   EXPECT_FALSE(IsCandidateListVisible(converter));
1100   {  // Make sure the output
1101     commands::Output output;
1102     converter.FillOutput(*composer_, &output);
1103     EXPECT_FALSE(output.has_result());
1104     EXPECT_TRUE(output.has_preedit());
1105     EXPECT_FALSE(output.has_candidates());
1106 
1107     const commands::Preedit &conversion = output.preedit();
1108     EXPECT_EQ(1, conversion.segment_size());
1109     EXPECT_EQ("ABC", conversion.segment(0).value());
1110   }
1111 }
1112 
TEST_F(SessionConverterTest,ConvertToHalfWidth_2)1113 TEST_F(SessionConverterTest, ConvertToHalfWidth_2) {
1114   // http://b/2517514
1115   // ConvertToHalfWidth converts punctuations differently w/ or w/o kana.
1116   SessionConverter converter(
1117       convertermock_.get(), request_.get(), config_.get());
1118   composer_->InsertCharacterKeyAndPreedit("q", "q");
1119   composer_->InsertCharacterKeyAndPreedit(",", "、");
1120   composer_->InsertCharacterKeyAndPreedit(".", "。");
1121 
1122   Segments segments;
1123   {  // Initialize segments.
1124     Segment *segment = segments.add_segment();
1125     segment->set_key("q、。");
1126     segment->add_candidate()->value = "q,.";
1127     segment->add_candidate()->value = "q、。";
1128   }
1129   FillT13Ns(&segments, composer_.get());
1130   convertermock_->SetStartConversionForRequest(&segments, true);
1131   EXPECT_TRUE(converter.ConvertToHalfWidth(*composer_));
1132   std::vector<int> expected_indices;
1133   expected_indices.push_back(0);
1134   EXPECT_FALSE(IsCandidateListVisible(converter));
1135   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1136 
1137   {  // Make sure the output
1138     commands::Output output;
1139     converter.FillOutput(*composer_, &output);
1140     EXPECT_FALSE(output.has_result());
1141     EXPECT_TRUE(output.has_preedit());
1142     EXPECT_FALSE(output.has_candidates());
1143 
1144     const commands::Preedit &conversion = output.preedit();
1145     EXPECT_EQ(1, conversion.segment_size());
1146     EXPECT_EQ("q、。", conversion.segment(0).value());
1147   }
1148 }
1149 
TEST_F(SessionConverterTest,SwitchKanaType)1150 TEST_F(SessionConverterTest, SwitchKanaType) {
1151   {  // From composition mode.
1152     SessionConverter converter(
1153         convertermock_.get(), request_.get(), config_.get());
1154     composer_->InsertCharacterKeyAndPreedit("a", "あ");
1155     composer_->InsertCharacterKeyAndPreedit("b", "b");
1156     composer_->InsertCharacterKeyAndPreedit("c", "c");
1157 
1158     Segments segments;
1159     {  // Initialize segments.
1160       Segment *segment = segments.add_segment();
1161       segment->set_key("あbc");
1162       segment->add_candidate()->value = "あべし";
1163     }
1164     FillT13Ns(&segments, composer_.get());
1165     convertermock_->SetStartConversionForRequest(&segments, true);
1166     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1167     std::vector<int> expected_indices;
1168     expected_indices.push_back(0);
1169     EXPECT_FALSE(IsCandidateListVisible(converter));
1170     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1171 
1172     {  // Make sure the output
1173       commands::Output output;
1174       converter.FillOutput(*composer_, &output);
1175       EXPECT_FALSE(output.has_result());
1176       EXPECT_TRUE(output.has_preedit());
1177       EXPECT_FALSE(output.has_candidates());
1178 
1179       const commands::Preedit &conversion = output.preedit();
1180       EXPECT_EQ(1, conversion.segment_size());
1181       EXPECT_EQ("アbc",
1182                 conversion.segment(0).value());
1183     }
1184 
1185     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1186     EXPECT_FALSE(IsCandidateListVisible(converter));
1187     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1188     {
1189       commands::Output output;
1190       converter.FillOutput(*composer_, &output);
1191       EXPECT_FALSE(output.has_result());
1192       EXPECT_TRUE(output.has_preedit());
1193       EXPECT_FALSE(output.has_candidates());
1194 
1195       const commands::Preedit &conversion = output.preedit();
1196       EXPECT_EQ(1, conversion.segment_size());
1197       EXPECT_EQ("アbc", conversion.segment(0).value());
1198     }
1199 
1200     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1201     EXPECT_FALSE(IsCandidateListVisible(converter));
1202     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1203     {
1204       commands::Output output;
1205       converter.FillOutput(*composer_, &output);
1206       EXPECT_FALSE(output.has_result());
1207       EXPECT_TRUE(output.has_preedit());
1208       EXPECT_FALSE(output.has_candidates());
1209 
1210       const commands::Preedit &conversion = output.preedit();
1211       EXPECT_EQ(1, conversion.segment_size());
1212       EXPECT_EQ("あbc",
1213                 conversion.segment(0).value());
1214     }
1215   }
1216 
1217   {  // From conversion mode
1218     SessionConverter converter(
1219         convertermock_.get(), request_.get(), config_.get());
1220     composer_->EditErase();
1221     composer_->InsertCharacterKeyAndPreedit("ka", "か");
1222     composer_->InsertCharacterKeyAndPreedit("n", "ん");
1223     composer_->InsertCharacterKeyAndPreedit("ji", "じ");
1224 
1225     Segments segments;
1226     {  // Initialize segments.
1227       Segment *segment = segments.add_segment();
1228       segment->set_key("かんじ");
1229       segment->add_candidate()->value = "漢字";
1230     }
1231     FillT13Ns(&segments, composer_.get());
1232     convertermock_->SetStartConversionForRequest(&segments, true);
1233     EXPECT_TRUE(converter.Convert(*composer_));
1234     std::vector<int> expected_indices;
1235     expected_indices.push_back(0);
1236     EXPECT_FALSE(IsCandidateListVisible(converter));
1237     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1238 
1239     {  // Make sure the output
1240       commands::Output output;
1241       converter.FillOutput(*composer_, &output);
1242       EXPECT_FALSE(output.has_result());
1243       EXPECT_TRUE(output.has_preedit());
1244       EXPECT_FALSE(output.has_candidates());
1245 
1246       const commands::Preedit &conversion = output.preedit();
1247       EXPECT_EQ(1, conversion.segment_size());
1248       EXPECT_EQ("漢字", conversion.segment(0).value());
1249     }
1250 
1251     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1252     EXPECT_FALSE(IsCandidateListVisible(converter));
1253     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1254     {
1255       commands::Output output;
1256       converter.FillOutput(*composer_, &output);
1257       EXPECT_FALSE(output.has_result());
1258       EXPECT_TRUE(output.has_preedit());
1259       EXPECT_FALSE(output.has_candidates());
1260 
1261       const commands::Preedit &conversion = output.preedit();
1262       EXPECT_EQ(1, conversion.segment_size());
1263       EXPECT_EQ("かんじ", conversion.segment(0).value());
1264     }
1265 
1266     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1267     EXPECT_FALSE(IsCandidateListVisible(converter));
1268     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1269     {
1270       commands::Output output;
1271       converter.FillOutput(*composer_, &output);
1272       EXPECT_FALSE(output.has_result());
1273       EXPECT_TRUE(output.has_preedit());
1274       EXPECT_FALSE(output.has_candidates());
1275 
1276       const commands::Preedit &conversion = output.preedit();
1277       EXPECT_EQ(1, conversion.segment_size());
1278       EXPECT_EQ("カンジ", conversion.segment(0).value());
1279     }
1280 
1281     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1282     EXPECT_FALSE(IsCandidateListVisible(converter));
1283     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1284     {
1285       commands::Output output;
1286       converter.FillOutput(*composer_, &output);
1287       EXPECT_FALSE(output.has_result());
1288       EXPECT_TRUE(output.has_preedit());
1289       EXPECT_FALSE(output.has_candidates());
1290 
1291       const commands::Preedit &conversion = output.preedit();
1292       EXPECT_EQ(1, conversion.segment_size());
1293       EXPECT_EQ("カンジ", conversion.segment(0).value());
1294     }
1295 
1296     EXPECT_TRUE(converter.SwitchKanaType(*composer_));
1297     EXPECT_FALSE(IsCandidateListVisible(converter));
1298     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1299     {
1300       commands::Output output;
1301       converter.FillOutput(*composer_, &output);
1302       EXPECT_FALSE(output.has_result());
1303       EXPECT_TRUE(output.has_preedit());
1304       EXPECT_FALSE(output.has_candidates());
1305 
1306       const commands::Preedit &conversion = output.preedit();
1307       EXPECT_EQ(1, conversion.segment_size());
1308       EXPECT_EQ("かんじ", conversion.segment(0).value());
1309     }
1310   }
1311 }
1312 
TEST_F(SessionConverterTest,CommitFirstSegment)1313 TEST_F(SessionConverterTest, CommitFirstSegment) {
1314   SessionConverter converter(
1315       convertermock_.get(), request_.get(), config_.get());
1316   Segments segments;
1317   SetKamaboko(&segments);
1318   FillT13Ns(&segments, composer_.get());
1319   convertermock_->SetStartConversionForRequest(&segments, true);
1320 
1321   const string kKamabokono = "かまぼこの";
1322   const string kInbou = "いんぼう";
1323 
1324   composer_->InsertCharacterPreedit(kKamabokono + kInbou);
1325   EXPECT_TRUE(converter.Convert(*composer_));
1326   std::vector<int> expected_indices;
1327   expected_indices.push_back(0);
1328   expected_indices.push_back(0);
1329   EXPECT_FALSE(IsCandidateListVisible(converter));
1330   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1331 
1332   {  // Check the conversion.
1333     commands::Output output;
1334     converter.FillOutput(*composer_, &output);
1335     EXPECT_FALSE(output.has_result());
1336     EXPECT_TRUE(output.has_preedit());
1337     EXPECT_FALSE(output.has_candidates());
1338 
1339     const commands::Preedit &conversion = output.preedit();
1340     EXPECT_EQ(kKamabokono, conversion.segment(0).value());
1341     EXPECT_EQ("陰謀", conversion.segment(1).value());
1342   }
1343 
1344   converter.CandidateNext(*composer_);
1345   expected_indices[0] += 1;
1346   EXPECT_TRUE(IsCandidateListVisible(converter));
1347   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1348 
1349   {  // Check the conversion.
1350     commands::Output output;
1351     converter.FillOutput(*composer_, &output);
1352     EXPECT_FALSE(output.has_result());
1353     EXPECT_TRUE(output.has_preedit());
1354     EXPECT_TRUE(output.has_candidates());
1355 
1356     const commands::Preedit &conversion = output.preedit();
1357     EXPECT_EQ("カマボコの", conversion.segment(0).value());
1358     EXPECT_EQ("陰謀", conversion.segment(1).value());
1359   }
1360 
1361   {  // Initialization of SetCommitSegments.
1362     Segments segments_after_submit;
1363     Segment *segment = segments_after_submit.add_segment();
1364     segment->set_key("いんぼう");
1365     segment->add_candidate()->value = "陰謀";
1366     segment->add_candidate()->value = "印房";
1367     convertermock_->SetCommitSegments(&segments_after_submit, true);
1368   }
1369   size_t size;
1370   converter.CommitFirstSegment(*composer_, Context::default_instance(), &size);
1371   expected_indices.erase(expected_indices.begin(),
1372                          expected_indices.begin() + 1);
1373   EXPECT_FALSE(IsCandidateListVisible(converter));
1374   EXPECT_EQ(Util::CharsLen(kKamabokono), size);
1375   EXPECT_TRUE(converter.IsActive());
1376   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1377 
1378   EXPECT_COUNT_STATS("Commit", 1);
1379   EXPECT_COUNT_STATS("CommitFromConversion", 1);
1380   EXPECT_STATS_NOT_EXIST("ConversionCandidates0");
1381   EXPECT_COUNT_STATS("ConversionCandidates1", 1);
1382 }
1383 
TEST_F(SessionConverterTest,CommitHeadToFocusedSegments)1384 TEST_F(SessionConverterTest, CommitHeadToFocusedSegments) {
1385   SessionConverter converter(
1386       convertermock_.get(), request_.get(), config_.get());
1387   const string kIberiko = "いべりこ";
1388   const string kNekowo = "ねこを";
1389   const string kItadaita = "いただいた";
1390   {  // Three segments as the result of conversion.
1391     Segments segments;
1392     Segment *segment;
1393     Segment::Candidate *candidate;
1394 
1395     segment = segments.add_segment();
1396     segment->set_key(kIberiko);
1397     candidate = segment->add_candidate();
1398     candidate->value = "イベリコ";
1399 
1400     segment = segments.add_segment();
1401     segment->set_key(kNekowo);
1402     candidate = segment->add_candidate();
1403     candidate->value = "猫を";
1404 
1405     segment = segments.add_segment();
1406     segment->set_key(kItadaita);
1407     candidate = segment->add_candidate();
1408     candidate->value = "頂いた";
1409     convertermock_->SetStartConversionForRequest(&segments, true);
1410   }
1411 
1412   composer_->InsertCharacterPreedit(kIberiko + kNekowo + kItadaita);
1413   EXPECT_TRUE(converter.Convert(*composer_));
1414   // Here [イベリコ]|猫を|頂いた
1415 
1416   converter.SegmentFocusRight();
1417   // Here イベリコ|[猫を]|頂いた
1418 
1419   {  // Initialization of SetCommitSegments.
1420     Segments segments;
1421     Segment *segment;
1422     Segment::Candidate *candidate;
1423 
1424     segment = segments.add_segment();
1425     segment->set_key(kItadaita);
1426     candidate = segment->add_candidate();
1427     candidate->value = "頂いた";
1428     convertermock_->SetStartConversionForRequest(&segments, true);
1429 
1430     convertermock_->SetCommitSegments(&segments, true);
1431   }
1432   size_t size;
1433   converter.CommitHeadToFocusedSegments(*composer_,
1434                                         Context::default_instance(), &size);
1435   // Here 頂いた
1436   EXPECT_FALSE(IsCandidateListVisible(converter));
1437   EXPECT_EQ(Util::CharsLen(kIberiko + kNekowo), size);
1438   EXPECT_TRUE(converter.IsActive());
1439 }
1440 
TEST_F(SessionConverterTest,CommitHeadToFocusedSegments_atLastSegment)1441 TEST_F(SessionConverterTest, CommitHeadToFocusedSegments_atLastSegment) {
1442   SessionConverter converter(
1443       convertermock_.get(), request_.get(), config_.get());
1444   Segments segments;
1445   SetKamaboko(&segments);
1446   convertermock_->SetStartConversionForRequest(&segments, true);
1447 
1448   const string kKamabokono = "かまぼこの";
1449   const string kInbou = "いんぼう";
1450 
1451   composer_->InsertCharacterPreedit(kKamabokono + kInbou);
1452   EXPECT_TRUE(converter.Convert(*composer_));
1453   // Here [かまぼこの]|陰謀
1454 
1455   converter.SegmentFocusRight();
1456   // Here かまぼこの|[陰謀]
1457 
1458   {  // Initialization of SetCommitSegments.
1459     Segments segments_after_submit;
1460     convertermock_->SetCommitSegments(&segments_after_submit, true);
1461   }
1462   size_t size;
1463   // All the segments should be committed.
1464   converter.CommitHeadToFocusedSegments(*composer_,
1465                                         Context::default_instance(), &size);
1466   EXPECT_FALSE(IsCandidateListVisible(converter));
1467   EXPECT_EQ(0, size);
1468   EXPECT_FALSE(converter.IsActive());
1469 }
1470 
TEST_F(SessionConverterTest,CommitPreedit)1471 TEST_F(SessionConverterTest, CommitPreedit) {
1472   SessionConverter converter(
1473       convertermock_.get(), request_.get(), config_.get());
1474   std::vector<int> expected_indices;
1475   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1476   composer_->InsertCharacterPreedit(kChars_Aiueo);
1477   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1478   converter.CommitPreedit(*composer_, Context::default_instance());
1479   composer_->Reset();
1480   EXPECT_FALSE(IsCandidateListVisible(converter));
1481   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1482 
1483   {  // Check the result
1484     commands::Output output;
1485     converter.FillOutput(*composer_, &output);
1486     EXPECT_TRUE(output.has_result());
1487     EXPECT_FALSE(output.has_preedit());
1488     EXPECT_FALSE(output.has_candidates());
1489 
1490     const commands::Result &result = output.result();
1491     EXPECT_EQ(kChars_Aiueo, result.value());
1492     EXPECT_EQ(kChars_Aiueo, result.key());
1493   }
1494   EXPECT_FALSE(converter.IsActive());
1495 
1496   EXPECT_COUNT_STATS("Commit", 1);
1497   EXPECT_COUNT_STATS("CommitFromComposition", 1);
1498 }
1499 
TEST_F(SessionConverterTest,CommitSuggestionByIndex)1500 TEST_F(SessionConverterTest, CommitSuggestionByIndex) {
1501   SessionConverter converter(
1502       convertermock_.get(), request_.get(), config_.get());
1503   Segments segments;
1504   {  // Initialize mock segments for suggestion
1505     segments.set_request_type(Segments::SUGGESTION);
1506     Segment *segment = segments.add_segment();
1507     Segment::Candidate *candidate;
1508     segment->set_key(kChars_Mo);
1509     candidate = segment->add_candidate();
1510     candidate->value = kChars_Mozukusu;
1511     candidate->key = kChars_Mozukusu;
1512     candidate->content_key = kChars_Mozukusu;
1513     candidate = segment->add_candidate();
1514     candidate->value = kChars_Momonga;
1515     candidate->key = kChars_Momonga;
1516     candidate->content_key = kChars_Momonga;
1517   }
1518   composer_->InsertCharacterPreedit(kChars_Mo);
1519 
1520   // Suggestion
1521   convertermock_->SetStartSuggestionForRequest(&segments, true);
1522   EXPECT_TRUE(converter.Suggest(*composer_));
1523   std::vector<int> expected_indices;
1524   expected_indices.push_back(0);
1525   EXPECT_TRUE(IsCandidateListVisible(converter));
1526   EXPECT_TRUE(converter.IsActive());
1527 
1528   {  // Check the candidate list
1529     commands::Output output;
1530     converter.FillOutput(*composer_, &output);
1531     EXPECT_FALSE(output.has_result());
1532     EXPECT_TRUE(output.has_preedit());
1533     EXPECT_TRUE(output.has_candidates());
1534 
1535     const commands::Preedit &preedit = output.preedit();
1536     EXPECT_EQ(1, preedit.segment_size());
1537     EXPECT_EQ(kChars_Mo, preedit.segment(0).value());
1538 
1539     const commands::Candidates &candidates = output.candidates();
1540     EXPECT_EQ(2, candidates.size());
1541     EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value());
1542     EXPECT_FALSE(candidates.has_focused_index());
1543     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1544   }
1545 
1546   // FinishConversion is expected to return empty Segments.
1547   convertermock_->SetFinishConversion(
1548       std::unique_ptr<Segments>(new Segments).get(), true);
1549 
1550   size_t committed_key_size = 0;
1551   converter.CommitSuggestionByIndex(1, *composer_.get(),
1552                                     Context::default_instance(),
1553                                     &committed_key_size);
1554   expected_indices.clear();
1555   composer_->Reset();
1556   EXPECT_FALSE(IsCandidateListVisible(converter));
1557   EXPECT_FALSE(converter.IsActive());
1558   EXPECT_EQ(SessionConverter::kConsumedAllCharacters, committed_key_size);
1559 
1560   {  // Check the result
1561     commands::Output output;
1562     converter.FillOutput(*composer_, &output);
1563     EXPECT_TRUE(output.has_result());
1564     EXPECT_FALSE(output.has_preedit());
1565     EXPECT_FALSE(output.has_candidates());
1566 
1567     const commands::Result &result = output.result();
1568     EXPECT_EQ(kChars_Momonga, result.value());
1569     EXPECT_EQ(kChars_Momonga, result.key());
1570     EXPECT_EQ(SessionConverterInterface::COMPOSITION, GetState(converter));
1571     EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1572   }
1573 
1574   EXPECT_COUNT_STATS("Commit", 1);
1575   // Suggestion is counted as Prediction
1576   EXPECT_COUNT_STATS("CommitFromPrediction", 1);
1577   EXPECT_COUNT_STATS("PredictionCandidates1", 1);
1578 }
1579 
TEST_F(SessionConverterTest,CommitSuggestionById)1580 TEST_F(SessionConverterTest, CommitSuggestionById) {
1581   SessionConverter converter(
1582       convertermock_.get(), request_.get(), config_.get());
1583   Segments segments;
1584   {  // Initialize mock segments for suggestion
1585     segments.set_request_type(Segments::SUGGESTION);
1586     Segment *segment = segments.add_segment();
1587     Segment::Candidate *candidate;
1588     segment->set_key(kChars_Mo);
1589     candidate = segment->add_candidate();
1590     candidate->value = kChars_Mozukusu;
1591     candidate->key = kChars_Mozukusu;
1592     candidate->content_key = kChars_Mozukusu;
1593     candidate = segment->add_candidate();
1594     candidate->value = kChars_Momonga;
1595     candidate->key = kChars_Momonga;
1596     candidate->content_key = kChars_Momonga;
1597   }
1598   composer_->InsertCharacterPreedit(kChars_Mo);
1599 
1600   // Suggestion
1601   convertermock_->SetStartSuggestionForRequest(&segments, true);
1602   EXPECT_TRUE(converter.Suggest(*composer_));
1603   std::vector<int> expected_indices;
1604   expected_indices.push_back(0);
1605   EXPECT_TRUE(IsCandidateListVisible(converter));
1606   EXPECT_TRUE(converter.IsActive());
1607   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1608 
1609   // FinishConversion is expected to return empty Segments.
1610   convertermock_->SetFinishConversion(
1611       std::unique_ptr<Segments>(new Segments).get(), true);
1612 
1613   const int kCandidateIndex = 1;
1614   size_t committed_key_size = 0;
1615   convertermock_->SetCommitSegmentValue(&segments, true);
1616   converter.CommitSuggestionById(kCandidateIndex, *composer_.get(),
1617                                  Context::default_instance(),
1618                                  &committed_key_size);
1619   expected_indices.clear();
1620   composer_->Reset();
1621   EXPECT_FALSE(IsCandidateListVisible(converter));
1622   EXPECT_FALSE(converter.IsActive());
1623   EXPECT_EQ(SessionConverter::kConsumedAllCharacters, committed_key_size);
1624   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1625 
1626   {  // Check the result
1627     commands::Output output;
1628     converter.FillOutput(*composer_, &output);
1629     EXPECT_TRUE(output.has_result());
1630     EXPECT_FALSE(output.has_preedit());
1631     EXPECT_FALSE(output.has_candidates());
1632 
1633     const commands::Result &result = output.result();
1634     EXPECT_EQ(kChars_Momonga, result.value());
1635     EXPECT_EQ(kChars_Momonga, result.key());
1636     EXPECT_EQ(SessionConverterInterface::COMPOSITION, GetState(converter));
1637   }
1638   // Check the converter's internal state
1639   Segments committed_segments;
1640   size_t segment_index;
1641   int candidate_index;
1642   convertermock_->GetCommitSegmentValue(
1643       &committed_segments, &segment_index, &candidate_index);
1644   EXPECT_EQ(0, segment_index);
1645   EXPECT_EQ(kCandidateIndex, candidate_index);
1646 
1647   EXPECT_COUNT_STATS("Commit", 1);
1648   // Suggestion is counted as Prediction.
1649   EXPECT_COUNT_STATS("CommitFromPrediction", 1);
1650   EXPECT_COUNT_STATS("PredictionCandidates" +
1651                      std::to_string(kCandidateIndex),
1652                      1);
1653 }
1654 
TEST_F(SessionConverterTest,PartialSuggestion)1655 TEST_F(SessionConverterTest, PartialSuggestion) {
1656   RequestForUnitTest::FillMobileRequest(request_.get());
1657   SessionConverter converter(
1658       convertermock_.get(), request_.get(), config_.get());
1659   Segments segments1, segments2;
1660   Segments suggestion_segments;
1661   const string kChars_Kokode = "ここで";
1662   const string kChars_Hakimonowo = "はきものを";
1663 
1664   {  // Initialize mock segments for partial suggestion
1665     segments1.set_request_type(Segments::PARTIAL_SUGGESTION);
1666     Segment *segment = segments1.add_segment();
1667     Segment::Candidate *candidate;
1668     segment->set_key(kChars_Kokode);
1669     candidate = segment->add_candidate();
1670     candidate->value = "此処では";
1671     candidate->key = kChars_Kokode;
1672     candidate->content_key = kChars_Kokode;
1673     candidate->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
1674     candidate->consumed_key_size = Util::CharsLen(kChars_Kokode);
1675   }
1676 
1677   // Suggestion that matches to the same key by its prefix.
1678   // Should not be used by partial suggestion.
1679   {
1680     suggestion_segments.set_request_type(Segments::SUGGESTION);
1681     Segment *segment = suggestion_segments.add_segment();
1682     Segment::Candidate *candidate;
1683     segment->set_key(kChars_Kokode);
1684     candidate = segment->add_candidate();
1685     candidate->value = "ここでは着物を";
1686     candidate->key = "ここではきものを";
1687     candidate->content_key = candidate->key;
1688     candidate = segment->add_candidate();
1689   }
1690 
1691   {  // Initialize mock segments for suggestion
1692     segments2.set_request_type(Segments::SUGGESTION);
1693     Segment *segment = segments2.add_segment();
1694     Segment::Candidate *candidate;
1695     segment->set_key(kChars_Hakimonowo);
1696     candidate = segment->add_candidate();
1697     candidate->value = "此処では";
1698     candidate->key = kChars_Hakimonowo;
1699     candidate->content_key = kChars_Hakimonowo;
1700   }
1701 
1702   // "ここではきものを|"    ("|" is cursor position)
1703   composer_->InsertCharacterPreedit(kChars_Kokode + kChars_Hakimonowo);
1704   composer_->MoveCursorToEnd();
1705   // Suggestion for "ここではきものを". Not partial suggestion.
1706   convertermock_->SetStartSuggestionForRequest(&suggestion_segments, true);
1707   convertermock_->SetStartPartialSuggestion(&suggestion_segments, false);
1708   EXPECT_TRUE(converter.Suggest(*composer_));
1709   std::vector<int> expected_indices;
1710   expected_indices.push_back(0);
1711   EXPECT_TRUE(IsCandidateListVisible(converter));
1712   EXPECT_TRUE(converter.IsActive());
1713   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1714 
1715   // "|ここではきものを"    ("|" is cursor position)
1716   composer_->MoveCursorTo(0);
1717 
1718   // Suggestion for "ここではきものを". Not partial suggestion.
1719   EXPECT_TRUE(converter.Suggest(*composer_));
1720   EXPECT_TRUE(IsCandidateListVisible(converter));
1721   EXPECT_TRUE(converter.IsActive());
1722   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1723 
1724   // "ここで|はきものを"    ("|" is cursor position)
1725   composer_->MoveCursorTo(3);
1726 
1727   // Partial Suggestion for "ここで"
1728   convertermock_->SetStartSuggestionForRequest(&segments1, false);
1729   convertermock_->SetStartPartialSuggestion(&segments1, false);
1730   convertermock_->SetStartPartialSuggestionForRequest(&segments1, true);
1731   EXPECT_TRUE(converter.Suggest(*composer_));
1732   EXPECT_TRUE(IsCandidateListVisible(converter));
1733   EXPECT_TRUE(converter.IsActive());
1734   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1735 
1736   // commit partial suggestion
1737   size_t committed_key_size = 0;
1738   convertermock_->SetStartSuggestionForRequest(&segments2, true);
1739   convertermock_->SetStartPartialSuggestion(&segments2, false);
1740   converter.CommitSuggestionById(0, *composer_.get(),
1741                                  Context::default_instance(),
1742                                  &committed_key_size);
1743   EXPECT_EQ(Util::CharsLen(kChars_Kokode), committed_key_size);
1744   // Indices should be {0} since there is another segment.
1745   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1746 
1747   {  // Check the result
1748     commands::Output output;
1749     converter.FillOutput(*composer_, &output);
1750     EXPECT_TRUE(output.has_result());
1751 
1752     const commands::Result &result = output.result();
1753     EXPECT_EQ("此処では", result.value());
1754     EXPECT_EQ(kChars_Kokode, result.key());
1755     EXPECT_EQ(SessionConverterInterface::SUGGESTION, GetState(converter));
1756   }
1757   // Check the converter's internal state
1758   Segments committed_segments;
1759   size_t segment_index;
1760   int candidate_index;
1761   string current_segment_key;
1762   string new_segment_key;
1763   convertermock_->GetCommitPartialSuggestionSegmentValue(&committed_segments,
1764                                                          &segment_index,
1765                                                          &candidate_index,
1766                                                          &current_segment_key,
1767                                                          &new_segment_key);
1768   EXPECT_EQ(0, segment_index);
1769   EXPECT_EQ(0, candidate_index);
1770   EXPECT_EQ(kChars_Kokode, current_segment_key);
1771   EXPECT_EQ(kChars_Hakimonowo, new_segment_key);
1772 
1773   EXPECT_COUNT_STATS("Commit", 1);
1774   // Suggestion is counted as Prediction.
1775   EXPECT_COUNT_STATS("CommitFromPrediction", 1);
1776   EXPECT_COUNT_STATS("PredictionCandidates0", 1);
1777 }
1778 
TEST_F(SessionConverterTest,SuggestAndPredict)1779 TEST_F(SessionConverterTest, SuggestAndPredict) {
1780   SessionConverter converter(
1781       convertermock_.get(), request_.get(), config_.get());
1782   Segments segments;
1783   {  // Initialize mock segments for suggestion
1784     segments.set_request_type(Segments::SUGGESTION);
1785     Segment *segment = segments.add_segment();
1786     Segment::Candidate *candidate;
1787     segment->set_key(kChars_Mo);
1788     candidate = segment->add_candidate();
1789     candidate->value = kChars_Mozukusu;
1790     candidate->content_key = kChars_Mozukusu;
1791     candidate = segment->add_candidate();
1792     candidate->value = kChars_Momonga;
1793     candidate->content_key = kChars_Momonga;
1794   }
1795   composer_->InsertCharacterPreedit(kChars_Mo);
1796 
1797   // Suggestion
1798   convertermock_->SetStartSuggestionForRequest(&segments, true);
1799   EXPECT_TRUE(converter.Suggest(*composer_));
1800   std::vector<int> expected_indices;
1801   expected_indices.push_back(0);
1802   EXPECT_TRUE(IsCandidateListVisible(converter));
1803   EXPECT_TRUE(converter.IsActive());
1804   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1805 
1806   {  // Check the candidate list
1807     commands::Output output;
1808     converter.FillOutput(*composer_, &output);
1809     EXPECT_FALSE(output.has_result());
1810     EXPECT_TRUE(output.has_preedit());
1811     EXPECT_TRUE(output.has_candidates());
1812     EXPECT_TRUE(output.candidates().has_footer());
1813 #if defined(CHANNEL_DEV) && defined(GOOGLE_JAPANESE_INPUT_BUILD)
1814     EXPECT_FALSE(output.candidates().footer().has_label());
1815     EXPECT_TRUE(output.candidates().footer().has_sub_label());
1816 #else  // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD
1817     EXPECT_TRUE(output.candidates().footer().has_label());
1818     EXPECT_FALSE(output.candidates().footer().has_sub_label());
1819 #endif  // CHANNEL_DEV && GOOGLE_JAPANESE_INPUT_BUILD
1820     EXPECT_FALSE(output.candidates().footer().index_visible());
1821     EXPECT_FALSE(output.candidates().footer().logo_visible());
1822 
1823     const commands::Candidates &candidates = output.candidates();
1824     EXPECT_EQ(2, candidates.size());
1825     EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value());
1826     EXPECT_FALSE(candidates.has_focused_index());
1827   }
1828 
1829   segments.Clear();
1830   {  // Initialize mock segments for prediction
1831     segments.set_request_type(Segments::PREDICTION);
1832     Segment *segment = segments.add_segment();
1833     Segment::Candidate *candidate;
1834     segment->set_key(kChars_Mo);
1835     candidate = segment->add_candidate();
1836     candidate->value = kChars_Mozuku;
1837     candidate->content_key = kChars_Mozuku;
1838     candidate = segment->add_candidate();
1839     candidate->value = kChars_Momonga;
1840     candidate->content_key = kChars_Momonga;
1841     candidate = segment->add_candidate();
1842     candidate->value = "モンドリアン";
1843     candidate->content_key = "もんどりあん";
1844   }
1845 
1846   // Prediction
1847   convertermock_->SetStartPredictionForRequest(&segments, true);
1848   EXPECT_TRUE(converter.Predict(*composer_));
1849   EXPECT_TRUE(IsCandidateListVisible(converter));
1850   EXPECT_TRUE(converter.IsActive());
1851   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1852 
1853   // If there are suggestion results, the Prediction is not triggered.
1854   {
1855     commands::Output output;
1856     converter.FillOutput(*composer_, &output);
1857     EXPECT_FALSE(output.has_result());
1858     EXPECT_TRUE(output.has_preedit());
1859     EXPECT_TRUE(output.has_candidates());
1860     EXPECT_FALSE(output.candidates().footer().has_label());
1861     EXPECT_TRUE(output.candidates().footer().index_visible());
1862     EXPECT_TRUE(output.candidates().footer().logo_visible());
1863 
1864     // Check the conversion
1865     const commands::Preedit &conversion = output.preedit();
1866     EXPECT_EQ(1, conversion.segment_size());
1867     EXPECT_EQ(kChars_Mozukusu, conversion.segment(0).value());
1868 
1869     // Check the candidate list
1870     const commands::Candidates &candidates = output.candidates();
1871     // Candidates should be the same as suggestion
1872     EXPECT_EQ(2, candidates.size());
1873     EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value());
1874     EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value());
1875     EXPECT_TRUE(candidates.has_focused_index());
1876     EXPECT_EQ(0, candidates.focused_index());
1877   }
1878 
1879   // Prediction is called
1880   converter.CandidateNext(*composer_);
1881   converter.CandidateNext(*composer_);
1882   expected_indices[0] += 2;
1883   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1884 
1885   {  // Check the candidate list
1886     commands::Output output;
1887     converter.FillOutput(*composer_, &output);
1888     EXPECT_FALSE(output.has_result());
1889     EXPECT_TRUE(output.has_preedit());
1890     EXPECT_TRUE(output.has_candidates());
1891 
1892     const commands::Candidates &candidates = output.candidates();
1893     // Candidates should be merged with the previous suggestions.
1894     EXPECT_EQ(4, candidates.size());
1895     EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value());
1896     EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value());
1897     EXPECT_EQ(kChars_Mozuku, candidates.candidate(2).value());
1898     EXPECT_EQ("モンドリアン", candidates.candidate(3).value());
1899     EXPECT_TRUE(candidates.has_focused_index());
1900   }
1901 
1902   // Select to "モンドリアン".
1903   converter.CandidateNext(*composer_);
1904   expected_indices[0] += 1;
1905   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1906   converter.Commit(*composer_, Context::default_instance());
1907   composer_->Reset();
1908   expected_indices.clear();
1909   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1910 
1911   {  // Check the submitted value
1912     commands::Output output;
1913     converter.FillOutput(*composer_, &output);
1914     EXPECT_TRUE(output.has_result());
1915     EXPECT_FALSE(output.has_preedit());
1916     EXPECT_FALSE(output.has_candidates());
1917 
1918     const commands::Result &result = output.result();
1919     EXPECT_EQ("モンドリアン", result.value());
1920     EXPECT_EQ("もんどりあん", result.key());
1921   }
1922 
1923   segments.Clear();
1924   {  // Initialize mock segments for prediction
1925     segments.set_request_type(Segments::PREDICTION);
1926     Segment *segment = segments.add_segment();
1927     Segment::Candidate *candidate;
1928     segment->set_key(kChars_Mo);
1929     candidate = segment->add_candidate();
1930     candidate->value = kChars_Mozuku;
1931     candidate->content_key = kChars_Mozuku;
1932     candidate = segment->add_candidate();
1933     candidate->value = kChars_Momonga;
1934     candidate->content_key = kChars_Momonga;
1935     candidate = segment->add_candidate();
1936     candidate->value = "モンドリアン";
1937     candidate->content_key = "もんどりあん";
1938   }
1939 
1940   // Prediction without suggestion.
1941   convertermock_->SetStartPredictionForRequest(&segments, true);
1942   expected_indices.push_back(0);
1943   EXPECT_TRUE(converter.Predict(*composer_));
1944   EXPECT_TRUE(converter.IsActive());
1945   EXPECT_SELECTED_CANDIDATE_INDICES_EQ(converter, expected_indices);
1946 
1947   {
1948     commands::Output output;
1949     converter.FillOutput(*composer_, &output);
1950     EXPECT_FALSE(output.has_result());
1951     EXPECT_TRUE(output.has_preedit());
1952     EXPECT_TRUE(output.has_candidates());
1953 
1954     // Check the conversion
1955     const commands::Preedit &conversion = output.preedit();
1956     EXPECT_EQ(1, conversion.segment_size());
1957     EXPECT_EQ(kChars_Mozuku, conversion.segment(0).value());
1958 
1959     // Check the candidate list
1960     const commands::Candidates &candidates = output.candidates();
1961     // Candidates should NOT be merged with the previous suggestions.
1962     EXPECT_EQ(3, candidates.size());
1963     EXPECT_EQ(kChars_Mozuku, candidates.candidate(0).value());
1964     EXPECT_EQ(kChars_Momonga, candidates.candidate(1).value());
1965     EXPECT_EQ("モンドリアン", candidates.candidate(2).value());
1966     EXPECT_TRUE(candidates.has_focused_index());
1967   }
1968 }
1969 
TEST_F(SessionConverterTest,SuppressSuggestionOnPasswordField)1970 TEST_F(SessionConverterTest, SuppressSuggestionOnPasswordField) {
1971   SessionConverter converter(
1972       convertermock_.get(), request_.get(), config_.get());
1973   Segments segments;
1974   {  // Initialize mock segments for suggestion
1975     segments.set_request_type(Segments::SUGGESTION);
1976     Segment *segment = segments.add_segment();
1977     Segment::Candidate *candidate;
1978     segment->set_key(kChars_Mo);
1979     candidate = segment->add_candidate();
1980     candidate->value = kChars_Mozukusu;
1981     candidate->content_key = kChars_Mozukusu;
1982     candidate = segment->add_candidate();
1983     candidate->value = kChars_Momonga;
1984     candidate->content_key = kChars_Momonga;
1985   }
1986   composer_->InsertCharacterPreedit(kChars_Mo);
1987 
1988   // Suggestion
1989   convertermock_->SetStartSuggestionForRequest(&segments, true);
1990   // No candidates should be visible because we are on password field.
1991 
1992   ConversionPreferences conversion_preferences =
1993       converter.conversion_preferences();
1994   conversion_preferences.request_suggestion = false;
1995   EXPECT_FALSE(converter.SuggestWithPreferences(
1996       *composer_, conversion_preferences));
1997   EXPECT_FALSE(IsCandidateListVisible(converter));
1998   EXPECT_FALSE(converter.IsActive());
1999 }
2000 
TEST_F(SessionConverterTest,SuppressSuggestion)2001 TEST_F(SessionConverterTest, SuppressSuggestion) {
2002   SessionConverter converter(
2003       convertermock_.get(), request_.get(), config_.get());
2004   Segments segments;
2005   {  // Initialize mock segments for suggestion
2006     segments.set_request_type(Segments::SUGGESTION);
2007     Segment *segment = segments.add_segment();
2008     Segment::Candidate *candidate;
2009     segment->set_key(kChars_Mo);
2010     candidate = segment->add_candidate();
2011     candidate->value = kChars_Mozukusu;
2012     candidate->content_key = kChars_Mozukusu;
2013     candidate = segment->add_candidate();
2014     candidate->value = kChars_Momonga;
2015     candidate->content_key = kChars_Momonga;
2016   }
2017   composer_->SetInputFieldType(Context::PASSWORD);
2018   composer_->InsertCharacterPreedit(kChars_Mo);
2019 
2020   // Suggestion
2021   convertermock_->SetStartSuggestionForRequest(&segments, true);
2022   // No candidates should be visible because we are on password field.
2023   EXPECT_FALSE(converter.Suggest(*composer_));
2024   EXPECT_FALSE(IsCandidateListVisible(converter));
2025   EXPECT_FALSE(converter.IsActive());
2026 }
2027 
TEST_F(SessionConverterTest,ExpandPartialSuggestion)2028 TEST_F(SessionConverterTest, ExpandPartialSuggestion) {
2029   RequestForUnitTest::FillMobileRequest(request_.get());
2030   SessionConverter converter(
2031       convertermock_.get(), request_.get(), config_.get());
2032 
2033   const char *kSuggestionValues[] = {
2034       "S0",
2035       "S1",
2036       "S2",
2037   };
2038   const char *kPredictionValues[] = {
2039       "P0",
2040       "P1",
2041       "P2",
2042       // Duplicate entry. Any dupulication should not exist
2043       // in the candidate list.
2044       "S1",
2045       "P3",
2046   };
2047   const char kPredictionKey[] = "left";
2048   const char kSuffixKey[] = "right";
2049   const int kDupulicationIndex = 3;
2050 
2051   Segments segments;
2052   {  // Initialize mock segments for suggestion
2053     segments.set_request_type(Segments::PARTIAL_SUGGESTION);
2054     Segment *segment = segments.add_segment();
2055     Segment::Candidate *candidate;
2056     segment->set_key(kPredictionKey);
2057     for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) {
2058       candidate = segment->add_candidate();
2059       candidate->value = kSuggestionValues[i];
2060       candidate->content_key = kPredictionKey;
2061     }
2062   }
2063   composer_->InsertCharacterPreedit(
2064       string(kPredictionKey) + string(kSuffixKey));
2065   composer_->MoveCursorTo(Util::CharsLen(string(kPredictionKey)));
2066 
2067   // Partial Suggestion
2068   convertermock_->SetStartPartialSuggestionForRequest(&segments, true);
2069   EXPECT_TRUE(converter.Suggest(*composer_));
2070   EXPECT_TRUE(IsCandidateListVisible(converter));
2071   EXPECT_TRUE(converter.IsActive());
2072 
2073   segments.Clear();
2074   {
2075     // Initialize mock segments for partial prediction for expanding suggestion.
2076     segments.set_request_type(Segments::PARTIAL_PREDICTION);
2077     Segment *segment = segments.add_segment();
2078     Segment::Candidate *candidate;
2079     segment->set_key(kPredictionKey);
2080     for (size_t i = 0; i < arraysize(kPredictionValues); ++i) {
2081       candidate = segment->add_candidate();
2082       candidate->value = kPredictionValues[i];
2083       candidate->content_key = kPredictionKey;
2084     }
2085   }
2086   // Expand suggestion candidate (cursor == HEAD)
2087   composer_->MoveCursorTo(0);
2088   convertermock_->SetStartPartialPrediction(&segments, false);
2089   convertermock_->SetStartPredictionForRequest(&segments, true);
2090   EXPECT_TRUE(converter.ExpandSuggestion(*composer_));
2091   EXPECT_TRUE(IsCandidateListVisible(converter));
2092   EXPECT_TRUE(converter.IsActive());
2093 
2094   // Expand suggestion candidate (cursor == TAIL)
2095   composer_->MoveCursorTo(composer_->GetLength());
2096   convertermock_->SetStartPredictionForRequest(&segments, false);
2097   convertermock_->SetStartPartialPrediction(&segments, false);
2098   convertermock_->SetStartPartialPredictionForRequest(&segments, true);
2099   EXPECT_TRUE(converter.ExpandSuggestion(*composer_));
2100   EXPECT_TRUE(IsCandidateListVisible(converter));
2101   EXPECT_TRUE(converter.IsActive());
2102   {  // Check the candidate list
2103     commands::Output output;
2104     converter.FillOutput(*composer_, &output);
2105     const commands::Candidates &candidates = output.candidates();
2106     EXPECT_EQ(commands::SUGGESTION, candidates.category());
2107     EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category());
2108     // -1 is for duplicate entry.
2109     EXPECT_EQ(arraysize(kSuggestionValues) + arraysize(kPredictionValues) - 1,
2110               candidates.size());
2111     size_t i;
2112     for (i = 0; i < arraysize(kSuggestionValues); ++i) {
2113       EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value());
2114     }
2115 
2116     // -1 is for duplicate entry.
2117     for (; i < candidates.size(); ++i) {
2118       size_t index_in_prediction = i - arraysize(kSuggestionValues);
2119       if (index_in_prediction >= kDupulicationIndex) {
2120         ++index_in_prediction;
2121       }
2122       EXPECT_EQ(kPredictionValues[index_in_prediction],
2123                 candidates.candidate(i).value());
2124     }
2125   }
2126 }
2127 
TEST_F(SessionConverterTest,ExpandSuggestion)2128 TEST_F(SessionConverterTest, ExpandSuggestion) {
2129   RequestForUnitTest::FillMobileRequest(request_.get());
2130   SessionConverter converter(
2131       convertermock_.get(), request_.get(), config_.get());
2132 
2133   const char *kSuggestionValues[] = {
2134       "S0",
2135       "S1",
2136       "S2",
2137   };
2138   const char *kPredictionValues[] = {
2139       "P0",
2140       "P1",
2141       "P2",
2142       // Duplicate entry. Any dupulication should not exist
2143       // in the candidate list.
2144       "S1",
2145       "P3",
2146   };
2147   const char kKey[] = "key";
2148   const int kDupulicationIndex = 3;
2149 
2150   Segments segments;
2151   {  // Initialize mock segments for suggestion
2152     segments.set_request_type(Segments::SUGGESTION);
2153     Segment *segment = segments.add_segment();
2154     Segment::Candidate *candidate;
2155     segment->set_key(kKey);
2156     for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) {
2157       candidate = segment->add_candidate();
2158       candidate->value = kSuggestionValues[i];
2159       candidate->content_key = kKey;
2160     }
2161   }
2162   composer_->InsertCharacterPreedit(kKey);
2163 
2164   // Suggestion
2165   convertermock_->SetStartSuggestionForRequest(&segments, true);
2166   EXPECT_TRUE(converter.Suggest(*composer_));
2167   EXPECT_TRUE(IsCandidateListVisible(converter));
2168   EXPECT_TRUE(converter.IsActive());
2169   {  // Check the candidate list
2170     commands::Output output;
2171     converter.FillOutput(*composer_, &output);
2172     const commands::Candidates &candidates = output.candidates();
2173     EXPECT_EQ(commands::SUGGESTION, candidates.category());
2174     EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category());
2175     EXPECT_EQ(arraysize(kSuggestionValues), candidates.size());
2176     for (size_t i = 0; i < arraysize(kSuggestionValues); ++i) {
2177       EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value());
2178     }
2179   }
2180 
2181   segments.Clear();
2182   {  // Initialize mock segments for prediction (== expanding suggestion)
2183     segments.set_request_type(Segments::PREDICTION);
2184     Segment *segment = segments.add_segment();
2185     Segment::Candidate *candidate;
2186     segment->set_key(kKey);
2187     for (size_t i = 0; i < arraysize(kPredictionValues); ++i) {
2188       candidate = segment->add_candidate();
2189       candidate->value = kPredictionValues[i];
2190       candidate->content_key = kKey;
2191     }
2192   }
2193   // Expand suggestion candidate
2194   convertermock_->SetStartPredictionForRequest(&segments, true);
2195   EXPECT_TRUE(converter.ExpandSuggestion(*composer_));
2196   EXPECT_TRUE(IsCandidateListVisible(converter));
2197   EXPECT_TRUE(converter.IsActive());
2198   {  // Check the candidate list
2199     commands::Output output;
2200     converter.FillOutput(*composer_, &output);
2201     const commands::Candidates &candidates = output.candidates();
2202     EXPECT_EQ(commands::SUGGESTION, candidates.category());
2203     EXPECT_EQ(commands::SUGGESTION, output.all_candidate_words().category());
2204     // -1 is for duplicate entry.
2205     EXPECT_EQ(arraysize(kSuggestionValues) + arraysize(kPredictionValues) - 1,
2206               candidates.size());
2207     size_t i;
2208     for (i = 0; i < arraysize(kSuggestionValues); ++i) {
2209       EXPECT_EQ(kSuggestionValues[i], candidates.candidate(i).value());
2210     }
2211 
2212     // -1 is for duplicate entry.
2213     for (; i < candidates.size(); ++i) {
2214       size_t index_in_prediction = i - arraysize(kSuggestionValues);
2215       if (index_in_prediction >= kDupulicationIndex) {
2216         ++index_in_prediction;
2217       }
2218       EXPECT_EQ(kPredictionValues[index_in_prediction],
2219                 candidates.candidate(i).value());
2220     }
2221   }
2222 }
2223 
TEST_F(SessionConverterTest,AppendCandidateList)2224 TEST_F(SessionConverterTest, AppendCandidateList) {
2225   SessionConverter converter(
2226       convertermock_.get(), request_.get(), config_.get());
2227   SetState(SessionConverterInterface::CONVERSION, &converter);
2228   converter.set_use_cascading_window(true);
2229   Segments segments;
2230 
2231   {
2232     SetAiueo(&segments);
2233     FillT13Ns(&segments, composer_.get());
2234 
2235     SetSegments(segments, &converter);
2236     AppendCandidateList(&converter);
2237     const CandidateList &candidate_list = GetCandidateList(converter);
2238     // 3 == hiragana cand, katakana cand and sub candidate list.
2239     EXPECT_EQ(3, candidate_list.size());
2240     EXPECT_TRUE(candidate_list.focused());
2241     size_t sub_cand_list_count = 0;
2242     for (size_t i = 0; i < candidate_list.size(); ++i) {
2243       if (candidate_list.candidate(i).IsSubcandidateList()) {
2244         ++sub_cand_list_count;
2245       }
2246     }
2247     // Sub candidate list for T13N.
2248     EXPECT_EQ(1, sub_cand_list_count);
2249   }
2250   {
2251     Segment *segment = segments.mutable_conversion_segment(0);
2252     Segment::Candidate *candidate = segment->add_candidate();
2253     candidate->value = "あいうえお_2";
2254     // New meta candidates.
2255     // They should be ignored.
2256     std::vector<Segment::Candidate> *meta_candidates =
2257         segment->mutable_meta_candidates();
2258     meta_candidates->clear();
2259     meta_candidates->resize(1);
2260     meta_candidates->at(0).Init();
2261     meta_candidates->at(0).value = "t13nValue";
2262     meta_candidates->at(0).content_value = "t13nValue";
2263     meta_candidates->at(0).content_key = segment->key();
2264 
2265     SetSegments(segments, &converter);
2266     AppendCandidateList(&converter);
2267     const CandidateList &candidate_list = GetCandidateList(converter);
2268     // 4 == hiragana cand, katakana cand, hiragana cand2
2269     // and sub candidate list.
2270     EXPECT_EQ(4, candidate_list.size());
2271     EXPECT_TRUE(candidate_list.focused());
2272     size_t sub_cand_list_count = 0;
2273     std::set<int> id_set;
2274     for (size_t i = 0; i < candidate_list.size(); ++i) {
2275       if (candidate_list.candidate(i).IsSubcandidateList()) {
2276         ++sub_cand_list_count;
2277       } else {
2278         // No duplicate ids are expected.
2279         int id = candidate_list.candidate(i).id();
2280         // EXPECT_EQ(iterator, iterator) might cause compile error in specific
2281         // environment.
2282         EXPECT_TRUE(id_set.end() == id_set.find(id));
2283         id_set.insert(id);
2284       }
2285     }
2286     // Sub candidate list shouldn't be duplicated.
2287     EXPECT_EQ(1, sub_cand_list_count);
2288   }
2289 }
2290 
TEST_F(SessionConverterTest,AppendCandidateListForRequestTypes)2291 TEST_F(SessionConverterTest, AppendCandidateListForRequestTypes) {
2292   SessionConverter converter(
2293       convertermock_.get(), request_.get(), config_.get());
2294   SetState(SessionConverterInterface::SUGGESTION, &converter);
2295   Segments segments;
2296 
2297   {
2298     SetAiueo(&segments);
2299     FillT13Ns(&segments, composer_.get());
2300     segments.set_request_type(Segments::SUGGESTION);
2301     SetSegments(segments, &converter);
2302     AppendCandidateList(&converter);
2303     const CandidateList &candidate_list = GetCandidateList(converter);
2304     EXPECT_FALSE(candidate_list.focused());
2305   }
2306 
2307   segments.Clear();
2308   {
2309     SetAiueo(&segments);
2310     FillT13Ns(&segments, composer_.get());
2311     segments.set_request_type(Segments::PARTIAL_SUGGESTION);
2312     SetSegments(segments, &converter);
2313     AppendCandidateList(&converter);
2314     const CandidateList &candidate_list = GetCandidateList(converter);
2315     EXPECT_FALSE(candidate_list.focused());
2316   }
2317 
2318   segments.Clear();
2319   {
2320     SetAiueo(&segments);
2321     FillT13Ns(&segments, composer_.get());
2322     segments.set_request_type(Segments::PARTIAL_PREDICTION);
2323     SetSegments(segments, &converter);
2324     AppendCandidateList(&converter);
2325     const CandidateList &candidate_list = GetCandidateList(converter);
2326     EXPECT_FALSE(candidate_list.focused());
2327   }
2328 }
2329 
TEST_F(SessionConverterTest,ReloadConfig)2330 TEST_F(SessionConverterTest, ReloadConfig) {
2331   SessionConverter converter(
2332       convertermock_.get(), request_.get(), config_.get());
2333   Segments segments;
2334   SetAiueo(&segments);
2335   FillT13Ns(&segments, composer_.get());
2336   convertermock_->SetStartConversionForRequest(&segments, true);
2337 
2338   composer_->InsertCharacterPreedit("aiueo");
2339   EXPECT_TRUE(converter.Convert(*composer_));
2340   converter.SetCandidateListVisible(true);
2341 
2342   {  // Set OperationPreferences
2343     converter.set_use_cascading_window(false);
2344     converter.set_selection_shortcut(config::Config::SHORTCUT_123456789);
2345     EXPECT_TRUE(IsCandidateListVisible(converter));
2346   }
2347   {  // Check the config update
2348     commands::Output output;
2349     converter.FillOutput(*composer_, &output);
2350     EXPECT_FALSE(output.has_result());
2351     EXPECT_TRUE(output.has_preedit());
2352     EXPECT_TRUE(output.has_candidates());
2353 
2354     const commands::Candidates &candidates = output.candidates();
2355     EXPECT_EQ("1", candidates.candidate(0).annotation().shortcut());
2356     EXPECT_EQ("2", candidates.candidate(1).annotation().shortcut());
2357   }
2358 
2359   {  // Set OperationPreferences #2
2360     converter.set_use_cascading_window(false);
2361     converter.set_selection_shortcut(config::Config::NO_SHORTCUT);
2362   }
2363   {  // Check the config update
2364     commands::Output output;
2365     converter.FillOutput(*composer_, &output);
2366     EXPECT_FALSE(output.has_result());
2367     EXPECT_TRUE(output.has_preedit());
2368     EXPECT_TRUE(output.has_candidates());
2369 
2370     const commands::Candidates &candidates = output.candidates();
2371     EXPECT_TRUE(candidates.candidate(0).annotation().shortcut().empty());
2372     EXPECT_TRUE(candidates.candidate(1).annotation().shortcut().empty());
2373   }
2374 }
2375 
TEST_F(SessionConverterTest,OutputAllCandidateWords)2376 TEST_F(SessionConverterTest, OutputAllCandidateWords) {
2377   SessionConverter converter(
2378       convertermock_.get(), request_.get(), config_.get());
2379   Segments segments;
2380   SetKamaboko(&segments);
2381   const string kKamabokono = "かまぼこの";
2382   const string kInbou = "いんぼう";
2383   composer_->InsertCharacterPreedit(kKamabokono + kInbou);
2384 
2385   FillT13Ns(&segments, composer_.get());
2386   convertermock_->SetStartConversionForRequest(&segments, true);
2387 
2388   commands::Output output;
2389 
2390   EXPECT_TRUE(converter.Convert(*composer_));
2391   {
2392     ASSERT_TRUE(converter.IsActive());
2393     EXPECT_FALSE(IsCandidateListVisible(converter));
2394 
2395     output.Clear();
2396     converter.PopOutput(*composer_, &output);
2397     EXPECT_FALSE(output.has_result());
2398     EXPECT_TRUE(output.has_preedit());
2399     EXPECT_FALSE(output.has_candidates());
2400     EXPECT_TRUE(output.has_all_candidate_words());
2401 
2402     EXPECT_EQ(0, output.all_candidate_words().focused_index());
2403     EXPECT_EQ(commands::CONVERSION, output.all_candidate_words().category());
2404     // [ "かまぼこの", "カマボコの", "カマボコノ" (t13n), "かまぼこの" (t13n),
2405     //   "カマボコノ" (t13n) ]
2406     EXPECT_EQ(5, output.all_candidate_words().candidates_size());
2407   }
2408 
2409   converter.CandidateNext(*composer_);
2410   {
2411     ASSERT_TRUE(converter.IsActive());
2412     EXPECT_TRUE(IsCandidateListVisible(converter));
2413 
2414     output.Clear();
2415     converter.PopOutput(*composer_, &output);
2416     EXPECT_FALSE(output.has_result());
2417     EXPECT_TRUE(output.has_preedit());
2418     EXPECT_TRUE(output.has_candidates());
2419     EXPECT_TRUE(output.has_all_candidate_words());
2420 
2421     EXPECT_EQ(1, output.all_candidate_words().focused_index());
2422     EXPECT_EQ(commands::CONVERSION, output.all_candidate_words().category());
2423     // [ "かまぼこの", "カマボコの", "カマボコノ" (t13n), "かまぼこの" (t13n),
2424     //   "カマボコノ" (t13n) ]
2425     EXPECT_EQ(5, output.all_candidate_words().candidates_size());
2426   }
2427 
2428   converter.SegmentFocusRight();
2429   {
2430     ASSERT_TRUE(converter.IsActive());
2431     EXPECT_FALSE(IsCandidateListVisible(converter));
2432 
2433     output.Clear();
2434     converter.PopOutput(*composer_, &output);
2435     EXPECT_FALSE(output.has_result());
2436     EXPECT_TRUE(output.has_preedit());
2437     EXPECT_FALSE(output.has_candidates());
2438     EXPECT_TRUE(output.has_all_candidate_words());
2439 
2440     EXPECT_EQ(0, output.all_candidate_words().focused_index());
2441     EXPECT_EQ(commands::CONVERSION, output.all_candidate_words().category());
2442     // [ "陰謀", "印房", "インボウ" (t13n), "いんぼう" (t13n), "インボウ" (t13n) ]
2443     EXPECT_EQ(5, output.all_candidate_words().candidates_size());
2444   }
2445 }
2446 
TEST_F(SessionConverterTest,GetPreeditAndGetConversion)2447 TEST_F(SessionConverterTest, GetPreeditAndGetConversion) {
2448   Segments segments;
2449 
2450   Segment *segment;
2451   Segment::Candidate *candidate;
2452 
2453   segment = segments.add_segment();
2454   segment->set_segment_type(Segment::HISTORY);
2455   segment->set_key("[key:history1]");
2456   candidate = segment->add_candidate();
2457   candidate->content_key = "[content_key:history1-1]";
2458   candidate = segment->add_candidate();
2459   candidate->content_key = "[content_key:history1-2]";
2460 
2461   segment = segments.add_segment();
2462   segment->set_segment_type(Segment::FREE);
2463   segment->set_key("[key:conversion1]");
2464   candidate = segment->add_candidate();
2465   candidate->key = "[key:conversion1-1]";
2466   candidate->content_key = "[content_key:conversion1-1]";
2467   candidate->value = "[value:conversion1-1]";
2468   candidate = segment->add_candidate();
2469   candidate->key = "[key:conversion1-2]";
2470   candidate->content_key = "[content_key:conversion1-2]";
2471   candidate->value = "[value:conversion1-2]";
2472   {
2473     // PREDICTION
2474     SessionConverter converter(
2475         convertermock_.get(), request_.get(), config_.get());
2476     convertermock_->SetStartPredictionForRequest(&segments, true);
2477     converter.Predict(*composer_);
2478     converter.CandidateNext(*composer_);
2479     string preedit;
2480     GetPreedit(converter, 0, 1, &preedit);
2481     EXPECT_EQ("[content_key:conversion1-2]", preedit);
2482     string conversion;
2483     GetConversion(converter, 0, 1, &conversion);
2484     EXPECT_EQ("[value:conversion1-2]", conversion);
2485   }
2486   {
2487     // SUGGESTION
2488     SessionConverter converter(
2489         convertermock_.get(), request_.get(), config_.get());
2490     convertermock_->SetStartSuggestionForRequest(&segments, true);
2491     converter.Suggest(*composer_);
2492     string preedit;
2493     GetPreedit(converter, 0, 1, &preedit);
2494     EXPECT_EQ("[content_key:conversion1-1]", preedit);
2495     string conversion;
2496     GetConversion(converter, 0, 1, &conversion);
2497     EXPECT_EQ("[value:conversion1-1]", conversion);
2498   }
2499   segment = segments.add_segment();
2500   segment->set_segment_type(Segment::FREE);
2501   segment->set_key("[key:conversion2]");
2502   candidate = segment->add_candidate();
2503   candidate->key = "[key:conversion2-1]";
2504   candidate->content_key = "[content_key:conversion2-1]";
2505   candidate->value = "[value:conversion2-1]";
2506   candidate = segment->add_candidate();
2507   candidate->key = "[key:conversion2-2]";
2508   candidate->content_key = "[content_key:conversion2-2]";
2509   candidate->value = "[value:conversion2-2]";
2510   {
2511     // CONVERSION
2512     SessionConverter converter(
2513         convertermock_.get(), request_.get(), config_.get());
2514     convertermock_->SetStartConversionForRequest(&segments, true);
2515     converter.Convert(*composer_);
2516     converter.CandidateNext(*composer_);
2517     string preedit;
2518     GetPreedit(converter, 0, 2, &preedit);
2519     EXPECT_EQ("[key:conversion1][key:conversion2]", preedit);
2520     string conversion;
2521     GetConversion(converter, 0, 2, &conversion);
2522     EXPECT_EQ("[value:conversion1-2][value:conversion2-1]", conversion);
2523   }
2524 }
2525 
TEST_F(SessionConverterTest,GetAndSetSegments)2526 TEST_F(SessionConverterTest, GetAndSetSegments) {
2527   SessionConverter converter(
2528       convertermock_.get(), request_.get(), config_.get());
2529   Segments segments;
2530 
2531   // Set history segments.
2532   const string kHistoryInput[] = {"車で", "行く"};
2533   for (size_t i = 0; i < arraysize(kHistoryInput); ++i) {
2534     Segment *segment = segments.add_segment();
2535     segment->set_segment_type(Segment::HISTORY);
2536     Segment::Candidate *candidate = segment->add_candidate();
2537     candidate->value = kHistoryInput[i];
2538   }
2539   convertermock_->SetFinishConversion(&segments, true);
2540   converter.CommitPreedit(*composer_, Context::default_instance());
2541 
2542   Segments src;
2543   GetSegments(converter, &src);
2544   ASSERT_EQ(2, src.history_segments_size());
2545   EXPECT_EQ("車で", src.history_segment(0).candidate(0).value);
2546   EXPECT_EQ("行く", src.history_segment(1).candidate(0).value);
2547 
2548   src.mutable_history_segment(0)->mutable_candidate(0)->value = "歩いて";
2549   Segment *segment = src.add_segment();
2550   segment->set_segment_type(Segment::FREE);
2551   Segment::Candidate *candidate = segment->add_candidate();
2552   candidate->value = "?";
2553 
2554   SetSegments(src, &converter);
2555 
2556   Segments dest;
2557   GetSegments(converter, &dest);
2558 
2559   ASSERT_EQ(2, dest.history_segments_size());
2560   ASSERT_EQ(1, dest.conversion_segments_size());
2561   EXPECT_EQ(src.history_segment(0).candidate(0).value,
2562             dest.history_segment(0).candidate(0).value);
2563   EXPECT_EQ(src.history_segment(1).candidate(0).value,
2564             dest.history_segment(1).candidate(0).value);
2565   EXPECT_EQ(src.history_segment(2).candidate(0).value,
2566             dest.history_segment(2).candidate(0).value);
2567 }
2568 
TEST_F(SessionConverterTest,Clone)2569 TEST_F(SessionConverterTest, Clone) {
2570   SessionConverter src(
2571       convertermock_.get(), request_.get(), config_.get());
2572 
2573   const string kKamabokono = "かまぼこの";
2574   const string kInbou = "いんぼう";
2575   const string kInbouKanji = "陰謀";
2576 
2577   {  // create source converter
2578     Segments segments;
2579     SetKamaboko(&segments);
2580 
2581     convertermock_->SetStartConversionForRequest(&segments, true);
2582 
2583     src.set_use_cascading_window(false);
2584     src.set_selection_shortcut(config::Config::SHORTCUT_123456789);
2585   }
2586 
2587   {  // validation
2588     // Copy and validate
2589     std::unique_ptr<SessionConverter> dest(src.Clone());
2590     ASSERT_TRUE(dest.get() != NULL);
2591     ExpectSameSessionConverter(src, *dest);
2592 
2593     // Convert source
2594     EXPECT_TRUE(src.Convert(*composer_));
2595     EXPECT_TRUE(src.IsActive());
2596 
2597     // Convert destination and validate
2598     EXPECT_TRUE(dest->Convert(*composer_));
2599     ExpectSameSessionConverter(src, *dest);
2600 
2601     // Copy converted and validate
2602     dest.reset(src.Clone());
2603     ASSERT_TRUE(dest.get() != NULL);
2604     ExpectSameSessionConverter(src, *dest);
2605   }
2606 }
2607 
2608 // Suggest() in the suggestion state was not accepted.  (http://b/1948334)
TEST_F(SessionConverterTest,Issue1948334)2609 TEST_F(SessionConverterTest, Issue1948334) {
2610   SessionConverter converter(
2611       convertermock_.get(), request_.get(), config_.get());
2612   Segments segments;
2613   {  // Initialize mock segments for the first suggestion
2614     segments.set_request_type(Segments::SUGGESTION);
2615     Segment *segment = segments.add_segment();
2616     Segment::Candidate *candidate;
2617     segment->set_key(kChars_Mo);
2618     candidate = segment->add_candidate();
2619     candidate->value = kChars_Mozukusu;
2620     candidate->content_key = kChars_Mozukusu;
2621     candidate = segment->add_candidate();
2622     candidate->value = kChars_Momonga;
2623     candidate->content_key = kChars_Momonga;
2624   }
2625   composer_->InsertCharacterPreedit(kChars_Mo);
2626 
2627   // Suggestion
2628   convertermock_->SetStartSuggestionForRequest(&segments, true);
2629   EXPECT_TRUE(converter.Suggest(*composer_));
2630   EXPECT_TRUE(converter.IsActive());
2631 
2632   segments.Clear();
2633   {  // Initialize mock segments for the second suggestion
2634     segments.set_request_type(Segments::SUGGESTION);
2635     Segment *segment = segments.add_segment();
2636     Segment::Candidate *candidate;
2637     segment->set_key("もず");
2638     candidate = segment->add_candidate();
2639     candidate->value = kChars_Mozukusu;
2640     candidate->content_key = kChars_Mozukusu;
2641   }
2642   composer_->InsertCharacterPreedit("もず");
2643 
2644   // Suggestion
2645   convertermock_->SetStartSuggestionForRequest(&segments, true);
2646   EXPECT_TRUE(converter.Suggest(*composer_));
2647   EXPECT_TRUE(converter.IsActive());
2648 
2649   {  // Check the candidate list
2650     commands::Output output;
2651     converter.FillOutput(*composer_, &output);
2652     EXPECT_FALSE(output.has_result());
2653     EXPECT_TRUE(output.has_preedit());
2654     EXPECT_TRUE(output.has_candidates());
2655 
2656     const commands::Candidates &candidates = output.candidates();
2657     // Candidates should be merged with the previous suggestions.
2658     EXPECT_EQ(1, candidates.size());
2659     EXPECT_EQ(kChars_Mozukusu, candidates.candidate(0).value());
2660     EXPECT_FALSE(candidates.has_focused_index());
2661   }
2662 }
2663 
TEST_F(SessionConverterTest,Issue1960362)2664 TEST_F(SessionConverterTest, Issue1960362) {
2665   // Testcase against http://b/1960362, a candidate list was not
2666   // updated when ConvertToTransliteration changed the size of segments.
2667 
2668   table_->AddRule("zyu", "ZYU", "");
2669   table_->AddRule("jyu", "ZYU", "");
2670   table_->AddRule("tt", "XTU", "t");
2671   table_->AddRule("ta", "TA", "");
2672 
2673   composer_->InsertCharacter("j");
2674   composer_->InsertCharacter("y");
2675   composer_->InsertCharacter("u");
2676   composer_->InsertCharacter("t");
2677 
2678   SessionConverter converter(
2679       convertermock_.get(), request_.get(), config_.get());
2680 
2681   Segments segments;
2682   {
2683     segments.set_request_type(Segments::CONVERSION);
2684     Segment *segment;
2685     Segment::Candidate *candidate;
2686 
2687     segment = segments.add_segment();
2688     segment->set_key("ZYU");
2689     candidate = segment->add_candidate();
2690     candidate->value = "[ZYU]";
2691     candidate->content_key = "[ZYU]";
2692 
2693     segment = segments.add_segment();
2694     segment->set_key("t");
2695     candidate = segment->add_candidate();
2696     candidate->value = "[t]";
2697     candidate->content_key = "[t]";
2698   }
2699 
2700   Segments resized_segments;
2701   {
2702     resized_segments.set_request_type(Segments::CONVERSION);
2703     Segment *segment = resized_segments.add_segment();
2704     Segment::Candidate *candidate;
2705     segment->set_key("ZYUt");
2706     candidate = segment->add_candidate();
2707     candidate->value = "[ZYUt]";
2708     candidate->content_key = "[ZYUt]";
2709   }
2710 
2711   FillT13Ns(&segments, composer_.get());
2712   convertermock_->SetStartConversionForRequest(&segments, true);
2713   FillT13Ns(&resized_segments, composer_.get());
2714   convertermock_->SetResizeSegment1(&resized_segments, true);
2715   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
2716                                                  transliteration::HALF_ASCII));
2717   EXPECT_FALSE(IsCandidateListVisible(converter));
2718 
2719   commands::Output output;
2720   converter.FillOutput(*composer_, &output);
2721   EXPECT_FALSE(output.has_result());
2722   EXPECT_TRUE(output.has_preedit());
2723   EXPECT_FALSE(output.has_candidates());
2724 
2725   const commands::Preedit &conversion = output.preedit();
2726   EXPECT_EQ("jyut", conversion.segment(0).value());
2727 }
2728 
TEST_F(SessionConverterTest,Issue1978201)2729 TEST_F(SessionConverterTest, Issue1978201) {
2730   // This is a unittest against http://b/1978201
2731   SessionConverter converter(
2732       convertermock_.get(), request_.get(), config_.get());
2733   Segments segments;
2734   composer_->InsertCharacterPreedit(kChars_Mo);
2735 
2736   {  // Initialize mock segments for prediction
2737     segments.set_request_type(Segments::PREDICTION);
2738     Segment *segment = segments.add_segment();
2739     Segment::Candidate *candidate;
2740     segment->set_key(kChars_Mo);
2741     candidate = segment->add_candidate();
2742     candidate->value = kChars_Mozuku;
2743     candidate->content_key = kChars_Mozuku;
2744     candidate = segment->add_candidate();
2745     candidate->value = kChars_Momonga;
2746     candidate->content_key = kChars_Momonga;
2747   }
2748 
2749   // Prediction
2750   convertermock_->SetStartPredictionForRequest(&segments, true);
2751   EXPECT_TRUE(converter.Predict(*composer_));
2752   EXPECT_TRUE(converter.IsActive());
2753 
2754   {  // Check the conversion
2755     commands::Output output;
2756     converter.FillOutput(*composer_, &output);
2757     EXPECT_FALSE(output.has_result());
2758     EXPECT_TRUE(output.has_preedit());
2759     EXPECT_TRUE(output.has_candidates());
2760 
2761     const commands::Preedit &conversion = output.preedit();
2762     EXPECT_EQ(1, conversion.segment_size());
2763     EXPECT_EQ(kChars_Mozuku, conversion.segment(0).value());
2764   }
2765 
2766   // Meaningless segment manipulations.
2767   converter.SegmentWidthShrink(*composer_);
2768   converter.SegmentFocusLeft();
2769   converter.SegmentFocusLast();
2770 
2771   {  // Check the conversion again
2772     commands::Output output;
2773     converter.FillOutput(*composer_, &output);
2774     EXPECT_FALSE(output.has_result());
2775     EXPECT_TRUE(output.has_preedit());
2776     EXPECT_FALSE(output.has_candidates());
2777 
2778     const commands::Preedit &conversion = output.preedit();
2779     EXPECT_EQ(1, conversion.segment_size());
2780     EXPECT_EQ(kChars_Mozuku, conversion.segment(0).value());
2781   }
2782 }
2783 
TEST_F(SessionConverterTest,Issue1981020)2784 TEST_F(SessionConverterTest, Issue1981020) {
2785   SessionConverter converter(
2786       convertermock_.get(), request_.get(), config_.get());
2787   // "〜〜〜〜" U+301C * 4
2788   const string wave_dash_301c = "〜〜〜〜";
2789   composer_->InsertCharacterPreedit(wave_dash_301c);
2790   Segments segments;
2791   convertermock_->SetFinishConversion(&segments, true);
2792   converter.CommitPreedit(*composer_, Context::default_instance());
2793   convertermock_->GetFinishConversion(&segments);
2794 
2795 #ifdef OS_WIN
2796   // "~~~~" U+FF5E * 4
2797   const string fullwidth_tilde_ff5e = "~~~~";
2798   EXPECT_EQ(fullwidth_tilde_ff5e,
2799             segments.conversion_segment(0).candidate(0).value);
2800   EXPECT_EQ(fullwidth_tilde_ff5e,
2801             segments.conversion_segment(0).candidate(0).content_value);
2802 #else  // OS_WIN
2803   EXPECT_EQ(wave_dash_301c,
2804             segments.conversion_segment(0).candidate(0).value);
2805   EXPECT_EQ(wave_dash_301c,
2806             segments.conversion_segment(0).candidate(0).content_value);
2807 #endif  // OS_WIN
2808 }
2809 
TEST_F(SessionConverterTest,Issue2029557)2810 TEST_F(SessionConverterTest, Issue2029557) {
2811   // Unittest against http://b/2029557
2812   // a<tab><F6> raised a DCHECK error.
2813   SessionConverter converter(
2814       convertermock_.get(), request_.get(), config_.get());
2815   // Composition (as "a")
2816   composer_->InsertCharacterPreedit("a");
2817 
2818   // Prediction (as <tab>)
2819   Segments segments;
2820   SetAiueo(&segments);
2821   convertermock_->SetStartPredictionForRequest(&segments, true);
2822   EXPECT_TRUE(converter.Predict(*composer_));
2823   EXPECT_TRUE(converter.IsActive());
2824 
2825   // Transliteration (as <F6>)
2826   segments.Clear();
2827   Segment *segment = segments.add_segment();
2828   segment->set_key("a");
2829   Segment::Candidate *candidate = segment->add_candidate();
2830   candidate->value = "a";
2831 
2832   FillT13Ns(&segments, composer_.get());
2833   convertermock_->SetStartConversionForRequest(&segments, true);
2834   EXPECT_TRUE(converter.ConvertToTransliteration(*composer_,
2835                                                  transliteration::HIRAGANA));
2836   EXPECT_TRUE(converter.IsActive());
2837 }
2838 
TEST_F(SessionConverterTest,Issue2031986)2839 TEST_F(SessionConverterTest, Issue2031986) {
2840   // Unittest against http://b/2031986
2841   // aaaaa<Shift+Enter> raised a CRT error.
2842   SessionConverter converter(
2843       convertermock_.get(), request_.get(), config_.get());
2844 
2845   {  // Initialize a suggest result triggered by "aaaa".
2846     Segments segments;
2847     Segment *segment = segments.add_segment();
2848     segment->set_key("aaaa");
2849     Segment::Candidate *candidate;
2850     candidate = segment->add_candidate();
2851     candidate->value = "AAAA";
2852     candidate = segment->add_candidate();
2853     candidate->value = "Aaaa";
2854     convertermock_->SetStartSuggestionForRequest(&segments, true);
2855   }
2856   // Get suggestion
2857   composer_->InsertCharacterPreedit("aaaa");
2858   EXPECT_TRUE(converter.Suggest(*composer_));
2859   EXPECT_TRUE(converter.IsActive());
2860 
2861   {  // Initialize no suggest result triggered by "aaaaa".
2862     Segments segments;
2863     Segment *segment = segments.add_segment();
2864     segment->set_key("aaaaa");
2865     convertermock_->SetStartSuggestionForRequest(&segments, false);
2866   }
2867   // Hide suggestion
2868   composer_->InsertCharacterPreedit("a");
2869   EXPECT_FALSE(converter.Suggest(*composer_));
2870   EXPECT_FALSE(converter.IsActive());
2871 }
2872 
TEST_F(SessionConverterTest,Issue2040116)2873 TEST_F(SessionConverterTest, Issue2040116) {
2874   // Unittest against http://b/2040116
2875   //
2876   // It happens when the first Predict returns results but the next
2877   // MaybeExpandPrediction does not return any results.  That's a
2878   // trick by GoogleSuggest.
2879   SessionConverter converter(
2880       convertermock_.get(), request_.get(), config_.get());
2881   composer_->InsertCharacterPreedit("G");
2882 
2883   {
2884     // Initialize no predict result.
2885     Segments segments;
2886     segments.set_request_type(Segments::PREDICTION);
2887     Segment *segment = segments.add_segment();
2888     segment->set_key("G");
2889     convertermock_->SetStartPredictionForRequest(&segments, false);
2890   }
2891   // Get prediction
2892   EXPECT_FALSE(converter.Predict(*composer_));
2893   EXPECT_FALSE(converter.IsActive());
2894 
2895   {
2896     // Initialize a suggest result triggered by "G".
2897     Segments segments;
2898     segments.set_request_type(Segments::PREDICTION);
2899     Segment *segment = segments.add_segment();
2900     segment->set_key("G");
2901     Segment::Candidate *candidate;
2902     candidate = segment->add_candidate();
2903     candidate->value = "GoogleSuggest";
2904     convertermock_->SetStartPredictionForRequest(&segments, true);
2905   }
2906   // Get prediction again
2907   EXPECT_TRUE(converter.Predict(*composer_));
2908   EXPECT_TRUE(converter.IsActive());
2909 
2910   {  // Check the conversion.
2911     commands::Output output;
2912     converter.FillOutput(*composer_, &output);
2913     EXPECT_FALSE(output.has_result());
2914     EXPECT_TRUE(output.has_preedit());
2915     EXPECT_TRUE(output.has_candidates());
2916 
2917     const commands::Preedit &conversion = output.preedit();
2918     EXPECT_EQ(1, conversion.segment_size());
2919     EXPECT_EQ("GoogleSuggest", conversion.segment(0).value());
2920   }
2921 
2922   {
2923     // Initialize no predict result triggered by "G".  It's possible
2924     // by Google Suggest.
2925     Segments segments;
2926     segments.set_request_type(Segments::PREDICTION);
2927     Segment *segment = segments.add_segment();
2928     segment->set_key("G");
2929     convertermock_->SetStartPredictionForRequest(&segments, false);
2930   }
2931   // Hide prediction
2932   converter.CandidateNext(*composer_);
2933   EXPECT_TRUE(converter.IsActive());
2934 
2935   {  // Check the conversion.
2936     commands::Output output;
2937     converter.FillOutput(*composer_, &output);
2938     EXPECT_FALSE(output.has_result());
2939     EXPECT_TRUE(output.has_preedit());
2940     EXPECT_TRUE(output.has_candidates());
2941 
2942     const commands::Preedit &conversion = output.preedit();
2943     EXPECT_EQ(1, conversion.segment_size());
2944     EXPECT_EQ("GoogleSuggest", conversion.segment(0).value());
2945 
2946     const commands::Candidates &candidates = output.candidates();
2947     EXPECT_EQ(1, candidates.candidate_size());
2948   }
2949 }
2950 
TEST_F(SessionConverterTest,GetReadingText)2951 TEST_F(SessionConverterTest, GetReadingText) {
2952   SessionConverter converter(
2953       convertermock_.get(), request_.get(), config_.get());
2954 
2955   const char *kKanjiAiueo = "阿伊宇江於";
2956   // Set up Segments for reverse conversion.
2957   Segments reverse_segments;
2958   Segment *segment;
2959   segment = reverse_segments.add_segment();
2960   segment->set_key(kKanjiAiueo);
2961   Segment::Candidate *candidate;
2962   candidate = segment->add_candidate();
2963   // For reverse conversion, key is the original kanji string.
2964   candidate->key = kKanjiAiueo;
2965   candidate->value = kChars_Aiueo;
2966   convertermock_->SetStartReverseConversion(&reverse_segments, true);
2967   // Set up Segments for forward conversion.
2968   Segments segments;
2969   segment = segments.add_segment();
2970   segment->set_key(kChars_Aiueo);
2971   candidate = segment->add_candidate();
2972   candidate->key = kChars_Aiueo;
2973   candidate->value = kKanjiAiueo;
2974   convertermock_->SetStartConversionForRequest(&segments, true);
2975 
2976   string reading;
2977   EXPECT_TRUE(converter.GetReadingText(kKanjiAiueo, &reading));
2978   EXPECT_EQ(kChars_Aiueo, reading);
2979 }
2980 
TEST_F(SessionConverterTest,ZeroQuerySuggestion)2981 TEST_F(SessionConverterTest, ZeroQuerySuggestion) {
2982   SessionConverter converter(
2983       convertermock_.get(), request_.get(), config_.get());
2984 
2985   // Set up a mock suggestion result.
2986   Segments segments;
2987   segments.set_request_type(Segments::SUGGESTION);
2988   Segment *segment;
2989   segment = segments.add_segment();
2990   segment->set_key("");
2991   segment->add_candidate()->value = "search";
2992   segment->add_candidate()->value = "input";
2993   convertermock_->SetStartSuggestionForRequest(&segments, true);
2994 
2995   EXPECT_TRUE(composer_->Empty());
2996   EXPECT_TRUE(converter.Suggest(*composer_));
2997   EXPECT_TRUE(IsCandidateListVisible(converter));
2998   EXPECT_TRUE(converter.IsActive());
2999 
3000   {  // Check the output
3001     commands::Output output;
3002     converter.FillOutput(*composer_, &output);
3003     EXPECT_FALSE(output.has_result());
3004     EXPECT_FALSE(output.has_preedit());
3005     EXPECT_TRUE(output.has_candidates());
3006 
3007     const commands::Candidates &candidates = output.candidates();
3008     EXPECT_EQ(2, candidates.size());
3009     EXPECT_EQ("search", candidates.candidate(0).value());
3010     EXPECT_EQ("input", candidates.candidate(1).value());
3011   }
3012 }
3013 
3014 // since History segments are almost hidden from
3015 namespace {
3016 
3017 class ConverterMockForReset : public ConverterMock {
3018  public:
ConverterMockForReset()3019   ConverterMockForReset() : reset_conversion_called_(false) {}
3020 
ResetConversion(Segments * segments) const3021   bool ResetConversion(Segments *segments) const override {
3022     reset_conversion_called_ = true;
3023     return true;
3024   }
3025 
reset_conversion_called() const3026   bool reset_conversion_called() const {
3027     return reset_conversion_called_;
3028   }
3029 
Reset()3030   void Reset() {
3031     reset_conversion_called_ = false;
3032   }
3033 
3034  private:
3035   mutable bool reset_conversion_called_;
3036 };
3037 
3038 class ConverterMockForRevert : public ConverterMock {
3039  public:
ConverterMockForRevert()3040   ConverterMockForRevert() : revert_conversion_called_(false) {}
3041 
RevertConversion(Segments * segments) const3042   bool RevertConversion(Segments *segments) const override {
3043     revert_conversion_called_ = true;
3044     return true;
3045   }
3046 
revert_conversion_called() const3047   bool revert_conversion_called() const {
3048     return revert_conversion_called_;
3049   }
3050 
Reset()3051   void Reset() {
3052     revert_conversion_called_ = false;
3053   }
3054 
3055  private:
3056   mutable bool revert_conversion_called_;
3057 };
3058 
3059 class ConverterMockForReconstructHistory : public ConverterMock {
3060  public:
ConverterMockForReconstructHistory()3061   ConverterMockForReconstructHistory() : reconstruct_history_called_(false) {}
3062 
ReconstructHistory(Segments * segments,const string & preceding_text) const3063   bool ReconstructHistory(Segments *segments,
3064                           const string &preceding_text) const override {
3065     reconstruct_history_called_ = true;
3066     preceding_text_ = preceding_text;
3067     ConverterMock::ReconstructHistory(segments, preceding_text);
3068     return true;
3069   }
3070 
reconstruct_history_called() const3071   bool reconstruct_history_called() const {
3072     return reconstruct_history_called_;
3073   }
3074 
preceding_text() const3075   const string preceding_text() const {
3076     return preceding_text_;
3077   }
3078 
Reset()3079   void Reset() {
3080     reconstruct_history_called_ = false;
3081     preceding_text_.clear();
3082   }
3083 
3084  private:
3085   mutable bool reconstruct_history_called_;
3086   mutable string preceding_text_;
3087 };
3088 
3089 }  // namespace
3090 
TEST(SessionConverterResetTest,Reset)3091 TEST(SessionConverterResetTest, Reset) {
3092   ConverterMockForReset convertermock;
3093   Request request;
3094   Config config;
3095   SessionConverter converter(&convertermock, &request, &config);
3096   EXPECT_FALSE(convertermock.reset_conversion_called());
3097   converter.Reset();
3098   EXPECT_TRUE(convertermock.reset_conversion_called());
3099 }
3100 
TEST(SessionConverterRevertTest,Revert)3101 TEST(SessionConverterRevertTest, Revert) {
3102   ConverterMockForRevert convertermock;
3103   Request request;
3104   Config config;
3105   SessionConverter converter(&convertermock, &request, &config);
3106   EXPECT_FALSE(convertermock.revert_conversion_called());
3107   converter.Revert();
3108   EXPECT_TRUE(convertermock.revert_conversion_called());
3109 }
3110 
TEST_F(SessionConverterTest,CommitHead)3111 TEST_F(SessionConverterTest, CommitHead) {
3112   SessionConverter converter(
3113       convertermock_.get(), request_.get(), config_.get());
3114   composer_->InsertCharacterPreedit(kChars_Aiueo);
3115 
3116   size_t committed_size;
3117   converter.CommitHead(1, *composer_, &committed_size);
3118   EXPECT_EQ(1, committed_size);
3119   composer_->DeleteAt(0);
3120 
3121   commands::Output output;
3122   converter.FillOutput(*composer_, &output);
3123   EXPECT_TRUE(output.has_result());
3124   EXPECT_FALSE(output.has_candidates());
3125 
3126   const commands::Result &result = output.result();
3127   EXPECT_EQ("あ", result.value());
3128   EXPECT_EQ("あ", result.key());
3129   string preedit;
3130   composer_->GetStringForPreedit(&preedit);
3131   EXPECT_EQ("いうえお", preedit);
3132 
3133   converter.CommitHead(3, *composer_, &committed_size);
3134   EXPECT_EQ(3, committed_size);
3135   composer_->DeleteAt(0);
3136   composer_->DeleteAt(0);
3137   composer_->DeleteAt(0);
3138   converter.FillOutput(*composer_, &output);
3139   EXPECT_TRUE(output.has_result());
3140   EXPECT_FALSE(output.has_candidates());
3141 
3142   const commands::Result &result2 = output.result();
3143   EXPECT_EQ("いうえ", result2.value());
3144   EXPECT_EQ("いうえ", result2.key());
3145   composer_->GetStringForPreedit(&preedit);
3146   EXPECT_EQ("お", preedit);
3147 
3148   EXPECT_STATS_NOT_EXIST("Commit");
3149   EXPECT_STATS_NOT_EXIST("CommitFromComposition");
3150 }
3151 
TEST_F(SessionConverterTest,CommandCandidate)3152 TEST_F(SessionConverterTest, CommandCandidate) {
3153   SessionConverter converter(
3154       convertermock_.get(), request_.get(), config_.get());
3155   Segments segments;
3156   SetAiueo(&segments);
3157   FillT13Ns(&segments, composer_.get());
3158   // set COMMAND_CANDIDATE.
3159   SetCommandCandidate(&segments, 0, 0, Segment::Candidate::DEFAULT_COMMAND);
3160   convertermock_->SetStartConversionForRequest(&segments, true);
3161 
3162   composer_->InsertCharacterPreedit(kChars_Aiueo);
3163   EXPECT_TRUE(converter.Convert(*composer_));
3164 
3165   converter.Commit(*composer_, Context::default_instance());
3166   commands::Output output;
3167   converter.FillOutput(*composer_, &output);
3168   EXPECT_FALSE(output.has_result());
3169 }
3170 
TEST_F(SessionConverterTest,CommandCandidateWithCommitCommands)3171 TEST_F(SessionConverterTest, CommandCandidateWithCommitCommands) {
3172   const string kKamabokono = "かまぼこの";
3173   const string kInbou = "いんぼう";
3174   composer_->InsertCharacterPreedit(kKamabokono + kInbou);
3175 
3176   {
3177     // The first candidate is a command candidate, so
3178     // CommitFirstSegment resets all conversion.
3179     SessionConverter converter(
3180         convertermock_.get(), request_.get(), config_.get());
3181     Segments segments;
3182     SetKamaboko(&segments);
3183     SetCommandCandidate(&segments, 0, 0,
3184                         Segment::Candidate::DEFAULT_COMMAND);
3185     convertermock_->SetStartConversionForRequest(&segments, true);
3186     converter.Convert(*composer_);
3187 
3188     size_t committed_size = 0;
3189     converter.CommitFirstSegment(*composer_,
3190                                  Context::default_instance(),
3191                                  &committed_size);
3192     EXPECT_EQ(0, committed_size);
3193 
3194     commands::Output output;
3195     converter.FillOutput(*composer_, &output);
3196     EXPECT_FALSE(converter.IsActive());
3197     EXPECT_FALSE(output.has_result());
3198   }
3199 
3200   {
3201     // The second candidate is a command candidate, so
3202     // CommitFirstSegment commits all conversion.
3203     SessionConverter converter(
3204         convertermock_.get(), request_.get(), config_.get());
3205     Segments segments;
3206     SetKamaboko(&segments);
3207     SetCommandCandidate(&segments, 1, 0,
3208                         Segment::Candidate::DEFAULT_COMMAND);
3209     convertermock_->SetStartConversionForRequest(&segments, true);
3210     converter.Convert(*composer_);
3211 
3212     size_t committed_size = 0;
3213     converter.CommitFirstSegment(*composer_,
3214                                  Context::default_instance(),
3215                                  &committed_size);
3216     EXPECT_EQ(Util::CharsLen(kKamabokono), committed_size);
3217 
3218     commands::Output output;
3219     converter.FillOutput(*composer_, &output);
3220     EXPECT_TRUE(converter.IsActive());
3221     EXPECT_TRUE(output.has_result());
3222   }
3223 
3224   {
3225     // The selected suggestion with Id is a command candidate.
3226     SessionConverter converter(
3227         convertermock_.get(), request_.get(), config_.get());
3228     Segments segments;
3229     SetAiueo(&segments);
3230     SetCommandCandidate(&segments, 0, 0,
3231                         Segment::Candidate::DEFAULT_COMMAND);
3232     convertermock_->SetStartSuggestionForRequest(&segments, true);
3233     converter.Suggest(*composer_);
3234 
3235     size_t committed_size = 0;
3236     EXPECT_FALSE(converter.CommitSuggestionById(
3237         0, *composer_, Context::default_instance(), &committed_size));
3238     EXPECT_EQ(0, committed_size);
3239   }
3240 
3241   {
3242     // The selected suggestion with Index is a command candidate.
3243     SessionConverter converter(
3244         convertermock_.get(), request_.get(), config_.get());
3245     Segments segments;
3246     SetAiueo(&segments);
3247     SetCommandCandidate(&segments, 0, 1,
3248                         Segment::Candidate::DEFAULT_COMMAND);
3249     convertermock_->SetStartSuggestionForRequest(&segments, true);
3250     converter.Suggest(*composer_);
3251 
3252     size_t committed_size = 0;
3253     EXPECT_FALSE(converter.CommitSuggestionByIndex(
3254         1, *composer_, Context::default_instance(), &committed_size));
3255     EXPECT_EQ(0, committed_size);
3256   }
3257 }
3258 
TEST_F(SessionConverterTest,ExecuteCommandCandidate)3259 TEST_F(SessionConverterTest, ExecuteCommandCandidate) {
3260   // Enable Incognito mode
3261   {
3262     config_->set_incognito_mode(false);
3263     SessionConverter converter(
3264         convertermock_.get(), request_.get(), config_.get());
3265     Segments segments;
3266     SetAiueo(&segments);
3267     SetCommandCandidate(&segments, 0, 0,
3268                         Segment::Candidate::ENABLE_INCOGNITO_MODE);
3269     convertermock_->SetStartConversionForRequest(&segments, true);
3270 
3271     composer_->InsertCharacterPreedit(kChars_Aiueo);
3272     EXPECT_TRUE(converter.Convert(*composer_));
3273 
3274     converter.Commit(*composer_, Context::default_instance());
3275     commands::Output output;
3276     converter.FillOutput(*composer_, &output);
3277     EXPECT_FALSE(output.has_result());
3278 
3279     // The config in the |output| has the updated value, but |config_| keeps
3280     // the previous value.
3281     EXPECT_TRUE(output.has_config());
3282     EXPECT_TRUE(output.config().incognito_mode());
3283     EXPECT_FALSE(config_->incognito_mode());
3284   }
3285 
3286   // Disable Incognito mode
3287   {
3288     config_->set_incognito_mode(false);
3289     SessionConverter converter(
3290         convertermock_.get(), request_.get(), config_.get());
3291     Segments segments;
3292     SetAiueo(&segments);
3293     SetCommandCandidate(&segments, 0, 0,
3294                         Segment::Candidate::DISABLE_INCOGNITO_MODE);
3295     convertermock_->SetStartConversionForRequest(&segments, true);
3296 
3297     composer_->InsertCharacterPreedit(kChars_Aiueo);
3298     EXPECT_TRUE(converter.Convert(*composer_));
3299 
3300     converter.Commit(*composer_, Context::default_instance());
3301     commands::Output output;
3302     converter.FillOutput(*composer_, &output);
3303     EXPECT_FALSE(output.has_result());
3304 
3305     // The config in the |output| has the updated value, but |config_| keeps
3306     // the previous value.
3307     EXPECT_TRUE(output.has_config());
3308     EXPECT_FALSE(output.config().incognito_mode());
3309     EXPECT_FALSE(config_->incognito_mode());
3310   }
3311 
3312   // Enable Presentation mode
3313   {
3314     config_->set_presentation_mode(false);
3315     SessionConverter converter(
3316         convertermock_.get(), request_.get(), config_.get());
3317 
3318     Segments segments;
3319     SetAiueo(&segments);
3320     SetCommandCandidate(&segments, 0, 0,
3321                         Segment::Candidate::ENABLE_PRESENTATION_MODE);
3322     convertermock_->SetStartConversionForRequest(&segments, true);
3323 
3324     composer_->InsertCharacterPreedit(kChars_Aiueo);
3325     EXPECT_TRUE(converter.Convert(*composer_));
3326 
3327     converter.Commit(*composer_, Context::default_instance());
3328     commands::Output output;
3329     converter.FillOutput(*composer_, &output);
3330     EXPECT_FALSE(output.has_result());
3331 
3332     // The config in the |output| has the updated value, but |config_| keeps
3333     // the previous value.
3334     EXPECT_TRUE(output.has_config());
3335     EXPECT_TRUE(output.config().presentation_mode());
3336     EXPECT_FALSE(config_->presentation_mode());
3337   }
3338 
3339   // Disable Presentation mode
3340   {
3341     config_->set_incognito_mode(true);
3342     SessionConverter converter(
3343         convertermock_.get(), request_.get(), config_.get());
3344 
3345     Segments segments;
3346     SetAiueo(&segments);
3347     SetCommandCandidate(&segments, 0, 0,
3348                         Segment::Candidate::DISABLE_PRESENTATION_MODE);
3349     convertermock_->SetStartConversionForRequest(&segments, true);
3350 
3351     composer_->InsertCharacterPreedit(kChars_Aiueo);
3352     EXPECT_TRUE(converter.Convert(*composer_));
3353 
3354     converter.Commit(*composer_, Context::default_instance());
3355     commands::Output output;
3356     converter.FillOutput(*composer_, &output);
3357     EXPECT_FALSE(output.has_result());
3358 
3359     // The config in the |output| has the updated value, but |config_| keeps
3360     // the previous value.
3361     EXPECT_TRUE(output.has_config());
3362     EXPECT_FALSE(output.config().presentation_mode());
3363     EXPECT_FALSE(config_->presentation_mode());
3364   }
3365 }
3366 
TEST_F(SessionConverterTest,PropageteConfigToRenderer)3367 TEST_F(SessionConverterTest, PropageteConfigToRenderer) {
3368   // Disable information_list_config()
3369   {
3370     SessionConverter converter(
3371         convertermock_.get(), request_.get(), config_.get());
3372     Segments segments;
3373     SetAiueo(&segments);
3374     FillT13Ns(&segments, composer_.get());
3375     convertermock_->SetStartConversionForRequest(&segments, true);
3376 
3377     commands::Output output;
3378     composer_->InsertCharacterPreedit(kChars_Aiueo);
3379     converter.Convert(*composer_);
3380 
3381     EXPECT_FALSE(IsCandidateListVisible(converter));
3382     output.Clear();
3383     converter.FillOutput(*composer_, &output);
3384     EXPECT_FALSE(output.has_config());
3385 
3386     converter.CandidateNext(*composer_);
3387     EXPECT_TRUE(IsCandidateListVisible(converter));
3388     output.Clear();
3389     converter.FillOutput(*composer_, &output);
3390     EXPECT_FALSE(output.has_config());
3391   }
3392 }
3393 
TEST_F(SessionConverterTest,ConversionFail)3394 TEST_F(SessionConverterTest, ConversionFail) {
3395   SessionConverter converter(
3396       convertermock_.get(), request_.get(), config_.get());
3397 
3398   // Conversion fails.
3399   {
3400     // segments doesn't have any candidates.
3401     Segments segments;
3402     segments.add_segment()->set_key(kChars_Aiueo);
3403     convertermock_->SetStartConversionForRequest(&segments, false);
3404     composer_->InsertCharacterPreedit(kChars_Aiueo);
3405 
3406     // Falls back to composition state.
3407     EXPECT_FALSE(converter.Convert(*composer_));
3408     EXPECT_FALSE(IsCandidateListVisible(converter));
3409     EXPECT_TRUE(converter.CheckState(SessionConverterInterface::COMPOSITION));
3410 
3411     commands::Output output;
3412     converter.FillOutput(*composer_, &output);
3413     EXPECT_FALSE(output.has_result());
3414     EXPECT_TRUE(output.has_preedit());
3415     EXPECT_FALSE(output.has_candidates());
3416     EXPECT_FALSE(IsCandidateListVisible(converter));
3417   }
3418 
3419   composer_->Reset();
3420 
3421   // Suggestion succeeds and conversion fails.
3422   {
3423     Segments segments;
3424     SetAiueo(&segments);
3425     convertermock_->SetStartSuggestionForRequest(&segments, true);
3426     composer_->InsertCharacterPreedit(kChars_Aiueo);
3427 
3428     EXPECT_TRUE(converter.Suggest(*composer_));
3429     EXPECT_TRUE(IsCandidateListVisible(converter));
3430     EXPECT_TRUE(converter.CheckState(SessionConverterInterface::SUGGESTION));
3431 
3432     commands::Output output;
3433     converter.FillOutput(*composer_, &output);
3434     EXPECT_FALSE(output.has_result());
3435     EXPECT_TRUE(output.has_preedit());
3436     EXPECT_TRUE(output.has_candidates());
3437 
3438     segments.Clear();
3439     output.Clear();
3440 
3441     // segments doesn't have any candidates.
3442     segments.add_segment()->set_key(kChars_Aiueo);
3443     convertermock_->SetStartConversionForRequest(&segments, false);
3444 
3445     // Falls back to composition state.
3446     EXPECT_FALSE(converter.Convert(*composer_));
3447     EXPECT_FALSE(IsCandidateListVisible(converter));
3448     EXPECT_TRUE(converter.CheckState(SessionConverterInterface::COMPOSITION));
3449 
3450     converter.FillOutput(*composer_, &output);
3451     EXPECT_FALSE(output.has_result());
3452     EXPECT_TRUE(output.has_preedit());
3453     EXPECT_FALSE(output.has_candidates());
3454   }
3455 }
3456 
TEST_F(SessionConverterTest,ResetByClientRevision)3457 TEST_F(SessionConverterTest, ResetByClientRevision) {
3458   const int32 kRevision = 0x1234;
3459 
3460   ConverterMockForReset convertermock;
3461   SessionConverter converter(&convertermock, request_.get(), config_.get());
3462   Context context;
3463 
3464   // Initialize the session converter with given context age.
3465   context.set_revision(kRevision);
3466   converter.OnStartComposition(context);
3467   converter.Revert();
3468 
3469   convertermock.Reset();
3470   EXPECT_FALSE(convertermock.reset_conversion_called());
3471 
3472   // OnStartComposition with different context age causes Reset()
3473   context.set_revision(kRevision + 1);
3474   converter.OnStartComposition(context);
3475   EXPECT_TRUE(convertermock.reset_conversion_called());
3476 }
3477 
TEST_F(SessionConverterTest,ResetByPrecedingText)3478 TEST_F(SessionConverterTest, ResetByPrecedingText) {
3479   ConverterMockForReset convertermock;
3480   SessionConverter converter(&convertermock, request_.get(), config_.get());
3481 
3482   // no preceding_text -> Reset should not be called.
3483   {
3484     convertermock.Reset();
3485     Segments segments;
3486     SetAiueo(&segments);
3487     FillT13Ns(&segments, composer_.get());
3488     for (size_t i = 0; i < segments.segments_size(); ++i) {
3489       Segment *segment = segments.mutable_segment(i);
3490       segment->set_segment_type(Segment::HISTORY);
3491     }
3492     SetSegments(segments, &converter);
3493     converter.OnStartComposition(Context::default_instance());
3494     EXPECT_FALSE(convertermock.reset_conversion_called());
3495     converter.Revert();
3496   }
3497 
3498   // preceding_text == history_segments -> Reset should not be called.
3499   {
3500     convertermock.Reset();
3501     Segments segments;
3502     SetAiueo(&segments);
3503     FillT13Ns(&segments, composer_.get());
3504     for (size_t i = 0; i < segments.segments_size(); ++i) {
3505       Segment *segment = segments.mutable_segment(i);
3506       segment->set_segment_type(Segment::HISTORY);
3507     }
3508     SetSegments(segments, &converter);
3509     Context context;
3510     context.set_preceding_text(kChars_Aiueo);
3511     converter.OnStartComposition(context);
3512     EXPECT_FALSE(convertermock.reset_conversion_called());
3513     converter.Revert();
3514   }
3515 
3516   // preceding_text == "" && history_segments != "" -> Reset should be called.
3517   {
3518     convertermock.Reset();
3519     Segments segments;
3520     SetAiueo(&segments);
3521     FillT13Ns(&segments, composer_.get());
3522     for (size_t i = 0; i < segments.segments_size(); ++i) {
3523       Segment *segment = segments.mutable_segment(i);
3524       segment->set_segment_type(Segment::HISTORY);
3525     }
3526     SetSegments(segments, &converter);
3527     Context context;
3528     context.set_preceding_text("");
3529     converter.OnStartComposition(context);
3530     EXPECT_TRUE(convertermock.reset_conversion_called());
3531     converter.Revert();
3532   }
3533 
3534   // preceding_text != "" && preceding_text.EndsWith(history_segments).
3535   //    -> Reset should not be called.
3536   {
3537     convertermock.Reset();
3538     Segments segments;
3539     SetAiueo(&segments);
3540     FillT13Ns(&segments, composer_.get());
3541     for (size_t i = 0; i < segments.segments_size(); ++i) {
3542       Segment *segment = segments.mutable_segment(i);
3543       segment->set_segment_type(Segment::HISTORY);
3544     }
3545     SetSegments(segments, &converter);
3546     Context context;
3547     context.set_preceding_text(kChars_Aiueo);
3548     converter.OnStartComposition(context);
3549     EXPECT_FALSE(convertermock.reset_conversion_called());
3550     converter.Revert();
3551   }
3552 
3553   // preceding_text != "" && history_segments.EndsWith(preceding_text).
3554   //    -> Reset should not be called.
3555   {
3556     convertermock.Reset();
3557     Segments segments;
3558     SetAiueo(&segments);
3559     FillT13Ns(&segments, composer_.get());
3560     for (size_t i = 0; i < segments.segments_size(); ++i) {
3561       Segment *segment = segments.mutable_segment(i);
3562       segment->set_segment_type(Segment::HISTORY);
3563     }
3564     SetSegments(segments, &converter);
3565     Context context;
3566     context.set_preceding_text(kChars_Aiueo);
3567     converter.OnStartComposition(context);
3568     EXPECT_FALSE(convertermock.reset_conversion_called());
3569     converter.Revert();
3570   }
3571 }
3572 
TEST_F(SessionConverterTest,ReconstructHistoryByPrecedingText)3573 TEST_F(SessionConverterTest, ReconstructHistoryByPrecedingText) {
3574   ConverterMockForReconstructHistory convertermock;
3575 
3576   const uint16 kId = 1234;
3577   const char kKey[] = "1";
3578   const char kValue[] = "1";
3579 
3580   // Set up mock
3581   Segments mock_result;
3582   {
3583     Segment *segment = mock_result.add_segment();
3584     segment->set_key(kKey);
3585     segment->set_segment_type(Segment::HISTORY);
3586     Segment::Candidate *candidate = segment->push_back_candidate();
3587     candidate->rid = kId;
3588     candidate->lid = kId;
3589     candidate->content_key = kKey;
3590     candidate->key = kKey;
3591     candidate->content_value = kValue;
3592     candidate->value = kValue;
3593     candidate->attributes = Segment::Candidate::NO_LEARNING;
3594   }
3595   convertermock.SetReconstructHistory(&mock_result, true);
3596 
3597   // With revision
3598   {
3599     SessionConverter converter(&convertermock, request_.get(), config_.get());
3600 
3601     Context context;
3602     context.set_revision(0);
3603     context.set_preceding_text(kKey);
3604     EXPECT_FALSE(convertermock.reconstruct_history_called());
3605     converter.OnStartComposition(context);
3606     EXPECT_TRUE(convertermock.reconstruct_history_called());
3607     EXPECT_EQ(kKey, convertermock.preceding_text());
3608 
3609     // History segments should be reconstructed.
3610     Segments segments;
3611     GetSegments(converter, &segments);
3612     EXPECT_EQ(1, segments.segments_size());
3613     const Segment &segment = segments.segment(0);
3614     EXPECT_EQ(Segment::HISTORY, segment.segment_type());
3615     EXPECT_EQ(kKey, segment.key());
3616     EXPECT_EQ(1, segment.candidates_size());
3617     const Segment::Candidate &candidate = segment.candidate(0);
3618     EXPECT_EQ(Segment::Candidate::NO_LEARNING, candidate.attributes);
3619     EXPECT_EQ(kKey, candidate.content_key);
3620     EXPECT_EQ(kKey, candidate.key);
3621     EXPECT_EQ(kValue, candidate.content_value);
3622     EXPECT_EQ(kValue, candidate.value);
3623     EXPECT_EQ(kId, candidate.lid);
3624     EXPECT_EQ(kId, candidate.rid);
3625 
3626     convertermock.Reset();
3627     convertermock.SetReconstructHistory(&mock_result, true);
3628 
3629     // Increment revesion
3630     context.set_revision(1);
3631     context.set_preceding_text(kKey);
3632     EXPECT_FALSE(convertermock.reconstruct_history_called());
3633     converter.OnStartComposition(context);
3634     EXPECT_FALSE(convertermock.reconstruct_history_called())
3635         << "ReconstructHistory should not be called as long as the "
3636         << "history segments are consistent with the preceding text "
3637         << "even when the revision number is changed.";
3638   }
3639 
3640   convertermock.Reset();
3641   convertermock.SetReconstructHistory(&mock_result, true);
3642 
3643   // Without revision
3644   {
3645     SessionConverter converter(&convertermock, request_.get(), config_.get());
3646 
3647     Context context;
3648     context.set_preceding_text(kKey);
3649     EXPECT_FALSE(convertermock.reconstruct_history_called());
3650     converter.OnStartComposition(context);
3651     EXPECT_TRUE(convertermock.reconstruct_history_called());
3652     EXPECT_EQ(kKey, convertermock.preceding_text());
3653 
3654     // History segments should be reconstructed.
3655     Segments segments;
3656     GetSegments(converter, &segments);
3657     EXPECT_EQ(1, segments.segments_size());
3658     const Segment &segment = segments.segment(0);
3659     EXPECT_EQ(Segment::HISTORY, segment.segment_type());
3660     EXPECT_EQ(kKey, segment.key());
3661     EXPECT_EQ(1, segment.candidates_size());
3662     const Segment::Candidate &candidate = segment.candidate(0);
3663     EXPECT_EQ(Segment::Candidate::NO_LEARNING, candidate.attributes);
3664     EXPECT_EQ(kKey, candidate.content_key);
3665     EXPECT_EQ(kKey, candidate.key);
3666     EXPECT_EQ(kValue, candidate.content_value);
3667     EXPECT_EQ(kValue, candidate.value);
3668     EXPECT_EQ(kId, candidate.lid);
3669     EXPECT_EQ(kId, candidate.rid);
3670 
3671     convertermock.Reset();
3672     convertermock.SetReconstructHistory(&mock_result, true);
3673 
3674     context.set_preceding_text(kKey);
3675     EXPECT_FALSE(convertermock.reconstruct_history_called());
3676     converter.OnStartComposition(context);
3677     EXPECT_FALSE(convertermock.reconstruct_history_called())
3678         << "ReconstructHistory should not be called as long as the "
3679         << "history segments are consistent with the preceding text "
3680         << "even when the revision number is changed.";
3681   }
3682 }
3683 
3684 // Test whether Request::candidate_page_size is correctly propagated to
3685 // CandidateList.page_size in SessionConverter.  The tests for the behavior
3686 // of CandidateList.page_size is in session/internal/candidate_list_test.cc
TEST_F(SessionConverterTest,CandidatePageSize)3687 TEST_F(SessionConverterTest, CandidatePageSize) {
3688   const size_t kPageSize = 3;
3689   request_->set_candidate_page_size(kPageSize);
3690   SessionConverter converter(
3691       convertermock_.get(), request_.get(), config_.get());
3692   EXPECT_EQ(kPageSize, GetCandidateList(converter).page_size());
3693 }
3694 
3695 }  // namespace session
3696 }  // namespace mozc
3697