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 #include "session/session.h"
31 
32 #include <memory>
33 #include <string>
34 #include <vector>
35 
36 #include "base/logging.h"
37 #include "base/util.h"
38 #include "composer/composer.h"
39 #include "composer/key_parser.h"
40 #include "composer/table.h"
41 #include "config/config_handler.h"
42 #include "converter/converter_mock.h"
43 #include "converter/segments.h"
44 #include "data_manager/testing/mock_data_manager.h"
45 #include "engine/engine.h"
46 #include "engine/mock_converter_engine.h"
47 #include "engine/mock_data_engine_factory.h"
48 #include "protocol/candidates.pb.h"
49 #include "protocol/commands.pb.h"
50 #include "protocol/config.pb.h"
51 #include "request/conversion_request.h"
52 #include "rewriter/transliteration_rewriter.h"
53 #include "session/internal/ime_context.h"
54 #include "session/internal/keymap.h"
55 #include "session/request_test_util.h"
56 #include "session/session_converter_interface.h"
57 #include "testing/base/public/gunit.h"
58 #include "testing/base/public/mozctest.h"
59 #include "usage_stats/usage_stats.h"
60 #include "usage_stats/usage_stats_testing_util.h"
61 
62 namespace mozc {
63 
64 class ConverterInterface;
65 class PredictorInterface;
66 
67 namespace dictionary { class SuppressionDictionary; }
68 
69 namespace session {
70 namespace {
71 
72 using ::mozc::commands::Request;
73 using ::mozc::usage_stats::UsageStats;
74 
SetSendKeyCommandWithKeyString(const string & key_string,commands::Command * command)75 void SetSendKeyCommandWithKeyString(const string &key_string,
76                                     commands::Command *command) {
77   command->Clear();
78   command->mutable_input()->set_type(commands::Input::SEND_KEY);
79   commands::KeyEvent *key = command->mutable_input()->mutable_key();
80   key->set_key_string(key_string);
81 }
82 
SetSendKeyCommand(const string & key,commands::Command * command)83 bool SetSendKeyCommand(const string &key, commands::Command *command) {
84   command->Clear();
85   command->mutable_input()->set_type(commands::Input::SEND_KEY);
86   return KeyParser::ParseKey(key, command->mutable_input()->mutable_key());
87 }
88 
SendKey(const string & key,Session * session,commands::Command * command)89 bool SendKey(const string &key,
90              Session *session,
91              commands::Command *command) {
92   if (!SetSendKeyCommand(key, command)) {
93     return false;
94   }
95   return session->SendKey(command);
96 }
97 
SendKeyWithMode(const string & key,commands::CompositionMode mode,Session * session,commands::Command * command)98 bool SendKeyWithMode(const string &key,
99                      commands::CompositionMode mode,
100                      Session *session,
101                      commands::Command *command) {
102   if (!SetSendKeyCommand(key, command)) {
103     return false;
104   }
105   command->mutable_input()->mutable_key()->set_mode(mode);
106   return session->SendKey(command);
107 }
108 
SendKeyWithModeAndActivated(const string & key,bool activated,commands::CompositionMode mode,Session * session,commands::Command * command)109 bool SendKeyWithModeAndActivated(const string &key,
110                                  bool activated,
111                                  commands::CompositionMode mode,
112                                  Session *session,
113                                  commands::Command *command) {
114   if (!SetSendKeyCommand(key, command)) {
115     return false;
116   }
117   command->mutable_input()->mutable_key()->set_activated(activated);
118   command->mutable_input()->mutable_key()->set_mode(mode);
119   return session->SendKey(command);
120 }
121 
TestSendKey(const string & key,Session * session,commands::Command * command)122 bool TestSendKey(const string &key,
123                  Session *session,
124                  commands::Command *command) {
125   if (!SetSendKeyCommand(key, command)) {
126     return false;
127   }
128   return session->TestSendKey(command);
129 }
130 
TestSendKeyWithMode(const string & key,commands::CompositionMode mode,Session * session,commands::Command * command)131 bool TestSendKeyWithMode(const string &key,
132                          commands::CompositionMode mode,
133                          Session *session,
134                          commands::Command *command) {
135   if (!SetSendKeyCommand(key, command)) {
136     return false;
137   }
138   command->mutable_input()->mutable_key()->set_mode(mode);
139   return session->TestSendKey(command);
140 }
141 
TestSendKeyWithModeAndActivated(const string & key,bool activated,commands::CompositionMode mode,Session * session,commands::Command * command)142 bool TestSendKeyWithModeAndActivated(const string &key,
143                                      bool activated,
144                                      commands::CompositionMode mode,
145                                      Session *session,
146                                      commands::Command *command) {
147   if (!SetSendKeyCommand(key, command)) {
148     return false;
149   }
150   command->mutable_input()->mutable_key()->set_activated(activated);
151   command->mutable_input()->mutable_key()->set_mode(mode);
152   return session->TestSendKey(command);
153 }
154 
SendSpecialKey(commands::KeyEvent::SpecialKey special_key,Session * session,commands::Command * command)155 bool SendSpecialKey(commands::KeyEvent::SpecialKey special_key,
156                     Session* session,
157                     commands::Command* command) {
158   command->Clear();
159   command->mutable_input()->set_type(commands::Input::SEND_KEY);
160   command->mutable_input()->mutable_key()->set_special_key(special_key);
161   return session->SendKey(command);
162 }
163 
164 
SetSendCommandCommand(commands::SessionCommand::CommandType type,commands::Command * command)165 void SetSendCommandCommand(commands::SessionCommand::CommandType type,
166                            commands::Command *command) {
167   command->Clear();
168   command->mutable_input()->set_type(commands::Input::SEND_COMMAND);
169   command->mutable_input()->mutable_command()->set_type(type);
170 }
171 
SendCommand(commands::SessionCommand::CommandType type,Session * session,commands::Command * command)172 bool SendCommand(commands::SessionCommand::CommandType type,
173                  Session *session,
174                  commands::Command *command) {
175   SetSendCommandCommand(type, command);
176   return session->SendCommand(command);
177 }
178 
InsertCharacterCodeAndString(const char key_code,const string & key_string,Session * session,commands::Command * command)179 bool InsertCharacterCodeAndString(const char key_code,
180                                   const string &key_string,
181                                   Session *session,
182                                   commands::Command *command) {
183   command->Clear();
184   commands::KeyEvent *key_event = command->mutable_input()->mutable_key();
185   key_event->set_key_code(key_code);
186   key_event->set_key_string(key_string);
187   return session->InsertCharacter(command);
188 }
189 
AddCandidate(const string & key,const string & value,Segment * segment)190 Segment::Candidate *AddCandidate(const string &key, const string &value,
191                                  Segment *segment) {
192   Segment::Candidate *candidate = segment->add_candidate();
193   candidate->key = key;
194   candidate->content_key = key;
195   candidate->value = value;
196   return candidate;
197 }
198 
AddMetaCandidate(const string & key,const string & value,Segment * segment)199 Segment::Candidate *AddMetaCandidate(const string &key, const string &value,
200                                      Segment *segment) {
201   Segment::Candidate *candidate = segment->add_meta_candidate();
202   candidate->key = key;
203   candidate->content_key = key;
204   candidate->value = value;
205   return candidate;
206 }
207 
GetComposition(const commands::Command & command)208 string GetComposition(const commands::Command &command) {
209   if (!command.output().has_preedit()) {
210     return "";
211   }
212 
213   string preedit;
214   for (size_t i = 0; i < command.output().preedit().segment_size(); ++i) {
215     preedit.append(command.output().preedit().segment(i).value());
216   }
217   return preedit;
218 }
219 
EnsurePreedit(const string & expected,const commands::Command & command)220 ::testing::AssertionResult EnsurePreedit(const string &expected,
221                                          const commands::Command &command) {
222   if (!command.output().has_preedit()) {
223     return ::testing::AssertionFailure() << "No preedit.";
224   }
225   string actual;
226   for (size_t i = 0; i < command.output().preedit().segment_size(); ++i) {
227     actual.append(command.output().preedit().segment(i).value());
228   }
229   if (expected == actual) {
230     return ::testing::AssertionSuccess();
231   }
232   return ::testing::AssertionFailure()
233       << "expected: " << expected << ", actual: " << actual;
234 }
235 
EnsureSingleSegment(const string & expected,const commands::Command & command)236 ::testing::AssertionResult EnsureSingleSegment(
237     const string &expected, const commands::Command &command) {
238   if (!command.output().has_preedit()) {
239     return ::testing::AssertionFailure() << "No preedit.";
240   }
241   if (command.output().preedit().segment_size() != 1) {
242     return ::testing::AssertionFailure()
243         << "Not single segment. segment size: "
244         << command.output().preedit().segment_size();
245   }
246   const commands::Preedit::Segment &segment =
247       command.output().preedit().segment(0);
248   if (!segment.has_value()) {
249     return ::testing::AssertionFailure() << "No segment value.";
250   }
251   const string &actual = segment.value();
252   if (expected == actual) {
253     return ::testing::AssertionSuccess();
254   }
255   return ::testing::AssertionFailure()
256       << "expected: " << expected << ", actual: " << actual;
257 }
258 
EnsureSingleSegmentAndKey(const string & expected_value,const string & expected_key,const commands::Command & command)259 ::testing::AssertionResult EnsureSingleSegmentAndKey(
260     const string &expected_value,
261     const string &expected_key,
262     const commands::Command &command) {
263   if (!command.output().has_preedit()) {
264     return ::testing::AssertionFailure() << "No preedit.";
265   }
266   if (command.output().preedit().segment_size() != 1) {
267     return ::testing::AssertionFailure()
268         << "Not single segment. segment size: "
269         << command.output().preedit().segment_size();
270   }
271   const commands::Preedit::Segment &segment =
272       command.output().preedit().segment(0);
273   if (!segment.has_value()) {
274     return ::testing::AssertionFailure() << "No segment value.";
275   }
276   if (!segment.has_key()) {
277     return ::testing::AssertionFailure() << "No segment key.";
278   }
279   const string &actual_value = segment.value();
280   const string &actual_key = segment.key();
281   if (expected_value == actual_value && expected_key == actual_key) {
282     return ::testing::AssertionSuccess();
283   }
284   return ::testing::AssertionFailure()
285       << "expected_value: " << expected_value
286       << ", actual_value: " << actual_value
287       << ", expected_key: " << expected_key
288       << ", actual_key: " << actual_key;
289 }
290 
EnsureResult(const string & expected,const commands::Command & command)291 ::testing::AssertionResult EnsureResult(const string &expected,
292                                         const commands::Command &command) {
293   if (!command.output().has_result()) {
294     return ::testing::AssertionFailure() << "No result.";
295   }
296   if (!command.output().result().has_value()) {
297     return ::testing::AssertionFailure() << "No result value.";
298   }
299   const string &actual = command.output().result().value();
300   if (expected == actual) {
301     return ::testing::AssertionSuccess();
302   }
303   return ::testing::AssertionFailure()
304       << "expected: " << expected << ", actual: " << actual;
305 }
306 
EnsureResultAndKey(const string & expected_value,const string & expected_key,const commands::Command & command)307 ::testing::AssertionResult EnsureResultAndKey(
308     const string &expected_value,
309     const string &expected_key,
310     const commands::Command &command) {
311   if (!command.output().has_result()) {
312     return ::testing::AssertionFailure() << "No result.";
313   }
314   if (!command.output().result().has_value()) {
315     return ::testing::AssertionFailure() << "No result value.";
316   }
317   if (!command.output().result().has_key()) {
318     return ::testing::AssertionFailure() << "No result value.";
319   }
320   const string &actual_value = command.output().result().value();
321   const string &actual_key = command.output().result().key();
322   if (expected_value == actual_value && expected_key == actual_key) {
323     return ::testing::AssertionSuccess();
324   }
325   return ::testing::AssertionFailure()
326       << "expected_value: " << expected_value
327       << ", actual_value: " << actual_value
328       << ", expected_key: " << expected_key
329       << ", actual_key: " << actual_key;
330 }
331 
TryUndoAndAssertSuccess(Session * session)332 ::testing::AssertionResult TryUndoAndAssertSuccess(Session *session) {
333   commands::Command command;
334   session->RequestUndo(&command);
335   if (!command.output().consumed()) {
336     return ::testing::AssertionFailure() << "Not consumed.";
337   }
338   if (!command.output().has_callback()) {
339     return ::testing::AssertionFailure() << "No callback.";
340   }
341   if (command.output().callback().session_command().type() !=
342       commands::SessionCommand::UNDO) {
343     return ::testing::AssertionFailure() <<
344         "Callback type is not Undo. Actual type: " <<
345         command.output().callback().session_command().type();
346   }
347   return ::testing::AssertionSuccess();
348 }
349 
TryUndoAndAssertDoNothing(Session * session)350 ::testing::AssertionResult TryUndoAndAssertDoNothing(Session *session) {
351   commands::Command command;
352   session->RequestUndo(&command);
353   if (command.output().consumed()) {
354     return ::testing::AssertionFailure()
355         << "Key event is consumed against expectation.";
356   }
357   return ::testing::AssertionSuccess();
358 }
359 
360 #define EXPECT_PREEDIT(expected, command)  \
361     EXPECT_TRUE(EnsurePreedit(expected, command))
362 #define EXPECT_SINGLE_SEGMENT(expected, command)  \
363     EXPECT_TRUE(EnsureSingleSegment(expected, command))
364 #define EXPECT_SINGLE_SEGMENT_AND_KEY(expected_value, expected_key, command)  \
365     EXPECT_TRUE(EnsureSingleSegmentAndKey(expected_value,                     \
366                                           expected_key, command))
367 #define EXPECT_RESULT(expected, command)  \
368     EXPECT_TRUE(EnsureResult(expected, command))
369 #define EXPECT_RESULT_AND_KEY(expected_value, expected_key, command)  \
370     EXPECT_TRUE(EnsureResultAndKey(expected_value, expected_key, command))
371 
SwitchInputFieldType(commands::Context::InputFieldType type,Session * session)372 void SwitchInputFieldType(commands::Context::InputFieldType type,
373                           Session *session) {
374   commands::Command command;
375   SetSendCommandCommand(commands::SessionCommand::SWITCH_INPUT_FIELD_TYPE,
376                         &command);
377   command.mutable_input()->mutable_context()->set_input_field_type(type);
378   EXPECT_TRUE(session->SendCommand(&command));
379   EXPECT_EQ(type, session->context().composer().GetInputFieldType());
380 }
381 
SwitchInputMode(commands::CompositionMode mode,Session * session)382 void SwitchInputMode(commands::CompositionMode mode, Session *session) {
383   commands::Command command;
384   SetSendCommandCommand(commands::SessionCommand::SWITCH_INPUT_MODE, &command);
385   command.mutable_input()->mutable_command()->set_composition_mode(mode);
386   EXPECT_TRUE(session->SendCommand(&command));
387 }
388 
389 // since History segments are almost hidden from
390 class ConverterMockForReset : public ConverterMock {
391  public:
ConverterMockForReset()392   ConverterMockForReset() : reset_conversion_called_(false) {}
393 
ResetConversion(Segments * segments) const394   bool ResetConversion(Segments *segments) const override {
395     reset_conversion_called_ = true;
396     return true;
397   }
398 
reset_conversion_called() const399   bool reset_conversion_called() const {
400     return reset_conversion_called_;
401   }
402 
Reset()403   void Reset() {
404     reset_conversion_called_ = false;
405   }
406 
407  private:
408   mutable bool reset_conversion_called_;
409 };
410 
411 class MockConverterEngineForReset : public EngineInterface {
412  public:
MockConverterEngineForReset()413   MockConverterEngineForReset() : converter_mock_(new ConverterMockForReset) {}
414   ~MockConverterEngineForReset() override = default;
415 
GetConverter() const416   ConverterInterface *GetConverter() const override {
417     return converter_mock_.get();
418   }
419 
GetPredictor() const420   PredictorInterface *GetPredictor() const override {
421     return nullptr;
422   }
423 
GetSuppressionDictionary()424   dictionary::SuppressionDictionary *GetSuppressionDictionary() override {
425     return nullptr;
426   }
427 
Reload()428   bool Reload() override {
429     return true;
430   }
431 
GetUserDataManager()432   UserDataManagerInterface *GetUserDataManager() override {
433     return nullptr;
434   }
435 
GetDataManager() const436   const DataManagerInterface *GetDataManager() const override {
437     return nullptr;
438   }
439 
GetDataVersion() const440   StringPiece GetDataVersion() const override { return StringPiece(); }
441 
converter_mock() const442   const ConverterMockForReset &converter_mock() const {
443     return *converter_mock_;
444   }
445 
mutable_converter_mock()446   ConverterMockForReset *mutable_converter_mock() {
447     return converter_mock_.get();
448   }
449 
450  private:
451   std::unique_ptr<ConverterMockForReset> converter_mock_;
452 };
453 
454 class ConverterMockForRevert : public ConverterMock {
455  public:
ConverterMockForRevert()456   ConverterMockForRevert() : revert_conversion_called_(false) {}
457 
RevertConversion(Segments * segments) const458   bool RevertConversion(Segments *segments) const override {
459     revert_conversion_called_ = true;
460     return true;
461   }
462 
revert_conversion_called() const463   bool revert_conversion_called() const {
464     return revert_conversion_called_;
465   }
466 
Reset()467   void Reset() {
468     revert_conversion_called_ = false;
469   }
470 
471  private:
472   mutable bool revert_conversion_called_;
473 };
474 
475 class MockConverterEngineForRevert : public EngineInterface {
476  public:
MockConverterEngineForRevert()477   MockConverterEngineForRevert()
478       : converter_mock_(new ConverterMockForRevert) {}
479   ~MockConverterEngineForRevert() override = default;
480 
GetConverter() const481   ConverterInterface *GetConverter() const override {
482     return converter_mock_.get();
483   }
484 
GetPredictor() const485   PredictorInterface *GetPredictor() const override {
486     return nullptr;
487   }
488 
GetSuppressionDictionary()489   dictionary::SuppressionDictionary *GetSuppressionDictionary() override {
490     return nullptr;
491   }
492 
Reload()493   bool Reload() override {
494     return true;
495   }
496 
GetUserDataManager()497   UserDataManagerInterface *GetUserDataManager() override {
498     return nullptr;
499   }
500 
GetDataManager() const501   const DataManagerInterface *GetDataManager() const override {
502     return nullptr;
503   }
504 
GetDataVersion() const505   StringPiece GetDataVersion() const override { return StringPiece(); }
506 
converter_mock() const507   const ConverterMockForRevert &converter_mock() const {
508     return *converter_mock_;
509   }
510 
mutable_converter_mock()511   ConverterMockForRevert *mutable_converter_mock() {
512     return converter_mock_.get();
513   }
514 
515  private:
516   std::unique_ptr<ConverterMockForRevert> converter_mock_;
517 };
518 
519 }  // namespace
520 
521 class SessionTest : public ::testing::Test {
522  protected:
SetUp()523   void SetUp() override {
524     UsageStats::ClearAllStatsForTest();
525 
526     mobile_request_.reset(new Request);
527     commands::RequestForUnitTest::FillMobileRequest(mobile_request_.get());
528 
529     mock_data_engine_.reset(MockDataEngineFactory::Create());
530     engine_.reset(new MockConverterEngine);
531 
532     t13n_rewriter_.reset(
533         new TransliterationRewriter(
534             dictionary::POSMatcher(mock_data_manager_.GetPOSMatcherData())));
535   }
536 
TearDown()537   void TearDown() override {
538     UsageStats::ClearAllStatsForTest();
539   }
540 
InsertCharacterChars(const string & chars,Session * session,commands::Command * command) const541   void InsertCharacterChars(const string &chars,
542                             Session *session,
543                             commands::Command *command) const {
544     const uint32 kNoModifiers = 0;
545     for (int i = 0; i < chars.size(); ++i) {
546       command->Clear();
547       commands::KeyEvent *key_event = command->mutable_input()->mutable_key();
548       key_event->set_key_code(chars[i]);
549       key_event->set_modifiers(kNoModifiers);
550       session->InsertCharacter(command);
551     }
552   }
553 
InsertCharacterCharsWithContext(const string & chars,const commands::Context & context,Session * session,commands::Command * command) const554   void InsertCharacterCharsWithContext(const string &chars,
555                                        const commands::Context &context,
556                                        Session *session,
557                                        commands::Command *command) const {
558     const uint32 kNoModifiers = 0;
559     for (size_t i = 0; i < chars.size(); ++i) {
560       command->Clear();
561       command->mutable_input()->mutable_context()->CopyFrom(context);
562       commands::KeyEvent *key_event = command->mutable_input()->mutable_key();
563       key_event->set_key_code(chars[i]);
564       key_event->set_modifiers(kNoModifiers);
565       session->InsertCharacter(command);
566     }
567   }
568 
InsertCharacterString(const string & key_strings,const string & chars,Session * session,commands::Command * command) const569   void InsertCharacterString(const string &key_strings,
570                              const string &chars,
571                              Session *session,
572                              commands::Command *command) const {
573     const uint32 kNoModifiers = 0;
574     std::vector<string> inputs;
575     const char *begin = key_strings.data();
576     const char *end = key_strings.data() + key_strings.size();
577     while (begin < end) {
578       const size_t mblen = Util::OneCharLen(begin);
579       inputs.push_back(string(begin, mblen));
580       begin += mblen;
581     }
582     CHECK_EQ(inputs.size(), chars.size());
583     for (int i = 0; i < chars.size(); ++i) {
584       command->Clear();
585       commands::KeyEvent *key_event = command->mutable_input()->mutable_key();
586       key_event->set_key_code(chars[i]);
587       key_event->set_modifiers(kNoModifiers);
588       key_event->set_key_string(inputs[i]);
589       session->InsertCharacter(command);
590     }
591   }
592 
593   // set result for "あいうえお"
SetAiueo(Segments * segments)594   void SetAiueo(Segments *segments) {
595     segments->Clear();
596     Segment *segment;
597     Segment::Candidate *candidate;
598 
599     segment = segments->add_segment();
600     segment->set_key("あいうえお");
601     candidate = segment->add_candidate();
602     candidate->key = "あいうえお";
603     candidate->content_key = "あいうえお";
604     candidate->value = "あいうえお";
605     candidate = segment->add_candidate();
606     candidate->key = "あいうえお";
607     candidate->content_key = "あいうえお";
608     candidate->value = "アイウエオ";
609   }
610 
InitSessionToDirect(Session * session)611   void InitSessionToDirect(Session* session) {
612     InitSessionToPrecomposition(session);
613     commands::Command command;
614     session->IMEOff(&command);
615   }
616 
InitSessionToConversionWithAiueo(Session * session)617   void InitSessionToConversionWithAiueo(Session *session) {
618     InitSessionToPrecomposition(session);
619 
620     commands::Command command;
621     InsertCharacterChars("aiueo", session, &command);
622     ConversionRequest request;
623     Segments segments;
624     SetComposer(session, &request);
625     SetAiueo(&segments);
626     FillT13Ns(request, &segments);
627     GetConverterMock()->SetStartConversionForRequest(&segments, true);
628 
629     command.Clear();
630     EXPECT_TRUE(session->Convert(&command));
631     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
632   }
633 
InitSessionToPrecomposition(Session * session)634   void InitSessionToPrecomposition(Session* session) {
635 #ifdef OS_WIN
636     // Session is created with direct mode on Windows
637     // Direct status
638     commands::Command command;
639     session->IMEOn(&command);
640 #endif  // OS_WIN
641     InitSessionWithRequest(session, commands::Request::default_instance());
642   }
643 
InitSessionToPrecomposition(Session * session,const commands::Request & request)644   void InitSessionToPrecomposition(
645       Session* session,
646       const commands::Request &request) {
647 #ifdef OS_WIN
648     // Session is created with direct mode on Windows
649     // Direct status
650     commands::Command command;
651     session->IMEOn(&command);
652 #endif  // OS_WIN
653     InitSessionWithRequest(session, request);
654   }
655 
InitSessionWithRequest(Session * session,const commands::Request & request)656   void InitSessionWithRequest(
657       Session* session,
658       const commands::Request &request) {
659     session->SetRequest(&request);
660     table_.reset(new composer::Table());
661     table_->InitializeWithRequestAndConfig(
662         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
663     session->SetTable(table_.get());
664   }
665 
666   // set result for "like"
SetLike(Segments * segments)667   void SetLike(Segments *segments) {
668     Segment *segment;
669     Segment::Candidate *candidate;
670 
671     segments->Clear();
672     segment = segments->add_segment();
673 
674     segment->set_key("ぃ");
675     candidate = segment->add_candidate();
676     candidate->value = "ぃ";
677 
678     candidate = segment->add_candidate();
679     candidate->value = "ィ";
680 
681     segment = segments->add_segment();
682     segment->set_key("け");
683     candidate = segment->add_candidate();
684     candidate->value = "家";
685     candidate = segment->add_candidate();
686     candidate->value = "け";
687   }
688 
FillT13Ns(const ConversionRequest & request,Segments * segments)689   void FillT13Ns(const ConversionRequest &request, Segments *segments) {
690     t13n_rewriter_->Rewrite(request, segments);
691   }
692 
SetComposer(Session * session,ConversionRequest * request)693   void SetComposer(Session *session, ConversionRequest *request) {
694     DCHECK(request);
695     request->set_composer(&session->context().composer());
696   }
697 
SetupMockForReverseConversion(const string & kanji,const string & hiragana)698   void SetupMockForReverseConversion(const string &kanji,
699                                      const string &hiragana) {
700     // Set up Segments for reverse conversion.
701     Segments reverse_segments;
702     Segment *segment;
703     segment = reverse_segments.add_segment();
704     segment->set_key(kanji);
705     Segment::Candidate *candidate;
706     candidate = segment->add_candidate();
707     // For reverse conversion, key is the original kanji string.
708     candidate->key = kanji;
709     candidate->value = hiragana;
710     GetConverterMock()->SetStartReverseConversion(&reverse_segments, true);
711     // Set up Segments for forward conversion.
712     Segments segments;
713     segment = segments.add_segment();
714     segment->set_key(hiragana);
715     candidate = segment->add_candidate();
716     candidate->key = hiragana;
717     candidate->value = kanji;
718     GetConverterMock()->SetStartConversionForRequest(&segments, true);
719   }
720 
SetupCommandForReverseConversion(const string & text,commands::Input * input)721   void SetupCommandForReverseConversion(const string &text,
722                                         commands::Input *input) {
723     input->Clear();
724     input->set_type(commands::Input::SEND_COMMAND);
725     input->mutable_command()->set_type(
726         commands::SessionCommand::CONVERT_REVERSE);
727     input->mutable_command()->set_text(text);
728   }
729 
SetupZeroQuerySuggestionReady(bool enable,Session * session,commands::Request * request)730   void SetupZeroQuerySuggestionReady(bool enable,
731                                      Session *session,
732                                      commands::Request *request) {
733     InitSessionToPrecomposition(session);
734 
735     // Enable zero query suggest.
736     request->set_zero_query_suggestion(enable);
737     session->SetRequest(request);
738 
739     // Type "google".
740     commands::Command command;
741     InsertCharacterChars("google", session, &command);
742 
743     {
744       // Set up a mock conversion result.
745       Segments segments;
746       segments.set_request_type(Segments::CONVERSION);
747       Segment *segment;
748       segment = segments.add_segment();
749       segment->set_key("google");
750       segment->add_candidate()->value = "GOOGLE";
751       GetConverterMock()->SetStartConversionForRequest(&segments, true);
752     }
753     command.Clear();
754     session->Convert(&command);
755 
756     {
757       // Set up a mock suggestion result.
758       Segments segments;
759       segments.set_request_type(Segments::SUGGESTION);
760       Segment *segment;
761       segment = segments.add_segment();
762       segment->set_key("");
763       AddCandidate("search", "search", segment);
764       AddCandidate("input", "input", segment);
765       GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
766     }
767   }
768 
SetupZeroQuerySuggestion(Session * session,commands::Request * request,commands::Command * command)769   void SetupZeroQuerySuggestion(Session *session,
770                                 commands::Request *request,
771                                 commands::Command *command) {
772     SetupZeroQuerySuggestionReady(true, session, request);
773     command->Clear();
774     session->Commit(command);
775   }
776 
SetUndoContext(Session * session)777   void SetUndoContext(Session *session) {
778     commands::Command command;
779     Segments segments;
780 
781     {  // Create segments
782       InsertCharacterChars("aiueo", session, &command);
783       SetAiueo(&segments);
784       // Don't use FillT13Ns(). It makes platform dependent segments.
785       // TODO(hsumita): Makes FillT13Ns() independent from platforms.
786       Segment::Candidate *candidate;
787       candidate = segments.mutable_segment(0)->add_candidate();
788       candidate->value = "aiueo";
789       candidate = segments.mutable_segment(0)->add_candidate();
790       candidate->value = "AIUEO";
791     }
792 
793     {  // Commit the composition to make an undo context.
794       GetConverterMock()->SetStartConversionForRequest(&segments, true);
795       command.Clear();
796       session->Convert(&command);
797       EXPECT_FALSE(command.output().has_result());
798       EXPECT_PREEDIT("あいうえお", command);
799 
800       GetConverterMock()->SetCommitSegmentValue(&segments, true);
801       command.Clear();
802 
803       session->Commit(&command);
804       EXPECT_FALSE(command.output().has_preedit());
805       EXPECT_RESULT("あいうえお", command);
806     }
807   }
808 
GetConverterMock()809   ConverterMock *GetConverterMock() {
810     return engine_->mutable_converter_mock();
811   }
812 
813   // IMPORTANT: Use std::unique_ptr and instanciate an object in SetUp() method
814   //    if the target object should be initialized *AFTER* global settings
815   //    such as user profile dir or global config are set up for unit test.
816   //    If you directly define a variable here without std::unique_ptr, its
817   //    constructor will be called *BEFORE* SetUp() is called.
818   std::unique_ptr<MockConverterEngine> engine_;
819   std::unique_ptr<Engine> mock_data_engine_;
820   std::unique_ptr<TransliterationRewriter> t13n_rewriter_;
821   std::unique_ptr<composer::Table> table_;
822   std::unique_ptr<Request> mobile_request_;
823   mozc::usage_stats::scoped_usage_stats_enabler usage_stats_enabler_;
824   const testing::MockDataManager mock_data_manager_;
825 
826  private:
827   const testing::ScopedTmpUserProfileDirectory scoped_profile_dir_;
828 };
829 
830 // This test is intentionally defined at this location so that this
831 // test can ensure that the first SetUp() initialized table object to
832 // the default state.  Please do not define another test before this.
833 // FYI, each TEST_F will be eventually expanded into a global variable
834 // and global variables in a single translation unit (source file) are
835 // always initialized in the order in which they are defined.
TEST_F(SessionTest,TestOfTestForSetup)836 TEST_F(SessionTest, TestOfTestForSetup) {
837   config::Config config;
838   config::ConfigHandler::GetDefaultConfig(&config);
839   EXPECT_FALSE(config.has_use_auto_conversion())
840       << "Global config should be initialized for each test fixture.";
841 
842   // Make sure that the default roman table is initialized.
843   {
844     std::unique_ptr<Session> session(new Session(engine_.get()));
845     session->SetConfig(&config);
846     InitSessionToPrecomposition(session.get());
847     commands::Command command;
848     SendKey("a", session.get(), &command);
849     EXPECT_SINGLE_SEGMENT("あ", command)
850         << "Global Romaji table should be initialized for each test fixture.";
851   }
852 }
853 
TEST_F(SessionTest,TestSendKey)854 TEST_F(SessionTest, TestSendKey) {
855   std::unique_ptr<Session> session(new Session(engine_.get()));
856   InitSessionToPrecomposition(session.get());
857 
858   commands::Command command;
859 
860   // Precomposition status
861   TestSendKey("Up", session.get(), &command);
862   EXPECT_FALSE(command.output().consumed());
863 
864   SendKey("Up", session.get(), &command);
865   EXPECT_FALSE(command.output().consumed());
866 
867   // InsertSpace on Precomposition status
868   // TODO(komatsu): Test both cases of config.ascii_character_form() is
869   // FULL_WIDTH and HALF_WIDTH.
870   TestSendKey("Space", session.get(), &command);
871   const bool consumed_on_testsendkey = command.output().consumed();
872   SendKey("Space", session.get(), &command);
873   const bool consumed_on_sendkey = command.output().consumed();
874   EXPECT_EQ(consumed_on_sendkey, consumed_on_testsendkey);
875 
876   // Precomposition status
877   TestSendKey("G", session.get(), &command);
878   EXPECT_TRUE(command.output().consumed());
879   SendKey("G", session.get(), &command);
880   EXPECT_TRUE(command.output().consumed());
881 
882   // Composition status
883   TestSendKey("Up", session.get(), &command);
884   EXPECT_TRUE(command.output().consumed());
885   SendKey("Up", session.get(), &command);
886   EXPECT_TRUE(command.output().consumed());
887 }
888 
TEST_F(SessionTest,SendCommand)889 TEST_F(SessionTest, SendCommand) {
890   std::unique_ptr<Session> session(new Session(engine_.get()));
891   InitSessionToPrecomposition(session.get());
892 
893   commands::Command command;
894   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
895   InsertCharacterChars("kanji", session.get(), &command);
896 
897   // REVERT
898   SendCommand(commands::SessionCommand::REVERT, session.get(), &command);
899   EXPECT_TRUE(command.output().consumed());
900   EXPECT_FALSE(command.output().has_result());
901   EXPECT_FALSE(command.output().has_preedit());
902   EXPECT_FALSE(command.output().has_candidates());
903 
904   // SUBMIT
905   InsertCharacterChars("k", session.get(), &command);
906   SendCommand(commands::SessionCommand::SUBMIT, session.get(), &command);
907   EXPECT_TRUE(command.output().consumed());
908   EXPECT_RESULT("k", command);
909   EXPECT_FALSE(command.output().has_preedit());
910   EXPECT_FALSE(command.output().has_candidates());
911 
912   // SWITCH_INPUT_MODE
913   SendKey("a", session.get(), &command);
914   EXPECT_SINGLE_SEGMENT("あ", command);
915 
916   SwitchInputMode(commands::FULL_ASCII, session.get());
917 
918   SendKey("a", session.get(), &command);
919   EXPECT_SINGLE_SEGMENT("あa", command);
920 
921   // GET_STATUS
922   SendCommand(commands::SessionCommand::GET_STATUS, session.get(), &command);
923   // FULL_ASCII was set at the SWITCH_INPUT_MODE testcase.
924   SwitchInputMode(commands::FULL_ASCII, session.get());
925 
926   // RESET_CONTEXT
927   // test of reverting composition
928   InsertCharacterChars("kanji", session.get(), &command);
929   SendCommand(commands::SessionCommand::RESET_CONTEXT, session.get(), &command);
930   EXPECT_TRUE(command.output().consumed());
931   EXPECT_FALSE(command.output().has_result());
932   EXPECT_FALSE(command.output().has_preedit());
933   EXPECT_FALSE(command.output().has_candidates());
934   // test of reseting the history segements
935   std::unique_ptr<MockConverterEngineForReset> engine(
936       new MockConverterEngineForReset);
937   session.reset(new Session(engine.get()));
938   InitSessionToPrecomposition(session.get());
939   SendCommand(commands::SessionCommand::RESET_CONTEXT, session.get(), &command);
940   EXPECT_FALSE(command.output().consumed());
941   EXPECT_TRUE(engine->converter_mock().reset_conversion_called());
942 
943   // USAGE_STATS_EVENT
944   SendCommand(commands::SessionCommand::USAGE_STATS_EVENT, session.get(),
945               &command);
946   EXPECT_TRUE(command.output().has_consumed());
947   EXPECT_FALSE(command.output().consumed());
948 }
949 
TEST_F(SessionTest,SwitchInputMode)950 TEST_F(SessionTest, SwitchInputMode) {
951   {
952     std::unique_ptr<Session> session(new Session(engine_.get()));
953     InitSessionToPrecomposition(session.get());
954     commands::Command command;
955 
956     // SWITCH_INPUT_MODE
957     SendKey("a", session.get(), &command);
958     EXPECT_SINGLE_SEGMENT("あ", command);
959 
960     SwitchInputMode(commands::FULL_ASCII, session.get());
961 
962     SendKey("a", session.get(), &command);
963     EXPECT_SINGLE_SEGMENT("あa", command);
964 
965     // GET_STATUS
966     SendCommand(commands::SessionCommand::GET_STATUS, session.get(), &command);
967     // FULL_ASCII was set at the SWITCH_INPUT_MODE testcase.
968     EXPECT_EQ(commands::FULL_ASCII, command.output().mode());
969   }
970 
971   {
972     // Confirm that we can change the mode from DIRECT
973     // to other modes directly (without IMEOn command).
974     std::unique_ptr<Session> session(new Session(engine_.get()));
975     InitSessionToDirect(session.get());
976 
977     commands::Command command;
978 
979     // GET_STATUS
980     SendCommand(commands::SessionCommand::GET_STATUS, session.get(), &command);
981     // FULL_ASCII was set at the SWITCH_INPUT_MODE testcase.
982     EXPECT_EQ(commands::DIRECT, command.output().mode());
983 
984     // SWITCH_INPUT_MODE
985     SwitchInputMode(commands::HIRAGANA, session.get());
986 
987     // GET_STATUS
988     SendCommand(commands::SessionCommand::GET_STATUS, session.get(), &command);
989     // FULL_ASCII was set at the SWITCH_INPUT_MODE testcase.
990     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
991 
992     SendKey("a", session.get(), &command);
993     EXPECT_SINGLE_SEGMENT("あ", command);
994 
995     // GET_STATUS
996     SendCommand(commands::SessionCommand::GET_STATUS, session.get(), &command);
997     // FULL_ASCII was set at the SWITCH_INPUT_MODE testcase.
998     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
999   }
1000 }
1001 
TEST_F(SessionTest,RevertComposition)1002 TEST_F(SessionTest, RevertComposition) {
1003   // Issue#2237323
1004   std::unique_ptr<Session> session(new Session(engine_.get()));
1005   InitSessionToPrecomposition(session.get());
1006   commands::Command command;
1007 
1008   InsertCharacterChars("aiueo", session.get(), &command);
1009   ConversionRequest request;
1010   Segments segments;
1011   SetComposer(session.get(), &request);
1012   SetAiueo(&segments);
1013   FillT13Ns(request, &segments);
1014   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1015 
1016   command.Clear();
1017   session->Convert(&command);
1018 
1019   // REVERT
1020   SendCommand(commands::SessionCommand::REVERT, session.get(), &command);
1021   EXPECT_TRUE(command.output().consumed());
1022   EXPECT_FALSE(command.output().has_result());
1023   EXPECT_FALSE(command.output().has_preedit());
1024   EXPECT_FALSE(command.output().has_candidates());
1025 
1026   SendKey("a", session.get(), &command);
1027   EXPECT_SINGLE_SEGMENT("あ", command);
1028 }
1029 
TEST_F(SessionTest,InputMode)1030 TEST_F(SessionTest, InputMode) {
1031   std::unique_ptr<Session> session(new Session(engine_.get()));
1032   InitSessionToPrecomposition(session.get());
1033   commands::Command command;
1034   EXPECT_TRUE(session->InputModeHalfASCII(&command));
1035   EXPECT_TRUE(command.output().consumed());
1036   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
1037 
1038   SendKey("a", session.get(), &command);
1039   EXPECT_EQ("a", command.output().preedit().segment(0).key());
1040 
1041   command.Clear();
1042   session->Commit(&command);
1043 
1044   // Input mode remains even after submission.
1045   command.Clear();
1046   session->GetStatus(&command);
1047   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
1048 }
1049 
TEST_F(SessionTest,SelectCandidate)1050 TEST_F(SessionTest, SelectCandidate) {
1051   std::unique_ptr<Session> session(new Session(engine_.get()));
1052   InitSessionToPrecomposition(session.get());
1053 
1054   commands::Command command;
1055   InsertCharacterChars("aiueo", session.get(), &command);
1056   ConversionRequest request;
1057   Segments segments;
1058   SetComposer(session.get(), &request);
1059   SetAiueo(&segments);
1060   FillT13Ns(request, &segments);
1061   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1062 
1063   command.Clear();
1064   session->Convert(&command);
1065 
1066   command.Clear();
1067   session->ConvertNext(&command);
1068 
1069   SetSendCommandCommand(commands::SessionCommand::SELECT_CANDIDATE, &command);
1070   command.mutable_input()->mutable_command()->set_id(
1071       -(transliteration::HALF_KATAKANA + 1));
1072   session->SendCommand(&command);
1073   EXPECT_TRUE(command.output().consumed());
1074   EXPECT_FALSE(command.output().has_result());
1075   EXPECT_PREEDIT("アイウエオ", command);
1076   EXPECT_FALSE(command.output().has_candidates());
1077 }
1078 
TEST_F(SessionTest,HighlightCandidate)1079 TEST_F(SessionTest, HighlightCandidate) {
1080   std::unique_ptr<Session> session(new Session(engine_.get()));
1081   InitSessionToPrecomposition(session.get());
1082 
1083   commands::Command command;
1084   InsertCharacterChars("aiueo", session.get(), &command);
1085   ConversionRequest request;
1086   Segments segments;
1087   SetComposer(session.get(), &request);
1088   SetAiueo(&segments);
1089   FillT13Ns(request, &segments);
1090   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1091 
1092   command.Clear();
1093   session->Convert(&command);
1094 
1095   command.Clear();
1096   session->ConvertNext(&command);
1097   EXPECT_SINGLE_SEGMENT("アイウエオ", command);
1098 
1099   SetSendCommandCommand(commands::SessionCommand::HIGHLIGHT_CANDIDATE,
1100                         &command);
1101   command.mutable_input()->mutable_command()->set_id(
1102       -(transliteration::HALF_KATAKANA + 1));
1103   session->SendCommand(&command);
1104   EXPECT_TRUE(command.output().consumed());
1105   EXPECT_FALSE(command.output().has_result());
1106   EXPECT_SINGLE_SEGMENT("アイウエオ", command);
1107   EXPECT_TRUE(command.output().has_candidates());
1108 }
1109 
TEST_F(SessionTest,Conversion)1110 TEST_F(SessionTest, Conversion) {
1111   std::unique_ptr<Session> session(new Session(engine_.get()));
1112   InitSessionToPrecomposition(session.get());
1113 
1114   commands::Command command;
1115   InsertCharacterChars("aiueo", session.get(), &command);
1116   ConversionRequest request;
1117   Segments segments;
1118   SetComposer(session.get(), &request);
1119   SetAiueo(&segments);
1120   FillT13Ns(request, &segments);
1121   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1122 
1123   EXPECT_SINGLE_SEGMENT_AND_KEY("あいうえお", "あいうえお", command);
1124 
1125   command.Clear();
1126   session->Convert(&command);
1127 
1128   command.Clear();
1129   session->ConvertNext(&command);
1130 
1131   string key;
1132   for (int i = 0; i < command.output().preedit().segment_size(); ++i) {
1133     EXPECT_TRUE(command.output().preedit().segment(i).has_value());
1134     EXPECT_TRUE(command.output().preedit().segment(i).has_key());
1135     key += command.output().preedit().segment(i).key();
1136   }
1137   EXPECT_EQ("あいうえお", key);
1138 }
1139 
TEST_F(SessionTest,SegmentWidthShrink)1140 TEST_F(SessionTest, SegmentWidthShrink) {
1141   std::unique_ptr<Session> session(new Session(engine_.get()));
1142   InitSessionToPrecomposition(session.get());
1143 
1144   commands::Command command;
1145   InsertCharacterChars("aiueo", session.get(), &command);
1146   ConversionRequest request;
1147   Segments segments;
1148   SetComposer(session.get(), &request);
1149   SetAiueo(&segments);
1150   FillT13Ns(request, &segments);
1151   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1152 
1153   command.Clear();
1154   session->Convert(&command);
1155 
1156   command.Clear();
1157   session->SegmentWidthShrink(&command);
1158 
1159   command.Clear();
1160   session->SegmentWidthShrink(&command);
1161 }
1162 
TEST_F(SessionTest,ConvertPrev)1163 TEST_F(SessionTest, ConvertPrev) {
1164   std::unique_ptr<Session> session(new Session(engine_.get()));
1165   InitSessionToPrecomposition(session.get());
1166 
1167   commands::Command command;
1168   InsertCharacterChars("aiueo", session.get(), &command);
1169   ConversionRequest request;
1170   Segments segments;
1171   SetComposer(session.get(), &request);
1172   SetAiueo(&segments);
1173   FillT13Ns(request, &segments);
1174   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1175 
1176   command.Clear();
1177   session->Convert(&command);
1178 
1179   command.Clear();
1180   session->ConvertNext(&command);
1181 
1182   command.Clear();
1183   session->ConvertPrev(&command);
1184 
1185   command.Clear();
1186   session->ConvertPrev(&command);
1187 }
1188 
TEST_F(SessionTest,ResetFocusedSegmentAfterCommit)1189 TEST_F(SessionTest, ResetFocusedSegmentAfterCommit) {
1190   ConversionRequest request;
1191   Segments segments;
1192   Segment *segment;
1193   Segment::Candidate *candidate;
1194   std::unique_ptr<Session> session(new Session(engine_.get()));
1195   InitSessionToPrecomposition(session.get());
1196 
1197   commands::Command command;
1198   InsertCharacterChars("watasinonamaehanakanodesu", session.get(), &command);
1199   // "わたしのなまえはなかのです[]"
1200 
1201   segment = segments.add_segment();
1202   segment->set_key("わたしの");
1203   candidate = segment->add_candidate();
1204   candidate->value = "私の";
1205   candidate = segment->add_candidate();
1206   candidate->value = "わたしの";
1207   candidate = segment->add_candidate();
1208   candidate->value = "渡しの";
1209 
1210   segment = segments.add_segment();
1211   segment->set_key("なまえは");
1212   candidate = segment->add_candidate();
1213   candidate->value = "名前は";
1214   candidate = segment->add_candidate();
1215   candidate->value = "ナマエは";
1216 
1217   segment = segments.add_segment();
1218   segment->set_key("なかのです");
1219   candidate = segment->add_candidate();
1220   candidate->value = "中野です";
1221   candidate = segment->add_candidate();
1222   candidate->value = "なかのです";
1223   SetComposer(session.get(), &request);
1224   FillT13Ns(request, &segments);
1225   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1226 
1227   command.Clear();
1228   session->Convert(&command);
1229   EXPECT_TRUE(command.output().has_preedit());
1230   EXPECT_FALSE(command.output().has_result());
1231   // "[私の]名前は中野です"
1232   command.Clear();
1233   session->SegmentFocusRight(&command);
1234   EXPECT_TRUE(command.output().has_preedit());
1235   EXPECT_FALSE(command.output().has_result());
1236   // "私の[名前は]中野です"
1237   command.Clear();
1238   session->SegmentFocusRight(&command);
1239   EXPECT_TRUE(command.output().has_preedit());
1240   EXPECT_FALSE(command.output().has_result());
1241   // "私の名前は[中野です]"
1242 
1243   command.Clear();
1244   session->ConvertNext(&command);
1245   EXPECT_EQ(1, command.output().candidates().focused_index());
1246   EXPECT_TRUE(command.output().has_preedit());
1247   EXPECT_FALSE(command.output().has_result());
1248   // "私の名前は[中のです]"
1249 
1250   command.Clear();
1251   session->ConvertNext(&command);
1252   EXPECT_EQ(2, command.output().candidates().focused_index());
1253   EXPECT_TRUE(command.output().has_preedit());
1254   EXPECT_FALSE(command.output().has_result());
1255   // "私の名前は[なかのです]"
1256 
1257   command.Clear();
1258   session->Commit(&command);
1259   EXPECT_FALSE(command.output().has_preedit());
1260   EXPECT_TRUE(command.output().has_result());
1261   // "私の名前はなかのです[]"
1262 
1263   InsertCharacterChars("a", session.get(), &command);
1264 
1265   segments.Clear();
1266   segment = segments.add_segment();
1267   segment->set_key("あ");
1268   candidate = segment->add_candidate();
1269   candidate->value = "阿";
1270   candidate = segment->add_candidate();
1271   candidate->value = "亜";
1272 
1273   SetComposer(session.get(), &request);
1274   FillT13Ns(request, &segments);
1275   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1276 
1277   // "あ[]"
1278 
1279   command.Clear();
1280   session->Convert(&command);
1281   // "[阿]"
1282 
1283   command.Clear();
1284   // If the forcused_segment_ was not reset, this raises segmentation fault.
1285   session->ConvertNext(&command);
1286   // "[亜]"
1287 }
1288 
TEST_F(SessionTest,ResetFocusedSegmentAfterCancel)1289 TEST_F(SessionTest, ResetFocusedSegmentAfterCancel) {
1290   Segments segments;
1291   Segment *segment;
1292   Segment::Candidate *candidate;
1293   std::unique_ptr<Session> session(new Session(engine_.get()));
1294   InitSessionToPrecomposition(session.get());
1295 
1296   commands::Command command;
1297   InsertCharacterChars("ai", session.get(), &command);
1298 
1299   segment = segments.add_segment();
1300   segment->set_key("あい");
1301   candidate = segment->add_candidate();
1302   candidate->value = "愛";
1303   candidate = segment->add_candidate();
1304   candidate->value = "相";
1305   ConversionRequest request;
1306   SetComposer(session.get(), &request);
1307   FillT13Ns(request, &segments);
1308   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1309   // "あい[]"
1310 
1311   command.Clear();
1312   session->Convert(&command);
1313   // "[愛]"
1314 
1315   segments.Clear();
1316   segment = segments.add_segment();
1317   segment->set_key("あ");
1318   candidate = segment->add_candidate();
1319   candidate->value = "あ";
1320   segment = segments.add_segment();
1321   segment->set_key("い");
1322   candidate = segment->add_candidate();
1323   candidate->value = "い";
1324   candidate = segment->add_candidate();
1325   candidate->value = "位";
1326   GetConverterMock()->SetResizeSegment1(&segments, true);
1327 
1328   command.Clear();
1329   session->SegmentWidthShrink(&command);
1330   // "[あ]い"
1331 
1332   segment = segments.mutable_segment(0);
1333   segment->set_segment_type(Segment::FIXED_VALUE);
1334   GetConverterMock()->SetCommitSegmentValue(&segments, true);
1335 
1336   command.Clear();
1337   session->SegmentFocusRight(&command);
1338   // "あ[い]"
1339 
1340   command.Clear();
1341   session->ConvertNext(&command);
1342   // "あ[位]"
1343 
1344   command.Clear();
1345   session->ConvertCancel(&command);
1346   // "あい[]"
1347 
1348   segments.Clear();
1349   segment = segments.add_segment();
1350   segment->set_key("あい");
1351   candidate = segment->add_candidate();
1352   candidate->value = "愛";
1353   candidate = segment->add_candidate();
1354   candidate->value = "相";
1355   SetComposer(session.get(), &request);
1356   FillT13Ns(request, &segments);
1357   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1358 
1359   command.Clear();
1360   session->Convert(&command);
1361   // "[愛]"
1362 
1363   command.Clear();
1364   // If the forcused_segment_ was not reset, this raises segmentation fault.
1365   session->Convert(&command);
1366   // "[相]"
1367 }
1368 
TEST_F(SessionTest,KeepFixedCandidateAfterSegmentWidthExpand)1369 TEST_F(SessionTest, KeepFixedCandidateAfterSegmentWidthExpand) {
1370   // Issue#1271099
1371   Segments segments;
1372   Segment *segment;
1373   Segment::Candidate *candidate;
1374   std::unique_ptr<Session> session(new Session(engine_.get()));
1375   InitSessionToPrecomposition(session.get());
1376 
1377   commands::Command command;
1378   InsertCharacterChars("bariniryokouniitta", session.get(), &command);
1379   // "ばりにりょこうにいった[]"
1380 
1381   segment = segments.add_segment();
1382   segment->set_key("ばりに");
1383   candidate = segment->add_candidate();
1384   candidate->value = "バリに";
1385   candidate = segment->add_candidate();
1386   candidate->value = "針に";
1387 
1388   segment = segments.add_segment();
1389   segment->set_key("りょこうに");
1390   candidate = segment->add_candidate();
1391   candidate->value = "旅行に";
1392 
1393   segment = segments.add_segment();
1394   segment->set_key("いった");
1395   candidate = segment->add_candidate();
1396   candidate->value = "行った";
1397 
1398   ConversionRequest request;
1399   SetComposer(session.get(), &request);
1400   FillT13Ns(request, &segments);
1401   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1402 
1403   command.Clear();
1404   session->Convert(&command);
1405   // ex. "[バリに]旅行に行った"
1406   EXPECT_EQ("バリに旅行に行った", GetComposition(command));
1407   command.Clear();
1408   session->ConvertNext(&command);
1409   // ex. "[針に]旅行に行った"
1410   const string first_segment = command.output().preedit().segment(0).value();
1411 
1412   segment = segments.mutable_segment(0);
1413   segment->set_segment_type(Segment::FIXED_VALUE);
1414   segment->move_candidate(1, 0);
1415   GetConverterMock()->SetCommitSegmentValue(&segments, true);
1416 
1417   command.Clear();
1418   session->SegmentFocusRight(&command);
1419   // ex. "針に[旅行に]行った"
1420   // Make sure the first segment (i.e. "針に" in the above case) remains
1421   // after moving the focused segment right.
1422   EXPECT_EQ(first_segment, command.output().preedit().segment(0).value());
1423 
1424   segment = segments.mutable_segment(1);
1425   segment->set_key("りょこうにい");
1426   candidate = segment->mutable_candidate(0);
1427   candidate->value = "旅行に行";
1428 
1429   segment = segments.mutable_segment(2);
1430   segment->set_key("った");
1431   candidate = segment->mutable_candidate(0);
1432   candidate->value = "った";
1433 
1434   GetConverterMock()->SetResizeSegment1(&segments, true);
1435 
1436   command.Clear();
1437   session->SegmentWidthExpand(&command);
1438   // ex. "針に[旅行に行]った"
1439 
1440   // Make sure the first segment (i.e. "針に" in the above case) remains
1441   // after expanding the focused segment.
1442   EXPECT_EQ(first_segment, command.output().preedit().segment(0).value());
1443 }
1444 
TEST_F(SessionTest,CommitSegment)1445 TEST_F(SessionTest, CommitSegment) {
1446   Segments segments;
1447   Segment *segment;
1448   Segment::Candidate *candidate;
1449 
1450   // Issue#1560608
1451   std::unique_ptr<Session> session(new Session(engine_.get()));
1452   InitSessionToPrecomposition(session.get());
1453 
1454   commands::Command command;
1455   InsertCharacterChars("watasinonamae", session.get(), &command);
1456   // "わたしのなまえ[]"
1457 
1458   segment = segments.add_segment();
1459   segment->set_key("わたしの");
1460   candidate = segment->add_candidate();
1461   candidate->value = "私の";
1462   candidate = segment->add_candidate();
1463   candidate->value = "わたしの";
1464   candidate = segment->add_candidate();
1465   candidate->value = "渡しの";
1466 
1467   segment = segments.add_segment();
1468   segment->set_key("なまえ");
1469   candidate = segment->add_candidate();
1470   candidate->value = "名前";
1471 
1472   ConversionRequest request;
1473   SetComposer(session.get(), &request);
1474   FillT13Ns(request, &segments);
1475   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1476 
1477   command.Clear();
1478   session->Convert(&command);
1479   EXPECT_EQ(0, command.output().candidates().focused_index());
1480   // "[私の]名前"
1481 
1482   command.Clear();
1483   session->ConvertNext(&command);
1484   EXPECT_EQ(1, command.output().candidates().focused_index());
1485   // "[わたしの]名前"
1486 
1487   command.Clear();
1488   session->ConvertNext(&command);
1489   // "[渡しの]名前" showing a candidate window
1490   EXPECT_EQ(2, command.output().candidates().focused_index());
1491 
1492   segment = segments.mutable_segment(0);
1493   segment->set_segment_type(Segment::FIXED_VALUE);
1494   segment->move_candidate(2, 0);
1495 
1496   GetConverterMock()->SetCommitSegments(&segments, true);
1497 
1498   command.Clear();
1499   session->CommitSegment(&command);
1500   // "渡しの" + "[名前]"
1501   EXPECT_EQ(0, command.output().candidates().focused_index());
1502 }
1503 
TEST_F(SessionTest,CommitSegmentAt2ndSegment)1504 TEST_F(SessionTest, CommitSegmentAt2ndSegment) {
1505   Segments segments;
1506   Segment *segment;
1507   Segment::Candidate *candidate;
1508   std::unique_ptr<Session> session(new Session(engine_.get()));
1509   InitSessionToPrecomposition(session.get());
1510 
1511   commands::Command command;
1512   InsertCharacterChars("watasinohaha", session.get(), &command);
1513   // "わたしのはは[]"
1514 
1515   segment = segments.add_segment();
1516   segment->set_key("わたしの");
1517   candidate = segment->add_candidate();
1518   candidate->value = "私の";
1519   segment = segments.add_segment();
1520   segment->set_key("はは");
1521   candidate = segment->add_candidate();
1522   candidate->value = "母";
1523 
1524   ConversionRequest request;
1525   SetComposer(session.get(), &request);
1526   FillT13Ns(request, &segments);
1527   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1528 
1529   command.Clear();
1530   session->Convert(&command);
1531   // "[私の]母"
1532 
1533   command.Clear();
1534   session->SegmentFocusRight(&command);
1535   // "私の[母]"
1536 
1537   segment->set_segment_type(Segment::FIXED_VALUE);
1538   segment->move_candidate(1, 0);
1539   GetConverterMock()->SetCommitSegments(&segments, true);
1540 
1541   command.Clear();
1542   session->CommitSegment(&command);
1543   // "私の" + "[母]"
1544 
1545   segment->set_key("は");
1546   candidate->value = "葉";
1547   segment = segments.add_segment();
1548   segment->set_key("は");
1549   candidate = segment->add_candidate();
1550   candidate->value = "は";
1551   segments.pop_front_segment();
1552   GetConverterMock()->SetResizeSegment1(&segments, true);
1553 
1554   command.Clear();
1555   session->SegmentWidthShrink(&command);
1556   // "私の" + "[葉]は"
1557   EXPECT_EQ(2, command.output().preedit().segment_size());
1558 }
1559 
TEST_F(SessionTest,Transliterations)1560 TEST_F(SessionTest, Transliterations) {
1561   Segments segments;
1562   Segment *segment;
1563   Segment::Candidate *candidate;
1564   std::unique_ptr<Session> session(new Session(engine_.get()));
1565   InitSessionToPrecomposition(session.get());
1566   commands::Command command;
1567   InsertCharacterChars("jishin", session.get(), &command);
1568 
1569   segment = segments.add_segment();
1570   segment->set_key("じしん");
1571   candidate = segment->add_candidate();
1572   candidate->value = "自信";
1573   candidate = segment->add_candidate();
1574   candidate->value = "自身";
1575 
1576   ConversionRequest request;
1577   SetComposer(session.get(), &request);
1578   FillT13Ns(request, &segments);
1579   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1580 
1581   command.Clear();
1582   session->Convert(&command);
1583 
1584   command.Clear();
1585   session->ConvertNext(&command);
1586 
1587   command.Clear();
1588   session->TranslateHalfASCII(&command);
1589   EXPECT_SINGLE_SEGMENT("jishin", command);
1590 
1591   command.Clear();
1592   session->TranslateHalfASCII(&command);
1593   EXPECT_SINGLE_SEGMENT("JISHIN", command);
1594 
1595   command.Clear();
1596   session->TranslateHalfASCII(&command);
1597   EXPECT_SINGLE_SEGMENT("Jishin", command);
1598 
1599   command.Clear();
1600   session->TranslateHalfASCII(&command);
1601   EXPECT_SINGLE_SEGMENT("jishin", command);
1602 }
1603 
TEST_F(SessionTest,ConvertToTransliteration)1604 TEST_F(SessionTest, ConvertToTransliteration) {
1605   Segments segments;
1606   Segment *segment;
1607   Segment::Candidate *candidate;
1608 
1609   std::unique_ptr<Session> session(new Session(engine_.get()));
1610   InitSessionToPrecomposition(session.get());
1611   commands::Command command;
1612   InsertCharacterChars("jishin", session.get(), &command);
1613 
1614   segment = segments.add_segment();
1615   segment->set_key("じしん");
1616   candidate = segment->add_candidate();
1617   candidate->value = "自信";
1618   candidate = segment->add_candidate();
1619   candidate->value = "自身";
1620 
1621   ConversionRequest request;
1622   SetComposer(session.get(), &request);
1623   FillT13Ns(request, &segments);
1624   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1625 
1626   command.Clear();
1627   session->ConvertToHalfASCII(&command);
1628   EXPECT_SINGLE_SEGMENT("jishin", command);
1629 
1630   command.Clear();
1631   session->ConvertToHalfASCII(&command);
1632   EXPECT_SINGLE_SEGMENT("JISHIN", command);
1633 
1634   command.Clear();
1635   session->ConvertToHalfASCII(&command);
1636   EXPECT_SINGLE_SEGMENT("Jishin", command);
1637 
1638   command.Clear();
1639   session->ConvertToHalfASCII(&command);
1640   EXPECT_SINGLE_SEGMENT("jishin", command);
1641 }
1642 
TEST_F(SessionTest,ConvertToTransliterationWithMultipleSegments)1643 TEST_F(SessionTest, ConvertToTransliterationWithMultipleSegments) {
1644   std::unique_ptr<Session> session(new Session(engine_.get()));
1645   InitSessionToPrecomposition(session.get());
1646 
1647   commands::Command command;
1648   InsertCharacterChars("like", session.get(), &command);
1649 
1650   Segments segments;
1651   SetLike(&segments);
1652   ConversionRequest request;
1653   SetComposer(session.get(), &request);
1654   FillT13Ns(request, &segments);
1655   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1656 
1657   // Convert
1658   command.Clear();
1659   session->Convert(&command);
1660   {  // Check the conversion #1
1661     const commands::Output &output = command.output();
1662     EXPECT_FALSE(output.has_result());
1663     EXPECT_TRUE(output.has_preedit());
1664     EXPECT_FALSE(output.has_candidates());
1665 
1666     const commands::Preedit &conversion = output.preedit();
1667     EXPECT_EQ(2, conversion.segment_size());
1668     EXPECT_EQ("ぃ", conversion.segment(0).value());
1669     EXPECT_EQ("家", conversion.segment(1).value());
1670   }
1671 
1672   // TranslateHalfASCII
1673   command.Clear();
1674   session->TranslateHalfASCII(&command);
1675   {  // Check the conversion #2
1676     const commands::Output &output = command.output();
1677     EXPECT_FALSE(output.has_result());
1678     EXPECT_TRUE(output.has_preedit());
1679     EXPECT_FALSE(output.has_candidates());
1680 
1681     const commands::Preedit &conversion = output.preedit();
1682     EXPECT_EQ(2, conversion.segment_size());
1683     EXPECT_EQ("li", conversion.segment(0).value());
1684   }
1685 }
1686 
TEST_F(SessionTest,ConvertToHalfWidth)1687 TEST_F(SessionTest, ConvertToHalfWidth) {
1688   std::unique_ptr<Session> session(new Session(engine_.get()));
1689   InitSessionToPrecomposition(session.get());
1690   commands::Command command;
1691   InsertCharacterChars("abc", session.get(), &command);
1692 
1693   Segments segments;
1694   {  // Initialize segments.
1695     Segment *segment = segments.add_segment();
1696     segment->set_key("あbc");
1697     segment->add_candidate()->value = "あべし";
1698   }
1699   ConversionRequest request;
1700   SetComposer(session.get(), &request);
1701   FillT13Ns(request, &segments);
1702   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1703 
1704   command.Clear();
1705   session->ConvertToHalfWidth(&command);
1706   EXPECT_SINGLE_SEGMENT("アbc", command);
1707 
1708   command.Clear();
1709   session->ConvertToFullASCII(&command);
1710   // The output is "abc".
1711 
1712   command.Clear();
1713   session->ConvertToHalfWidth(&command);
1714   EXPECT_SINGLE_SEGMENT("abc", command);
1715 }
1716 
TEST_F(SessionTest,ConvertConsonantsToFullAlphanumeric)1717 TEST_F(SessionTest, ConvertConsonantsToFullAlphanumeric) {
1718   Segments segments;
1719   Segment *segment;
1720   Segment::Candidate *candidate;
1721 
1722   std::unique_ptr<Session> session(new Session(engine_.get()));
1723   InitSessionToPrecomposition(session.get());
1724   commands::Command command;
1725   InsertCharacterChars("dvd", session.get(), &command);
1726 
1727   segment = segments.add_segment();
1728   segment->set_key("dvd");
1729   candidate = segment->add_candidate();
1730   candidate->value = "DVD";
1731   candidate = segment->add_candidate();
1732   candidate->value = "dvd";
1733 
1734   ConversionRequest request;
1735   SetComposer(session.get(), &request);
1736   FillT13Ns(request, &segments);
1737   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1738 
1739   command.Clear();
1740   session->ConvertToFullASCII(&command);
1741   EXPECT_SINGLE_SEGMENT("dvd", command);
1742 
1743   command.Clear();
1744   session->ConvertToFullASCII(&command);
1745   EXPECT_SINGLE_SEGMENT("DVD", command);
1746 
1747   command.Clear();
1748   session->ConvertToFullASCII(&command);
1749   EXPECT_SINGLE_SEGMENT("Dvd", command);
1750 
1751   command.Clear();
1752   session->ConvertToFullASCII(&command);
1753   EXPECT_SINGLE_SEGMENT("dvd", command);
1754 }
1755 
TEST_F(SessionTest,ConvertConsonantsToFullAlphanumericWithoutCascadingWindow)1756 TEST_F(SessionTest, ConvertConsonantsToFullAlphanumericWithoutCascadingWindow) {
1757   commands::Command command;
1758   Segments segments;
1759   Segment *segment;
1760   Segment::Candidate *candidate;
1761 
1762   std::unique_ptr<Session> session(new Session(engine_.get()));
1763 
1764   config::Config config;
1765   config.set_use_cascading_window(false);
1766   session->SetConfig(&config);
1767 
1768   InitSessionToPrecomposition(session.get());
1769   InsertCharacterChars("dvd", session.get(), &command);
1770 
1771   segment = segments.add_segment();
1772   segment->set_key("dvd");
1773   candidate = segment->add_candidate();
1774   candidate->value = "DVD";
1775   candidate = segment->add_candidate();
1776   candidate->value = "dvd";
1777 
1778   ConversionRequest request;
1779   SetComposer(session.get(), &request);
1780   FillT13Ns(request, &segments);
1781   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1782 
1783   command.Clear();
1784   session->ConvertToFullASCII(&command);
1785   EXPECT_SINGLE_SEGMENT("dvd", command);
1786 
1787   command.Clear();
1788   session->ConvertToFullASCII(&command);
1789   EXPECT_SINGLE_SEGMENT("DVD", command);
1790 
1791   command.Clear();
1792   session->ConvertToFullASCII(&command);
1793   EXPECT_SINGLE_SEGMENT("Dvd", command);
1794 
1795   command.Clear();
1796   session->ConvertToFullASCII(&command);
1797   EXPECT_SINGLE_SEGMENT("dvd", command);
1798 }
1799 
1800 // Convert input string to Hiragana, Katakana, and Half Katakana
TEST_F(SessionTest,SwitchKanaType)1801 TEST_F(SessionTest, SwitchKanaType) {
1802   {  // From composition mode.
1803     std::unique_ptr<Session> session(new Session(engine_.get()));
1804     InitSessionToPrecomposition(session.get());
1805     commands::Command command;
1806     InsertCharacterChars("abc", session.get(), &command);
1807 
1808     Segments segments;
1809     {  // Initialize segments.
1810       Segment *segment = segments.add_segment();
1811       segment->set_key("あbc");
1812       segment->add_candidate()->value = "あべし";
1813     }
1814 
1815     ConversionRequest request;
1816     SetComposer(session.get(), &request);
1817     FillT13Ns(request, &segments);
1818     GetConverterMock()->SetStartConversionForRequest(&segments, true);
1819 
1820     command.Clear();
1821     session->SwitchKanaType(&command);
1822     EXPECT_SINGLE_SEGMENT("アbc", command);
1823 
1824     command.Clear();
1825     session->SwitchKanaType(&command);
1826     EXPECT_SINGLE_SEGMENT("アbc", command);
1827 
1828     command.Clear();
1829     session->SwitchKanaType(&command);
1830     EXPECT_SINGLE_SEGMENT("あbc", command);
1831 
1832     command.Clear();
1833     session->SwitchKanaType(&command);
1834     EXPECT_SINGLE_SEGMENT("アbc", command);
1835   }
1836 
1837   {  // From conversion mode.
1838     std::unique_ptr<Session> session(new Session(engine_.get()));
1839     InitSessionToPrecomposition(session.get());
1840     commands::Command command;
1841     InsertCharacterChars("kanji", session.get(), &command);
1842 
1843     Segments segments;
1844     {  // Initialize segments.
1845       Segment *segment = segments.add_segment();
1846       segment->set_key("かんじ");
1847       segment->add_candidate()->value = "漢字";
1848     }
1849 
1850     ConversionRequest request;
1851     SetComposer(session.get(), &request);
1852     FillT13Ns(request, &segments);
1853     GetConverterMock()->SetStartConversionForRequest(&segments, true);
1854 
1855     command.Clear();
1856     session->Convert(&command);
1857     EXPECT_SINGLE_SEGMENT("漢字", command);
1858 
1859     command.Clear();
1860     session->SwitchKanaType(&command);
1861     EXPECT_SINGLE_SEGMENT("かんじ", command);
1862 
1863     command.Clear();
1864     session->SwitchKanaType(&command);
1865     EXPECT_SINGLE_SEGMENT("カンジ", command);
1866 
1867     command.Clear();
1868     session->SwitchKanaType(&command);
1869     EXPECT_SINGLE_SEGMENT(
1870         "カンジ", command);
1871 
1872     command.Clear();
1873     session->SwitchKanaType(&command);
1874     EXPECT_SINGLE_SEGMENT("かんじ", command);
1875   }
1876 }
1877 
1878 // Rotate input mode among Hiragana, Katakana, and Half Katakana
TEST_F(SessionTest,InputModeSwitchKanaType)1879 TEST_F(SessionTest, InputModeSwitchKanaType) {
1880   std::unique_ptr<Session> session(new Session(engine_.get()));
1881   InitSessionToPrecomposition(session.get());
1882   commands::Command command;
1883 
1884   // HIRAGANA
1885   InsertCharacterChars("a", session.get(), &command);
1886   EXPECT_EQ("あ", GetComposition(command));
1887   EXPECT_TRUE(command.output().has_mode());
1888   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
1889 
1890   // HIRAGANA to FULL_KATAKANA
1891   command.Clear();
1892   session->Commit(&command);
1893   command.Clear();
1894   session->InputModeSwitchKanaType(&command);
1895   InsertCharacterChars("a", session.get(), &command);
1896   EXPECT_EQ("ア", GetComposition(command));
1897   EXPECT_TRUE(command.output().has_mode());
1898   EXPECT_EQ(commands::FULL_KATAKANA, command.output().mode());
1899 
1900   // FULL_KATRAKANA to HALF_KATAKANA
1901   command.Clear();
1902   session->Commit(&command);
1903   command.Clear();
1904   session->InputModeSwitchKanaType(&command);
1905   InsertCharacterChars("a", session.get(), &command);
1906   EXPECT_EQ("ア", GetComposition(command));
1907   EXPECT_TRUE(command.output().has_mode());
1908   EXPECT_EQ(commands::HALF_KATAKANA, command.output().mode());
1909 
1910   // HALF_KATAKANA to HIRAGANA
1911   command.Clear();
1912   session->Commit(&command);
1913   command.Clear();
1914   session->InputModeSwitchKanaType(&command);
1915   InsertCharacterChars("a", session.get(), &command);
1916   EXPECT_EQ("あ", GetComposition(command));
1917   EXPECT_TRUE(command.output().has_mode());
1918   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
1919 
1920   // To Half ASCII mode.
1921   command.Clear();
1922   session->Commit(&command);
1923   command.Clear();
1924   session->InputModeHalfASCII(&command);
1925   InsertCharacterChars("a", session.get(), &command);
1926   EXPECT_EQ("a", GetComposition(command));
1927   EXPECT_TRUE(command.output().has_mode());
1928   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
1929 
1930   // HALF_ASCII to HALF_ASCII
1931   command.Clear();
1932   session->Commit(&command);
1933   command.Clear();
1934   session->InputModeSwitchKanaType(&command);
1935   InsertCharacterChars("a", session.get(), &command);
1936   EXPECT_EQ("a", GetComposition(command));
1937   EXPECT_TRUE(command.output().has_mode());
1938   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
1939 
1940   // To Full ASCII mode.
1941   command.Clear();
1942   session->Commit(&command);
1943   command.Clear();
1944   session->InputModeFullASCII(&command);
1945   InsertCharacterChars("a", session.get(), &command);
1946   EXPECT_EQ("a", GetComposition(command));
1947   EXPECT_TRUE(command.output().has_mode());
1948   EXPECT_EQ(commands::FULL_ASCII, command.output().mode());
1949 
1950   // FULL_ASCII to FULL_ASCII
1951   command.Clear();
1952   session->Commit(&command);
1953   command.Clear();
1954   session->InputModeSwitchKanaType(&command);
1955   InsertCharacterChars("a", session.get(), &command);
1956   EXPECT_EQ("a", GetComposition(command));
1957   EXPECT_TRUE(command.output().has_mode());
1958   EXPECT_EQ(commands::FULL_ASCII, command.output().mode());
1959 }
1960 
TEST_F(SessionTest,TranslateHalfWidth)1961 TEST_F(SessionTest, TranslateHalfWidth) {
1962   std::unique_ptr<Session> session(new Session(engine_.get()));
1963   InitSessionToPrecomposition(session.get());
1964   commands::Command command;
1965   InsertCharacterChars("abc", session.get(), &command);
1966 
1967   command.Clear();
1968   session->TranslateHalfWidth(&command);
1969   EXPECT_SINGLE_SEGMENT("アbc", command);
1970 
1971   command.Clear();
1972   session->TranslateFullASCII(&command);
1973   EXPECT_SINGLE_SEGMENT("abc", command);
1974 
1975   command.Clear();
1976   session->TranslateHalfWidth(&command);
1977   EXPECT_SINGLE_SEGMENT("abc", command);
1978 }
1979 
TEST_F(SessionTest,UpdatePreferences)1980 TEST_F(SessionTest, UpdatePreferences) {
1981   std::unique_ptr<Session> session(new Session(engine_.get()));
1982   InitSessionToPrecomposition(session.get());
1983   commands::Command command;
1984   InsertCharacterChars("aiueo", session.get(), &command);
1985   Segments segments;
1986   SetAiueo(&segments);
1987 
1988   ConversionRequest request;
1989   SetComposer(session.get(), &request);
1990   FillT13Ns(request, &segments);
1991   GetConverterMock()->SetStartConversionForRequest(&segments, true);
1992 
1993   SetSendKeyCommand("SPACE", &command);
1994   command.mutable_input()->mutable_config()->set_use_cascading_window(false);
1995   session->SendKey(&command);
1996   SetSendKeyCommand("SPACE", &command);
1997   session->SendKey(&command);
1998 
1999   const size_t no_cascading_cand_size =
2000       command.output().candidates().candidate_size();
2001 
2002   command.Clear();
2003   session->ConvertCancel(&command);
2004 
2005   SetSendKeyCommand("SPACE", &command);
2006   command.mutable_input()->mutable_config()->set_use_cascading_window(true);
2007   session->SendKey(&command);
2008   SetSendKeyCommand("SPACE", &command);
2009   session->SendKey(&command);
2010 
2011   const size_t cascading_cand_size =
2012       command.output().candidates().candidate_size();
2013 
2014 #if defined(OS_LINUX) || defined(OS_ANDROID) || OS_NACL
2015   EXPECT_EQ(no_cascading_cand_size, cascading_cand_size);
2016 #else  // defined(OS_LINUX) || defined(OS_ANDROID) || OS_NACL
2017   EXPECT_GT(no_cascading_cand_size, cascading_cand_size);
2018 #endif  // defined(OS_LINUX) || defined(OS_ANDROID) || OS_NACL
2019 
2020   command.Clear();
2021   session->ConvertCancel(&command);
2022 
2023   // On MS-IME keymap, EISU key does nothing.
2024   SetSendKeyCommand("EISU", &command);
2025   command.mutable_input()->mutable_config()->set_session_keymap(
2026       config::Config::MSIME);
2027   session->SendKey(&command);
2028   EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
2029   EXPECT_EQ(commands::HALF_ASCII, command.output().status().comeback_mode());
2030 
2031   // On KOTOERI keymap, EISU key does "ToggleAlphanumericMode".
2032   SetSendKeyCommand("EISU", &command);
2033   command.mutable_input()->mutable_config()->set_session_keymap(
2034       config::Config::KOTOERI);
2035   session->SendKey(&command);
2036   EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
2037   EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
2038 }
2039 
TEST_F(SessionTest,RomajiInput)2040 TEST_F(SessionTest, RomajiInput) {
2041   Segments segments;
2042   Segment *segment;
2043   Segment::Candidate *candidate;
2044   composer::Table table;
2045   table.AddRule("pa", "ぱ", "");
2046   table.AddRule("n", "ん", "");
2047   table.AddRule("na", "な", "");
2048   // This rule makes the "n" rule ambiguous.
2049 
2050   std::unique_ptr<Session> session(new Session(engine_.get()));
2051   session->get_internal_composer_only_for_unittest()->SetTable(&table);
2052   InitSessionToPrecomposition(session.get());
2053 
2054   commands::Command command;
2055   InsertCharacterChars("pan", session.get(), &command);
2056 
2057   EXPECT_EQ("ぱn",
2058             command.output().preedit().segment(0).value());
2059 
2060   command.Clear();
2061 
2062   segment = segments.add_segment();
2063   segment->set_key("ぱん");
2064   candidate = segment->add_candidate();
2065   candidate->value = "パン";
2066 
2067   ConversionRequest request;
2068   SetComposer(session.get(), &request);
2069   FillT13Ns(request, &segments);
2070   GetConverterMock()->SetStartConversionForRequest(&segments, true);
2071 
2072   session->ConvertToHiragana(&command);
2073   EXPECT_SINGLE_SEGMENT("ぱん", command);
2074 
2075   command.Clear();
2076   session->ConvertToHalfASCII(&command);
2077   EXPECT_SINGLE_SEGMENT("pan", command);
2078 }
2079 
2080 
TEST_F(SessionTest,KanaInput)2081 TEST_F(SessionTest, KanaInput) {
2082   Segments segments;
2083   Segment *segment;
2084   Segment::Candidate *candidate;
2085   composer::Table table;
2086   table.AddRule("す゛", "ず", "");
2087 
2088   std::unique_ptr<Session> session(new Session(engine_.get()));
2089   session->get_internal_composer_only_for_unittest()->SetTable(&table);
2090   InitSessionToPrecomposition(session.get());
2091 
2092   commands::Command command;
2093   SetSendKeyCommand("m", &command);
2094   command.mutable_input()->mutable_key()->set_key_string("も");
2095   session->SendKey(&command);
2096 
2097   SetSendKeyCommand("r", &command);
2098   command.mutable_input()->mutable_key()->set_key_string("す");
2099   session->SendKey(&command);
2100 
2101   SetSendKeyCommand("@", &command);
2102   command.mutable_input()->mutable_key()->set_key_string("゛");
2103   session->SendKey(&command);
2104 
2105   SetSendKeyCommand("h", &command);
2106   command.mutable_input()->mutable_key()->set_key_string("く");
2107   session->SendKey(&command);
2108 
2109   SetSendKeyCommand("!", &command);
2110   command.mutable_input()->mutable_key()->set_key_string("!");
2111   session->SendKey(&command);
2112 
2113   EXPECT_EQ("もずく!", command.output().preedit().segment(0).value());
2114 
2115   segment = segments.add_segment();
2116   segment->set_key("もずく!");
2117   candidate = segment->add_candidate();
2118   candidate->value = "もずく!";
2119 
2120   ConversionRequest request;
2121   SetComposer(session.get(), &request);
2122   FillT13Ns(request, &segments);
2123   GetConverterMock()->SetStartConversionForRequest(&segments, true);
2124 
2125   command.Clear();
2126   session->ConvertToHalfASCII(&command);
2127   EXPECT_SINGLE_SEGMENT("mr@h!", command);
2128 }
2129 
TEST_F(SessionTest,ExceededComposition)2130 TEST_F(SessionTest, ExceededComposition) {
2131   Segments segments;
2132   Segment *segment;
2133   Segment::Candidate *candidate;
2134   std::unique_ptr<Session> session(new Session(engine_.get()));
2135   InitSessionToPrecomposition(session.get());
2136   commands::Command command;
2137 
2138   const string exceeded_preedit(500, 'a');
2139   ASSERT_EQ(500, exceeded_preedit.size());
2140   InsertCharacterChars(exceeded_preedit, session.get(), &command);
2141 
2142   string long_a;
2143   for (int i = 0; i < 500; ++i) {
2144     long_a += "あ";
2145   }
2146   segment = segments.add_segment();
2147   segment->set_key(long_a);
2148   candidate = segment->add_candidate();
2149   candidate->value = long_a;
2150 
2151   ConversionRequest request;
2152   SetComposer(session.get(), &request);
2153   FillT13Ns(request, &segments);
2154   GetConverterMock()->SetStartConversionForRequest(&segments, true);
2155 
2156   command.Clear();
2157   session->Convert(&command);
2158   EXPECT_FALSE(command.output().has_candidates());
2159 
2160   // The status should remain the preedit status, although the
2161   // previous command was convert.  The next command makes sure that
2162   // the preedit will disappear by canceling the preedit status.
2163   command.Clear();
2164   command.mutable_input()->mutable_key()->set_special_key(
2165       commands::KeyEvent::ESCAPE);
2166   EXPECT_FALSE(command.output().has_preedit());
2167 }
2168 
TEST_F(SessionTest,OutputAllCandidateWords)2169 TEST_F(SessionTest, OutputAllCandidateWords) {
2170   std::unique_ptr<Session> session(new Session(engine_.get()));
2171   InitSessionToPrecomposition(session.get());
2172   commands::Command command;
2173 
2174   Segments segments;
2175   SetAiueo(&segments);
2176   InsertCharacterChars("aiueo", session.get(), &command);
2177 
2178   ConversionRequest request;
2179   SetComposer(session.get(), &request);
2180   FillT13Ns(request, &segments);
2181   GetConverterMock()->SetStartConversionForRequest(&segments, true);
2182 
2183   command.Clear();
2184   session->Convert(&command);
2185   {
2186     const commands::Output &output = command.output();
2187     EXPECT_TRUE(output.has_all_candidate_words());
2188 
2189     EXPECT_EQ(0, output.all_candidate_words().focused_index());
2190     EXPECT_EQ(commands::CONVERSION, output.all_candidate_words().category());
2191 #if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_NACL)
2192     // Cascading window is not supported on Linux, so the size of
2193     // candidate words is different from other platform.
2194     // TODO(komatsu): Modify the client for Linux to explicitly change
2195     // the preference rather than relying on the exceptional default value.
2196     // [ "あいうえお", "アイウエオ",
2197     //   "aiueo" (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2198     //   "aiueo"  (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2199     //   "アイウエオ" (t13n) ]
2200     EXPECT_EQ(9, output.all_candidate_words().candidates_size());
2201 #else  // OS_LINUX || OS_ANDROID || OS_NACL
2202     // [ "あいうえお", "アイウエオ", "アイウエオ" (t13n), "あいうえお" (t13n),
2203     //   "aiueo" (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2204     //   "aiueo"  (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2205     //   "アイウエオ" (t13n) ]
2206     EXPECT_EQ(11, output.all_candidate_words().candidates_size());
2207 #endif  // OS_LINUX || OS_ANDROID || OS_NACL
2208   }
2209 
2210   command.Clear();
2211   session->ConvertNext(&command);
2212   {
2213     const commands::Output &output = command.output();
2214 
2215     EXPECT_TRUE(output.has_all_candidate_words());
2216 
2217     EXPECT_EQ(1, output.all_candidate_words().focused_index());
2218     EXPECT_EQ(commands::CONVERSION, output.all_candidate_words().category());
2219 #if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_NACL)
2220     // Cascading window is not supported on Linux, so the size of
2221     // candidate words is different from other platform.
2222     // TODO(komatsu): Modify the client for Linux to explicitly change
2223     // the preference rather than relying on the exceptional default value.
2224     // [ "あいうえお", "アイウエオ", "アイウエオ" (t13n), "あいうえお" (t13n),
2225     //   "aiueo" (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2226     //   "aiueo"  (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2227     //   "アイウエオ" (t13n) ]
2228     EXPECT_EQ(9, output.all_candidate_words().candidates_size());
2229 #else  // OS_LINUX || OS_ANDROID || OS_NACL
2230     // [ "あいうえお", "アイウエオ",
2231     //   "aiueo" (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2232     //   "aiueo"  (t13n), "AIUEO" (t13n), "Aieuo" (t13n),
2233     //   "アイウエオ" (t13n) ]
2234     EXPECT_EQ(11, output.all_candidate_words().candidates_size());
2235 #endif  // OS_LINUX || OS_ANDROID || OS_NACL
2236   }
2237 }
2238 
TEST_F(SessionTest,UndoForComposition)2239 TEST_F(SessionTest, UndoForComposition) {
2240   Session session(engine_.get());
2241   InitSessionToPrecomposition(&session);
2242 
2243   // Enable zero query suggest.
2244   commands::Request request;
2245   SetupZeroQuerySuggestionReady(true, &session, &request);
2246 
2247   // Undo requires capability DELETE_PRECEDING_TEXT.
2248   commands::Capability capability;
2249   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2250   session.set_client_capability(capability);
2251 
2252   commands::Command command;
2253   Segments segments;
2254   Segments empty_segments;
2255 
2256   {  // Undo for CommitFirstSuggestion
2257     SetAiueo(&segments);
2258     GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
2259     InsertCharacterChars("ai", &session, &command);
2260     ConversionRequest request;
2261     SetComposer(&session, &request);
2262     EXPECT_EQ("あい", GetComposition(command));
2263 
2264     command.Clear();
2265     GetConverterMock()->SetFinishConversion(&empty_segments, true);
2266     session.CommitFirstSuggestion(&command);
2267     EXPECT_FALSE(command.output().has_preedit());
2268     EXPECT_RESULT("あいうえお", command);
2269     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
2270 
2271     command.Clear();
2272     session.Undo(&command);
2273     EXPECT_FALSE(command.output().has_result());
2274     EXPECT_TRUE(command.output().has_deletion_range());
2275     EXPECT_EQ(-5, command.output().deletion_range().offset());
2276     EXPECT_EQ(5, command.output().deletion_range().length());
2277     EXPECT_SINGLE_SEGMENT("あい", command);
2278     EXPECT_EQ(2, command.output().candidates().size());
2279     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
2280   }
2281 }
2282 
TEST_F(SessionTest,RequestUndo)2283 TEST_F(SessionTest, RequestUndo) {
2284   std::unique_ptr<Session> session(new Session(engine_.get()));
2285 
2286   // It is OK not to check ImeContext::DIRECT because you cannot
2287   // assign any key event to Undo command in DIRECT mode.
2288   // See "session/internal/keymap_interface.h".
2289 
2290   InitSessionToPrecomposition(session.get());
2291   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()))
2292       << "When the UNDO context is empty and the context state is "
2293          "ImeContext::PRECOMPOSITION, UNDO command should be "
2294          "ignored. See b/5553298.";
2295 
2296   InitSessionToPrecomposition(session.get());
2297   SetUndoContext(session.get());
2298   EXPECT_TRUE(TryUndoAndAssertSuccess(session.get()));
2299 
2300   InitSessionToPrecomposition(session.get());
2301   SetUndoContext(session.get());
2302   session->context_->set_state(ImeContext::COMPOSITION);
2303   EXPECT_TRUE(TryUndoAndAssertSuccess(session.get()));
2304 
2305   InitSessionToPrecomposition(session.get());
2306   SetUndoContext(session.get());
2307   session->context_->set_state(ImeContext::CONVERSION);
2308   EXPECT_TRUE(TryUndoAndAssertSuccess(session.get()));
2309 }
2310 
TEST_F(SessionTest,UndoForSingleSegment)2311 TEST_F(SessionTest, UndoForSingleSegment) {
2312   std::unique_ptr<Session> session(new Session(engine_.get()));
2313   InitSessionToPrecomposition(session.get());
2314 
2315   // Undo requires capability DELETE_PRECEDING_TEXT.
2316   commands::Capability capability;
2317   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2318   session->set_client_capability(capability);
2319 
2320   commands::Command command;
2321   Segments segments;
2322 
2323   {  // Create segments
2324     InsertCharacterChars("aiueo", session.get(), &command);
2325     ConversionRequest request;
2326     SetComposer(session.get(), &request);
2327     SetAiueo(&segments);
2328     // Don't use FillT13Ns(). It makes platform dependent segments.
2329     // TODO(hsumita): Makes FillT13Ns() independent from platforms.
2330     Segment::Candidate *candidate;
2331     candidate = segments.mutable_segment(0)->add_candidate();
2332     candidate->value = "aiueo";
2333     candidate = segments.mutable_segment(0)->add_candidate();
2334     candidate->value = "AIUEO";
2335   }
2336 
2337   {  // Undo after commitment of composition
2338     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2339     command.Clear();
2340     session->Convert(&command);
2341     EXPECT_FALSE(command.output().has_result());
2342     EXPECT_PREEDIT("あいうえお", command);
2343 
2344     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2345     command.Clear();
2346     session->Commit(&command);
2347     EXPECT_FALSE(command.output().has_preedit());
2348     EXPECT_RESULT("あいうえお", command);
2349 
2350     command.Clear();
2351     session->Undo(&command);
2352     EXPECT_FALSE(command.output().has_result());
2353     EXPECT_TRUE(command.output().has_deletion_range());
2354     EXPECT_EQ(-5, command.output().deletion_range().offset());
2355     EXPECT_EQ(5, command.output().deletion_range().length());
2356     EXPECT_PREEDIT("あいうえお", command);
2357 
2358     // Undo twice - do nothing and keep the previous status.
2359     command.Clear();
2360     session->Undo(&command);
2361     EXPECT_FALSE(command.output().has_result());
2362     EXPECT_FALSE(command.output().has_deletion_range());
2363     EXPECT_PREEDIT("あいうえお", command);
2364   }
2365 
2366   {  // Undo after commitment of conversion
2367     command.Clear();
2368     session->ConvertNext(&command);
2369     EXPECT_FALSE(command.output().has_result());
2370     EXPECT_PREEDIT("アイウエオ", command);
2371 
2372     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2373     command.Clear();
2374     session->Commit(&command);
2375     EXPECT_FALSE(command.output().has_preedit());
2376     EXPECT_RESULT("アイウエオ", command);
2377 
2378     command.Clear();
2379     session->Undo(&command);
2380     EXPECT_FALSE(command.output().has_result());
2381     EXPECT_TRUE(command.output().has_deletion_range());
2382     EXPECT_EQ(-5, command.output().deletion_range().offset());
2383     EXPECT_EQ(5, command.output().deletion_range().length());
2384     EXPECT_PREEDIT("アイウエオ", command);
2385 
2386     // Undo twice - do nothing and keep the previous status.
2387     command.Clear();
2388     session->Undo(&command);
2389     EXPECT_FALSE(command.output().has_result());
2390     EXPECT_FALSE(command.output().has_deletion_range());
2391     EXPECT_PREEDIT("アイウエオ", command);
2392   }
2393 
2394   {  // Undo after commitment of conversion with Ctrl-Backspace.
2395     command.Clear();
2396     session->ConvertNext(&command);
2397     EXPECT_FALSE(command.output().has_result());
2398     EXPECT_PREEDIT("aiueo", command);
2399 
2400     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2401     command.Clear();
2402     session->Commit(&command);
2403     EXPECT_FALSE(command.output().has_preedit());
2404     EXPECT_RESULT("aiueo", command);
2405 
2406     config::Config config;
2407     config.set_session_keymap(config::Config::MSIME);
2408     session->SetConfig(&config);
2409 
2410     command.Clear();
2411     session->Undo(&command);
2412     EXPECT_FALSE(command.output().has_result());
2413     EXPECT_TRUE(command.output().has_deletion_range());
2414     EXPECT_EQ(-5, command.output().deletion_range().offset());
2415     EXPECT_EQ(5, command.output().deletion_range().length());
2416     EXPECT_PREEDIT("aiueo", command);
2417   }
2418 
2419   {
2420     // If capability does not support DELETE_PRECEDIGN_TEXT, Undo is not
2421     // performed.
2422     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2423     command.Clear();
2424     session->Commit(&command);
2425     EXPECT_FALSE(command.output().has_preedit());
2426     EXPECT_RESULT("aiueo", command);
2427 
2428     // Reset capability
2429     capability.Clear();
2430     session->set_client_capability(capability);
2431 
2432     command.Clear();
2433     session->Undo(&command);
2434     EXPECT_FALSE(command.output().has_result());
2435     EXPECT_FALSE(command.output().has_deletion_range());
2436     EXPECT_FALSE(command.output().has_preedit());
2437   }
2438 }
2439 
TEST_F(SessionTest,ClearUndoContextByKeyEvent_Issue5529702)2440 TEST_F(SessionTest, ClearUndoContextByKeyEvent_Issue5529702) {
2441   std::unique_ptr<Session> session(new Session(engine_.get()));
2442   InitSessionToPrecomposition(session.get());
2443 
2444   // Undo requires capability DELETE_PRECEDING_TEXT.
2445   commands::Capability capability;
2446   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2447   session->set_client_capability(capability);
2448 
2449   SetUndoContext(session.get());
2450 
2451   commands::Command command;
2452 
2453   // Modifier key event does not clear undo context.
2454   SendKey("Shift", session.get(), &command);
2455 
2456   // Ctrl+BS should be consumed as UNDO.
2457   SetSendKeyCommand("Ctrl Backspace", &command);
2458   command.mutable_input()->mutable_config()->set_session_keymap(
2459       config::Config::MSIME);
2460   session->TestSendKey(&command);
2461   EXPECT_TRUE(command.output().consumed());
2462 
2463   // Any other (test) send key event clears undo context.
2464   TestSendKey("LEFT", session.get(), &command);
2465   EXPECT_FALSE(command.output().consumed());
2466 
2467   // Undo context is just cleared. Ctrl+BS should not be consumed b/5553298.
2468   SetSendKeyCommand("Ctrl Backspace", &command);
2469   command.mutable_input()->mutable_config()->set_session_keymap(
2470       config::Config::MSIME);
2471   session->TestSendKey(&command);
2472   EXPECT_FALSE(command.output().consumed());
2473 }
2474 
TEST_F(SessionTest,UndoForMultipleSegments)2475 TEST_F(SessionTest, UndoForMultipleSegments) {
2476   std::unique_ptr<Session> session(new Session(engine_.get()));
2477   InitSessionToPrecomposition(session.get());
2478 
2479   // Undo requires capability DELETE_PRECEDING_TEXT.
2480   commands::Capability capability;
2481   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2482   session->set_client_capability(capability);
2483 
2484   commands::Command command;
2485   Segments segments;
2486 
2487   {  // Create segments
2488     InsertCharacterChars("key1key2key3", session.get(), &command);
2489     ConversionRequest request;
2490     SetComposer(session.get(), &request);
2491 
2492     Segment::Candidate *candidate;
2493     Segment *segment;
2494 
2495     segment = segments.add_segment();
2496     segment->set_key("key1");
2497     candidate = segment->add_candidate();
2498     candidate->value = "cand1-1";
2499     candidate = segment->add_candidate();
2500     candidate->value = "cand1-2";
2501 
2502     segment = segments.add_segment();
2503     segment->set_key("key2");
2504     candidate = segment->add_candidate();
2505     candidate->value = "cand2-1";
2506     candidate = segment->add_candidate();
2507     candidate->value = "cand2-2";
2508 
2509     segment = segments.add_segment();
2510     segment->set_key("key3");
2511     candidate = segment->add_candidate();
2512     candidate->value = "cand3-1";
2513     candidate = segment->add_candidate();
2514     candidate->value = "cand3-2";
2515   }
2516 
2517   {  // Undo for CommitCandidate
2518     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2519     command.Clear();
2520     session->Convert(&command);
2521     EXPECT_FALSE(command.output().has_result());
2522     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2523     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2524 
2525     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2526     command.Clear();
2527     command.mutable_input()->mutable_command()->set_id(1);
2528     session->CommitCandidate(&command);
2529     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2530     EXPECT_RESULT("cand1-2", command);
2531     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2532 
2533     command.Clear();
2534     session->Undo(&command);
2535     EXPECT_FALSE(command.output().has_result());
2536     EXPECT_TRUE(command.output().has_deletion_range());
2537     EXPECT_EQ(-7, command.output().deletion_range().offset());
2538     EXPECT_EQ(7, command.output().deletion_range().length());
2539     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2540     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2541 
2542     // Move to second segment and do the same thing.
2543     command.Clear();
2544     session->SegmentFocusRight(&command);
2545     command.Clear();
2546     command.mutable_input()->mutable_command()->set_id(1);
2547     session->CommitCandidate(&command);
2548     // "cand2-2" is focused
2549     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2550     EXPECT_RESULT("cand1-1cand2-2", command);
2551     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2552 
2553     command.Clear();
2554     session->Undo(&command);
2555     EXPECT_FALSE(command.output().has_result());
2556     EXPECT_TRUE(command.output().has_deletion_range());
2557     EXPECT_EQ(-14, command.output().deletion_range().offset());
2558     EXPECT_EQ(14, command.output().deletion_range().length());
2559     // "cand2-1" is focused
2560     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2561     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2562   }
2563   {  // Undo for CommitSegment
2564     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2565     command.Clear();
2566     session->Convert(&command);
2567     EXPECT_FALSE(command.output().has_result());
2568     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2569     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2570 
2571     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2572     command.Clear();
2573     session->ConvertNext(&command);
2574     EXPECT_EQ("cand1-2cand2-1cand3-1", GetComposition(command));
2575     command.Clear();
2576     session->CommitSegment(&command);
2577     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2578     EXPECT_RESULT("cand1-2", command);
2579     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2580 
2581     command.Clear();
2582     session->Undo(&command);
2583     EXPECT_FALSE(command.output().has_result());
2584     EXPECT_TRUE(command.output().has_deletion_range());
2585     EXPECT_EQ(-7, command.output().deletion_range().offset());
2586     EXPECT_EQ(7, command.output().deletion_range().length());
2587     EXPECT_PREEDIT("cand1-2cand2-1cand3-1", command);
2588     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2589 
2590     // Move to third segment and do the same thing.
2591     command.Clear();
2592     session->SegmentFocusRight(&command);
2593     command.Clear();
2594     session->SegmentFocusRight(&command);
2595     command.Clear();
2596     session->ConvertNext(&command);
2597     EXPECT_PREEDIT("cand1-1cand2-1cand3-2", command);
2598     command.Clear();
2599     session->CommitSegment(&command);
2600     // "cand3-2" is focused
2601     EXPECT_PREEDIT("cand1-1cand2-1cand3-1", command);
2602     EXPECT_RESULT("cand1-1", command);
2603     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2604 
2605     command.Clear();
2606     session->Undo(&command);
2607     EXPECT_FALSE(command.output().has_result());
2608     EXPECT_TRUE(command.output().has_deletion_range());
2609     EXPECT_EQ(-7, command.output().deletion_range().offset());
2610     EXPECT_EQ(7, command.output().deletion_range().length());
2611     // "cand3-2" is focused
2612     EXPECT_PREEDIT("cand1-1cand2-1cand3-2", command);
2613     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2614   }
2615 }
2616 
TEST_F(SessionTest,UndoOrRewind_undo)2617 TEST_F(SessionTest, UndoOrRewind_undo) {
2618   std::unique_ptr<Session> session(new Session(engine_.get()));
2619   InitSessionToPrecomposition(session.get());
2620 
2621   // Undo requires capability DELETE_PRECEDING_TEXT.
2622   commands::Capability capability;
2623   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2624   session->set_client_capability(capability);
2625 
2626 
2627   // Commit twice.
2628   for (size_t i = 0; i < 2; ++i) {
2629     commands::Command command;
2630     Segments segments;
2631     {  // Create segments
2632       InsertCharacterChars("aiueo", session.get(), &command);
2633       ConversionRequest request;
2634       SetComposer(session.get(), &request);
2635       SetAiueo(&segments);
2636       Segment::Candidate *candidate;
2637       candidate = segments.mutable_segment(0)->add_candidate();
2638       candidate->value = "aiueo";
2639       candidate = segments.mutable_segment(0)->add_candidate();
2640       candidate->value = "AIUEO";
2641     }
2642     {
2643       GetConverterMock()->SetStartConversionForRequest(&segments, true);
2644       command.Clear();
2645       session->Convert(&command);
2646       EXPECT_FALSE(command.output().has_result());
2647       EXPECT_PREEDIT("あいうえお", command);
2648 
2649       GetConverterMock()->SetCommitSegmentValue(&segments, true);
2650       command.Clear();
2651       session->Commit(&command);
2652       EXPECT_FALSE(command.output().has_preedit());
2653       EXPECT_RESULT("あいうえお", command);
2654     }
2655   }
2656   // Try UndoOrRewind twice.
2657   // Second trial should not return deletation_range.
2658   commands::Command command;
2659   command.Clear();
2660   session->UndoOrRewind(&command);
2661   EXPECT_FALSE(command.output().has_result());
2662   EXPECT_PREEDIT("あいうえお", command);
2663   EXPECT_TRUE(command.output().has_deletion_range());
2664   command.Clear();
2665   session->UndoOrRewind(&command);
2666   EXPECT_FALSE(command.output().has_result());
2667   EXPECT_PREEDIT("あいうえお", command);
2668   EXPECT_FALSE(command.output().has_deletion_range());
2669 }
2670 
TEST_F(SessionTest,UndoOrRewind_rewind)2671 TEST_F(SessionTest, UndoOrRewind_rewind) {
2672   std::unique_ptr<Session> session(new Session(engine_.get()));
2673   InitSessionToPrecomposition(session.get(), *mobile_request_);
2674 
2675   Segments segments;
2676   {
2677     segments.set_request_type(Segments::SUGGESTION);
2678     Segment *segment;
2679     segment = segments.add_segment();
2680     AddCandidate("e", "e", segment);
2681     AddCandidate("e", "E", segment);
2682   }
2683   GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
2684 
2685   commands::Command command;
2686   InsertCharacterChars("11111", session.get(), &command);
2687   EXPECT_FALSE(command.output().has_result());
2688   EXPECT_PREEDIT("お", command);
2689   EXPECT_FALSE(command.output().has_deletion_range());
2690   EXPECT_TRUE(command.output().has_all_candidate_words());
2691 
2692   command.Clear();
2693   session->UndoOrRewind(&command);
2694   EXPECT_FALSE(command.output().has_result());
2695   EXPECT_PREEDIT("え", command);
2696   EXPECT_FALSE(command.output().has_deletion_range());
2697   EXPECT_TRUE(command.output().has_all_candidate_words());
2698 }
2699 
TEST_F(SessionTest,CommitRawText)2700 TEST_F(SessionTest, CommitRawText) {
2701   {  // From composition mode.
2702     std::unique_ptr<Session> session(new Session(engine_.get()));
2703     InitSessionToPrecomposition(session.get());
2704     commands::Command command;
2705     InsertCharacterChars("abc", session.get(), &command);
2706     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
2707 
2708     Segments segments;
2709     {  // Initialize segments.
2710       Segment *segment = segments.add_segment();
2711       segment->set_key("あbc");
2712       segment->add_candidate()->value = "あべし";
2713     }
2714 
2715     command.Clear();
2716     SetSendCommandCommand(commands::SessionCommand::COMMIT_RAW_TEXT, &command);
2717     session->SendCommand(&command);
2718     EXPECT_RESULT_AND_KEY("abc", "abc", command);
2719     EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
2720   }
2721   {  // From conversion mode.
2722     std::unique_ptr<Session> session(new Session(engine_.get()));
2723     InitSessionToPrecomposition(session.get());
2724     commands::Command command;
2725     InsertCharacterChars("abc", session.get(), &command);
2726     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
2727 
2728     Segments segments;
2729     {  // Initialize segments.
2730       Segment *segment = segments.add_segment();
2731       segment->set_key("あbc");
2732       segment->add_candidate()->value = "あべし";
2733     }
2734 
2735     ConversionRequest request;
2736     SetComposer(session.get(), &request);
2737     FillT13Ns(request, &segments);
2738     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2739     command.Clear();
2740     session->Convert(&command);
2741     EXPECT_PREEDIT("あべし", command);
2742     EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
2743 
2744     command.Clear();
2745     SetSendCommandCommand(commands::SessionCommand::COMMIT_RAW_TEXT, &command);
2746     session->SendCommand(&command);
2747     EXPECT_RESULT_AND_KEY("abc", "abc", command);
2748     EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
2749   }
2750 }
2751 
TEST_F(SessionTest,CommitRawText_KanaInput)2752 TEST_F(SessionTest, CommitRawText_KanaInput) {
2753   Segments segments;
2754   Segment *segment;
2755   Segment::Candidate *candidate;
2756   composer::Table table;
2757   table.AddRule("す゛", "ず", "");
2758 
2759   std::unique_ptr<Session> session(new Session(engine_.get()));
2760   session->get_internal_composer_only_for_unittest()->SetTable(&table);
2761   InitSessionToPrecomposition(session.get());
2762 
2763   commands::Command command;
2764   SetSendKeyCommand("m", &command);
2765   command.mutable_input()->mutable_key()->set_key_string("も");
2766   session->SendKey(&command);
2767 
2768   SetSendKeyCommand("r", &command);
2769   command.mutable_input()->mutable_key()->set_key_string("す");
2770   session->SendKey(&command);
2771 
2772   SetSendKeyCommand("@", &command);
2773   command.mutable_input()->mutable_key()->set_key_string("゛");
2774   session->SendKey(&command);
2775 
2776   SetSendKeyCommand("h", &command);
2777   command.mutable_input()->mutable_key()->set_key_string("く");
2778   session->SendKey(&command);
2779 
2780   SetSendKeyCommand("!", &command);
2781   command.mutable_input()->mutable_key()->set_key_string("!");
2782   session->SendKey(&command);
2783 
2784   EXPECT_EQ("もずく!", command.output().preedit().segment(0).value());
2785 
2786   segment = segments.add_segment();
2787   segment->set_key("もずく!");
2788   candidate = segment->add_candidate();
2789   candidate->value = "もずく!";
2790 
2791   ConversionRequest request;
2792   SetComposer(session.get(), &request);
2793   FillT13Ns(request, &segments);
2794   GetConverterMock()->SetStartConversionForRequest(&segments, true);
2795 
2796   command.Clear();
2797   SetSendCommandCommand(commands::SessionCommand::COMMIT_RAW_TEXT, &command);
2798   session->SendCommand(&command);
2799   EXPECT_RESULT_AND_KEY("mr@h!", "mr@h!", command);
2800   EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
2801 }
2802 
TEST_F(SessionTest,ConvertNextPage_PrevPage)2803 TEST_F(SessionTest, ConvertNextPage_PrevPage) {
2804   commands::Command command;
2805   std::unique_ptr<Session> session(new Session(engine_.get()));
2806 
2807   InitSessionToPrecomposition(session.get());
2808 
2809   // Should be ignored in precomposition state.
2810   {
2811     command.Clear();
2812     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2813     command.mutable_input()->mutable_command()->set_type(
2814         commands::SessionCommand::CONVERT_NEXT_PAGE);
2815     ASSERT_TRUE(session->SendCommand(&command));
2816     EXPECT_TRUE(command.output().consumed());
2817 
2818     command.Clear();
2819     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2820     command.mutable_input()->mutable_command()->set_type(
2821         commands::SessionCommand::CONVERT_PREV_PAGE);
2822     ASSERT_TRUE(session->SendCommand(&command));
2823     EXPECT_TRUE(command.output().consumed());
2824   }
2825 
2826   InsertCharacterChars("aiueo", session.get(), &command);
2827   EXPECT_PREEDIT("あいうえお", command);
2828 
2829   // Should be ignored in composition state.
2830   {
2831     command.Clear();
2832     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2833     command.mutable_input()->mutable_command()->set_type(
2834         commands::SessionCommand::CONVERT_NEXT_PAGE);
2835     ASSERT_TRUE(session->SendCommand(&command));
2836     EXPECT_TRUE(command.output().consumed());
2837     EXPECT_PREEDIT("あいうえお", command) << "should do nothing";
2838 
2839     command.Clear();
2840     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2841     command.mutable_input()->mutable_command()->set_type(
2842         commands::SessionCommand::CONVERT_PREV_PAGE);
2843     ASSERT_TRUE(session->SendCommand(&command));
2844     EXPECT_TRUE(command.output().consumed());
2845     EXPECT_PREEDIT("あいうえお", command) << "should do nothing";
2846   }
2847 
2848   // Generate sequential candidates as follows.
2849   //   "page0-cand0"
2850   //   "page0-cand1"
2851   //   ...
2852   //   "page0-cand8"
2853   //   "page1-cand0"
2854   //   ...
2855   //   "page1-cand8"
2856   //   "page2-cand0"
2857   //   ...
2858   //   "page2-cand8"
2859   {
2860     Segments segments;
2861     Segment *segment = NULL;
2862     segment = segments.add_segment();
2863     segment->set_key("あいうえお");
2864     for (int page_index = 0; page_index < 3; ++page_index) {
2865       for (int cand_index = 0; cand_index < 9; ++cand_index) {
2866         segment->add_candidate()->value = Util::StringPrintf(
2867             "page%d-cand%d", page_index, cand_index);
2868       }
2869     }
2870     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2871   }
2872 
2873   // Make sure the selected candidate changes as follows.
2874   //                              -> Convert
2875   //  -> "page0-cand0" -> SendCommand/CONVERT_NEXT_PAGE
2876   //  -> "page1-cand0" -> SendCommand/CONVERT_PREV_PAGE
2877   //  -> "page0-cand0" -> SendCommand/CONVERT_PREV_PAGE
2878   //  -> "page2-cand0"
2879 
2880   command.Clear();
2881   ASSERT_TRUE(session->Convert(&command));
2882   EXPECT_PREEDIT("page0-cand0", command);
2883 
2884   command.Clear();
2885   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2886   command.mutable_input()->mutable_command()->set_type(
2887       commands::SessionCommand::CONVERT_NEXT_PAGE);
2888   ASSERT_TRUE(session->SendCommand(&command));
2889   EXPECT_PREEDIT("page1-cand0", command);
2890 
2891   command.Clear();
2892   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2893   command.mutable_input()->mutable_command()->set_type(
2894       commands::SessionCommand::CONVERT_PREV_PAGE);
2895   ASSERT_TRUE(session->SendCommand(&command));
2896   EXPECT_PREEDIT("page0-cand0", command);
2897 
2898   command.Clear();
2899   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
2900   command.mutable_input()->mutable_command()->set_type(
2901       commands::SessionCommand::CONVERT_PREV_PAGE);
2902   ASSERT_TRUE(session->SendCommand(&command));
2903   EXPECT_PREEDIT("page2-cand0", command);
2904 }
2905 
TEST_F(SessionTest,NeedlessClearUndoContext)2906 TEST_F(SessionTest, NeedlessClearUndoContext) {
2907   // This is a unittest against http://b/3423910.
2908 
2909   std::unique_ptr<Session> session(new Session(engine_.get()));
2910   InitSessionToPrecomposition(session.get());
2911 
2912   // Undo requires capability DELETE_PRECEDING_TEXT.
2913   commands::Capability capability;
2914   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2915   session->set_client_capability(capability);
2916   commands::Command command;
2917 
2918   {  // Conversion -> Send Shift -> Undo
2919     Segments segments;
2920     InsertCharacterChars("aiueo", session.get(), &command);
2921     ConversionRequest request;
2922     SetComposer(session.get(), &request);
2923     SetAiueo(&segments);
2924     FillT13Ns(request, &segments);
2925 
2926     GetConverterMock()->SetStartConversionForRequest(&segments, true);
2927     command.Clear();
2928     session->Convert(&command);
2929     EXPECT_FALSE(command.output().has_result());
2930     EXPECT_PREEDIT("あいうえお", command);
2931 
2932     GetConverterMock()->SetCommitSegmentValue(&segments, true);
2933     command.Clear();
2934     session->Commit(&command);
2935     EXPECT_FALSE(command.output().has_preedit());
2936     EXPECT_RESULT("あいうえお", command);
2937 
2938     SendKey("Shift", session.get(), &command);
2939     EXPECT_FALSE(command.output().has_result());
2940     EXPECT_FALSE(command.output().has_preedit());
2941 
2942     command.Clear();
2943     session->Undo(&command);
2944     EXPECT_FALSE(command.output().has_result());
2945     EXPECT_TRUE(command.output().has_deletion_range());
2946     EXPECT_EQ(-5, command.output().deletion_range().offset());
2947     EXPECT_EQ(5, command.output().deletion_range().length());
2948     EXPECT_PREEDIT("あいうえお", command);
2949   }
2950 
2951   {  // Type "aiueo" -> Convert -> Type "a" -> Escape -> Undo
2952     Segments segments;
2953     InsertCharacterChars("aiueo", session.get(), &command);
2954     ConversionRequest request;
2955     SetComposer(session.get(), &request);
2956     SetAiueo(&segments);
2957     FillT13Ns(request, &segments);
2958 
2959     command.Clear();
2960     session->Convert(&command);
2961     EXPECT_FALSE(command.output().has_result());
2962     EXPECT_PREEDIT("あいうえお", command);
2963 
2964     SendKey("a", session.get(), &command);
2965     EXPECT_RESULT("あいうえお", command);
2966     EXPECT_SINGLE_SEGMENT("あ", command);
2967 
2968     SendKey("Escape", session.get(), &command);
2969     EXPECT_FALSE(command.output().has_result());
2970     EXPECT_FALSE(command.output().has_preedit());
2971 
2972     command.Clear();
2973     session->Undo(&command);
2974     EXPECT_FALSE(command.output().has_result());
2975     EXPECT_TRUE(command.output().has_deletion_range());
2976     EXPECT_EQ(-5, command.output().deletion_range().offset());
2977     EXPECT_EQ(5, command.output().deletion_range().length());
2978     EXPECT_PREEDIT("あいうえお", command);
2979   }
2980 }
2981 
TEST_F(SessionTest,ClearUndoContextAfterDirectInputAfterConversion)2982 TEST_F(SessionTest, ClearUndoContextAfterDirectInputAfterConversion) {
2983   std::unique_ptr<Session> session(new Session(engine_.get()));
2984   InitSessionToPrecomposition(session.get());
2985 
2986   // Prepare Numpad
2987   config::Config config;
2988   config.set_numpad_character_form(config::Config::NUMPAD_DIRECT_INPUT);
2989   // Update KeyEventTransformer
2990   session->SetConfig(&config);
2991 
2992   // Undo requires capability DELETE_PRECEDING_TEXT.
2993   commands::Capability capability;
2994   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
2995   session->set_client_capability(capability);
2996   commands::Command command;
2997 
2998   // Cleate segments
2999   Segments segments;
3000   InsertCharacterChars("aiueo", session.get(), &command);
3001   ConversionRequest request;
3002   SetComposer(session.get(), &request);
3003   SetAiueo(&segments);
3004   FillT13Ns(request, &segments);
3005 
3006   // Convert
3007   GetConverterMock()->SetStartConversionForRequest(&segments, true);
3008   command.Clear();
3009   session->Convert(&command);
3010   EXPECT_FALSE(command.output().has_result());
3011   EXPECT_PREEDIT("あいうえお", command);
3012   // Direct input
3013   SendKey("Numpad0", session.get(), &command);
3014   EXPECT_TRUE(GetComposition(command).empty());
3015   EXPECT_RESULT("あいうえお0", command);
3016 
3017   // Undo - Do NOT nothing
3018   command.Clear();
3019   session->Undo(&command);
3020   EXPECT_FALSE(command.output().has_result());
3021   EXPECT_FALSE(command.output().has_deletion_range());
3022   EXPECT_FALSE(command.output().has_preedit());
3023 }
3024 
TEST_F(SessionTest,TemporaryInputModeAfterUndo)3025 TEST_F(SessionTest, TemporaryInputModeAfterUndo) {
3026   // This is a unittest against http://b/3423599.
3027   std::unique_ptr<Session> session(new Session(engine_.get()));
3028   InitSessionToPrecomposition(session.get());
3029 
3030   // Undo requires capability DELETE_PRECEDING_TEXT.
3031   commands::Capability capability;
3032   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
3033   session->set_client_capability(capability);
3034   commands::Command command;
3035 
3036   // Shift + Ascii triggers temporary input mode switch.
3037   SendKey("A", session.get(), &command);
3038   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3039   SendKey("Enter", session.get(), &command);
3040   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3041 
3042   // Undo and keep temporary input mode correct
3043   command.Clear();
3044   session->Undo(&command);
3045   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3046   EXPECT_FALSE(command.output().has_result());
3047   EXPECT_PREEDIT("A", command);
3048   SendKey("Enter", session.get(), &command);
3049   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3050 
3051   // Undo and input additional "A" with temporary input mode.
3052   command.Clear();
3053   session->Undo(&command);
3054   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3055   SendKey("A", session.get(), &command);
3056   EXPECT_FALSE(command.output().has_result());
3057   EXPECT_PREEDIT("AA", command);
3058   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3059 
3060   // Input additional "a" with original input mode.
3061   SendKey("a", session.get(), &command);
3062   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3063   EXPECT_FALSE(command.output().has_result());
3064   EXPECT_PREEDIT("AAあ", command);
3065 
3066   // Submit and Undo
3067   SendKey("Enter", session.get(), &command);
3068   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3069   command.Clear();
3070   session->Undo(&command);
3071   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3072   EXPECT_FALSE(command.output().has_result());
3073   EXPECT_PREEDIT("AAあ", command);
3074 
3075   // Input additional "Aa"
3076   SendKey("A", session.get(), &command);
3077   SendKey("a", session.get(), &command);
3078   EXPECT_FALSE(command.output().has_result());
3079   EXPECT_PREEDIT("AAあAa", command);
3080   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3081 
3082   // Submit and Undo
3083   SendKey("Enter", session.get(), &command);
3084   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3085   command.Clear();
3086   session->Undo(&command);
3087   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
3088   EXPECT_FALSE(command.output().has_result());
3089   EXPECT_PREEDIT("AAあAa", command);
3090 }
3091 
TEST_F(SessionTest,DCHECKFailureAfterUndo)3092 TEST_F(SessionTest, DCHECKFailureAfterUndo) {
3093   // This is a unittest against http://b/3437358.
3094   std::unique_ptr<Session> session(new Session(engine_.get()));
3095   InitSessionToPrecomposition(session.get());
3096 
3097   commands::Capability capability;
3098   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
3099   session->set_client_capability(capability);
3100   commands::Command command;
3101 
3102   InsertCharacterChars("abe", session.get(), &command);
3103   command.Clear();
3104   session->Commit(&command);
3105   command.Clear();
3106   session->Undo(&command);
3107   EXPECT_FALSE(command.output().has_result());
3108   EXPECT_PREEDIT("あべ", command);
3109 
3110   InsertCharacterChars("s", session.get(), &command);
3111   EXPECT_FALSE(command.output().has_result());
3112   EXPECT_PREEDIT("あべs", command);
3113 
3114   InsertCharacterChars("h", session.get(), &command);
3115   EXPECT_FALSE(command.output().has_result());
3116   EXPECT_PREEDIT("あべsh", command);
3117 
3118   InsertCharacterChars("i", session.get(), &command);
3119   EXPECT_FALSE(command.output().has_result());
3120   EXPECT_PREEDIT("あべし", command);
3121 }
3122 
TEST_F(SessionTest,ConvertToFullOrHalfAlphanumericAfterUndo)3123 TEST_F(SessionTest, ConvertToFullOrHalfAlphanumericAfterUndo) {
3124   // This is a unittest against http://b/3423592.
3125   std::unique_ptr<Session> session(new Session(engine_.get()));
3126   InitSessionToPrecomposition(session.get());
3127 
3128   // Undo requires capability DELETE_PRECEDING_TEXT.
3129   commands::Capability capability;
3130   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
3131   session->set_client_capability(capability);
3132 
3133   Segments segments;
3134   SetAiueo(&segments);
3135   ConversionRequest request;
3136   SetComposer(session.get(), &request);
3137   FillT13Ns(request, &segments);
3138 
3139   {  // ConvertToHalfASCII
3140     commands::Command command;
3141     InsertCharacterChars("aiueo", session.get(), &command);
3142 
3143     SendKey("Enter", session.get(), &command);
3144     command.Clear();
3145     session->Undo(&command);
3146     EXPECT_FALSE(command.output().has_result());
3147     ASSERT_TRUE(command.output().has_preedit());
3148     EXPECT_EQ("あいうえお", GetComposition(command));
3149 
3150     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3151     command.Clear();
3152     session->ConvertToHalfASCII(&command);
3153     EXPECT_FALSE(command.output().has_result());
3154     ASSERT_TRUE(command.output().has_preedit());
3155     EXPECT_EQ("aiueo", GetComposition(command));
3156   }
3157 
3158   {  // ConvertToFullASCII
3159     commands::Command command;
3160     InsertCharacterChars("aiueo", session.get(), &command);
3161 
3162     SendKey("Enter", session.get(), &command);
3163     command.Clear();
3164     session->Undo(&command);
3165     EXPECT_FALSE(command.output().has_result());
3166     ASSERT_TRUE(command.output().has_preedit());
3167     EXPECT_EQ("あいうえお", GetComposition(command));
3168 
3169     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3170     command.Clear();
3171     session->ConvertToFullASCII(&command);
3172     EXPECT_FALSE(command.output().has_result());
3173     ASSERT_TRUE(command.output().has_preedit());
3174     EXPECT_EQ("aiueo", GetComposition(command));
3175   }
3176 }
3177 
TEST_F(SessionTest,ComposeVoicedSoundMarkAfterUndo_Issue5369632)3178 TEST_F(SessionTest, ComposeVoicedSoundMarkAfterUndo_Issue5369632) {
3179   // This is a unittest against http://b/5369632.
3180   config::Config config;
3181   config.set_preedit_method(config::Config::KANA);
3182 
3183   std::unique_ptr<Session> session(new Session(engine_.get()));
3184   session->SetConfig(&config);
3185   InitSessionToPrecomposition(session.get());
3186 
3187   // Undo requires capability DELETE_PRECEDING_TEXT.
3188   commands::Capability capability;
3189   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
3190   session->set_client_capability(capability);
3191 
3192   commands::Command command;
3193 
3194   InsertCharacterCodeAndString('a', "ち", session.get(), &command);
3195   EXPECT_EQ("ち", GetComposition(command));
3196 
3197   SendKey("Enter", session.get(), &command);
3198   command.Clear();
3199   session->Undo(&command);
3200 
3201   EXPECT_FALSE(command.output().has_result());
3202   ASSERT_TRUE(command.output().has_preedit());
3203   EXPECT_EQ("ち", GetComposition(command));
3204 
3205   InsertCharacterCodeAndString('@', "゛", session.get(), &command);
3206   EXPECT_FALSE(command.output().has_result());
3207   ASSERT_TRUE(command.output().has_preedit());
3208   EXPECT_EQ("ぢ", GetComposition(command));
3209 }
3210 
TEST_F(SessionTest,SpaceOnAlphanumeric)3211 TEST_F(SessionTest, SpaceOnAlphanumeric) {
3212   commands::Request request;
3213   commands::Command command;
3214 
3215   {
3216     request.set_space_on_alphanumeric(commands::Request::COMMIT);
3217 
3218     Session session(engine_.get());
3219     InitSessionToPrecomposition(&session, request);
3220 
3221     SendKey("A", &session, &command);
3222     EXPECT_EQ("A", GetComposition(command));
3223 
3224     SendKey("Space", &session, &command);
3225     EXPECT_RESULT("A ", command);
3226   }
3227 
3228   {
3229     request.set_space_on_alphanumeric(
3230         commands::Request::SPACE_OR_CONVERT_COMMITING_COMPOSITION);
3231 
3232     Session session(engine_.get());
3233     InitSessionToPrecomposition(&session, request);
3234 
3235     SendKey("A", &session, &command);
3236     EXPECT_EQ("A", GetComposition(command));
3237 
3238     SendKey("Space", &session, &command);
3239     EXPECT_FALSE(command.output().has_result());
3240     EXPECT_EQ("A ", GetComposition(command));
3241 
3242     SendKey("a", &session, &command);
3243     EXPECT_RESULT("A ", command);
3244     EXPECT_EQ("あ", GetComposition(command));
3245   }
3246 
3247   {
3248     request.set_space_on_alphanumeric(
3249         commands::Request::SPACE_OR_CONVERT_KEEPING_COMPOSITION);
3250 
3251     Session session(engine_.get());
3252     InitSessionToPrecomposition(&session, request);
3253 
3254     SendKey("A", &session, &command);
3255     EXPECT_EQ("A", GetComposition(command));
3256 
3257     SendKey("Space", &session, &command);
3258     EXPECT_FALSE(command.output().has_result());
3259     EXPECT_EQ("A ", GetComposition(command));
3260 
3261     SendKey("a", &session, &command);
3262     EXPECT_FALSE(command.output().has_result());
3263     EXPECT_EQ("A a", GetComposition(command));
3264   }
3265 }
3266 
TEST_F(SessionTest,Issue1805239)3267 TEST_F(SessionTest, Issue1805239) {
3268   // This is a unittest against http://b/1805239.
3269   Segments segments;
3270   Segment *segment;
3271   Segment::Candidate *candidate;
3272   std::unique_ptr<Session> session(new Session(engine_.get()));
3273   InitSessionToPrecomposition(session.get());
3274 
3275   commands::Command command;
3276   InsertCharacterChars("watasinonamae", session.get(), &command);
3277 
3278   segment = segments.add_segment();
3279   segment->set_key("わたしの");
3280   candidate = segment->add_candidate();
3281   candidate->value = "私の";
3282   candidate = segment->add_candidate();
3283   candidate->value = "渡しの";
3284   segment = segments.add_segment();
3285   segment->set_key("名前");
3286   candidate = segment->add_candidate();
3287   candidate->value = "なまえ";
3288   candidate = segment->add_candidate();
3289   candidate->value = "ナマエ";
3290 
3291   ConversionRequest request;
3292   SetComposer(session.get(), &request);
3293   FillT13Ns(request, &segments);
3294   GetConverterMock()->SetStartConversionForRequest(&segments, true);
3295 
3296   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3297   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
3298   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3299   EXPECT_TRUE(command.output().has_candidates());
3300 
3301   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
3302   EXPECT_FALSE(command.output().has_candidates());
3303 
3304   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
3305   EXPECT_FALSE(command.output().has_candidates());
3306 
3307   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3308   EXPECT_TRUE(command.output().has_candidates());
3309 
3310   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3311   EXPECT_TRUE(command.output().has_candidates());
3312 
3313   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3314   EXPECT_TRUE(command.output().has_candidates());
3315 
3316   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3317   EXPECT_TRUE(command.output().has_candidates());
3318 }
3319 
TEST_F(SessionTest,Issue1816861)3320 TEST_F(SessionTest, Issue1816861) {
3321   // This is a unittest against http://b/1816861
3322   Segments segments;
3323   Segment *segment;
3324   Segment::Candidate *candidate;
3325   std::unique_ptr<Session> session(new Session(engine_.get()));
3326   InitSessionToPrecomposition(session.get());
3327 
3328   commands::Command command;
3329   InsertCharacterChars("kamabokonoinbou", session.get(), &command);
3330   segment = segments.add_segment();
3331   segment->set_key("かまぼこの");
3332   candidate = segment->add_candidate();
3333   candidate->value = "かまぼこの";
3334   candidate = segment->add_candidate();
3335   candidate->value = "カマボコの";
3336   segment = segments.add_segment();
3337   segment->set_key("いんぼう");
3338   candidate = segment->add_candidate();
3339   candidate->value = "陰謀";
3340   candidate = segment->add_candidate();
3341   candidate->value = "印房";
3342 
3343   ConversionRequest request;
3344   SetComposer(session.get(), &request);
3345   FillT13Ns(request, &segments);
3346   GetConverterMock()->SetStartConversionForRequest(&segments, true);
3347 
3348   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3349   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
3350   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3351   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3352   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
3353   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
3354   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
3355   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
3356   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3357   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3358   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3359   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3360   SendSpecialKey(commands::KeyEvent::BACKSPACE, session.get(), &command);
3361 
3362   segments.Clear();
3363   segment = segments.add_segment();
3364   segment->set_key("いんぼう");
3365   candidate = segment->add_candidate();
3366   candidate->value = "陰謀";
3367   candidate = segment->add_candidate();
3368   candidate->value = "陰謀論";
3369   candidate = segment->add_candidate();
3370   candidate->value = "陰謀説";
3371 
3372   GetConverterMock()->SetStartPredictionForRequest(&segments, true);
3373 
3374   SendSpecialKey(commands::KeyEvent::TAB, session.get(), &command);
3375 }
3376 
TEST_F(SessionTest,T13NWithResegmentation)3377 TEST_F(SessionTest, T13NWithResegmentation) {
3378   // This is a unittest against http://b/3272827
3379   Segment::Candidate *candidate;
3380   std::unique_ptr<Session> session(new Session(engine_.get()));
3381   InitSessionToPrecomposition(session.get());
3382 
3383   commands::Command command;
3384   InsertCharacterChars("kamabokonoinbou", session.get(), &command);
3385 
3386   {
3387     Segments segments;
3388     Segment *segment;
3389     segment = segments.add_segment();
3390     segment->set_key("かまぼこの");
3391     candidate = segment->add_candidate();
3392     candidate->value = "かまぼこの";
3393     candidate = segment->add_candidate();
3394     candidate->value = "カマボコの";
3395 
3396     segment = segments.add_segment();
3397     segment->set_key("いんぼう");
3398     candidate = segment->add_candidate();
3399     candidate->value = "陰謀";
3400     candidate = segment->add_candidate();
3401     candidate->value = "印房";
3402     ConversionRequest request;
3403     SetComposer(session.get(), &request);
3404     FillT13Ns(request, &segments);
3405     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3406   }
3407   {
3408     Segments segments;
3409     Segment *segment;
3410     segment = segments.add_segment();
3411     segment->set_key("かまぼこの");
3412     candidate = segment->add_candidate();
3413     candidate->value = "かまぼこの";
3414     candidate = segment->add_candidate();
3415     candidate->value = "カマボコの";
3416 
3417     segment = segments.add_segment();
3418     segment->set_key("いんぼ");
3419     candidate = segment->add_candidate();
3420     candidate->value = "いんぼ";
3421     candidate = segment->add_candidate();
3422     candidate->value = "インボ";
3423 
3424     segment = segments.add_segment();
3425     segment->set_key("う");
3426     candidate = segment->add_candidate();
3427     candidate->value = "ウ";
3428     candidate = segment->add_candidate();
3429     candidate->value = "卯";
3430 
3431     ConversionRequest request;
3432     SetComposer(session.get(), &request);
3433     FillT13Ns(request, &segments);
3434     GetConverterMock()->SetResizeSegment1(&segments, true);
3435   }
3436 
3437   // Start conversion
3438   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3439   // Select second segment
3440   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
3441   // Shrink segment
3442   SendKey("Shift left", session.get(), &command);
3443   // Convert to T13N (Half katakana)
3444   SendKey("F8", session.get(), &command);
3445 
3446   EXPECT_EQ("インボ", command.output().preedit().segment(1).value());
3447 }
3448 
TEST_F(SessionTest,Shortcut)3449 TEST_F(SessionTest, Shortcut) {
3450   const config::Config::SelectionShortcut kDataShortcut[] = {
3451     config::Config::NO_SHORTCUT,
3452     config::Config::SHORTCUT_123456789,
3453     config::Config::SHORTCUT_ASDFGHJKL,
3454   };
3455   const string kDataExpected[][2] = {
3456     {"", ""},
3457     {"1", "2"},
3458     {"a", "s"},
3459   };
3460   for (size_t i = 0; i < arraysize(kDataShortcut); ++i) {
3461     config::Config::SelectionShortcut shortcut = kDataShortcut[i];
3462     const string *expected = kDataExpected[i];
3463 
3464     config::Config config;
3465     config.set_selection_shortcut(shortcut);
3466 
3467     std::unique_ptr<Session> session(new Session(engine_.get()));
3468     session->SetConfig(&config);
3469     InitSessionToPrecomposition(session.get());
3470 
3471     Segments segments;
3472     SetAiueo(&segments);
3473     const ImeContext &context = session->context();
3474     ConversionRequest request(&context.composer(),
3475                               &context.GetRequest(),
3476                               &context.GetConfig());
3477     FillT13Ns(request, &segments);
3478     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3479 
3480     commands::Command command;
3481     InsertCharacterChars("aiueo", session.get(), &command);
3482 
3483     command.Clear();
3484     session->Convert(&command);
3485 
3486     command.Clear();
3487     // Convert next
3488     SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3489     ASSERT_TRUE(command.output().has_candidates());
3490     const commands::Candidates &candidates = command.output().candidates();
3491     EXPECT_EQ(expected[0], candidates.candidate(0).annotation().shortcut());
3492     EXPECT_EQ(expected[1], candidates.candidate(1).annotation().shortcut());
3493   }
3494 }
3495 
TEST_F(SessionTest,ShortcutWithCapsLock_Issue5655743)3496 TEST_F(SessionTest, ShortcutWithCapsLock_Issue5655743) {
3497   config::Config config;
3498   config.set_selection_shortcut(config::Config::SHORTCUT_ASDFGHJKL);
3499 
3500   std::unique_ptr<Session> session(new Session(engine_.get()));
3501   session->SetConfig(&config);
3502   InitSessionToPrecomposition(session.get());
3503 
3504   Segments segments;
3505   SetAiueo(&segments);
3506   ConversionRequest request;
3507   SetComposer(session.get(), &request);
3508   FillT13Ns(request, &segments);
3509   GetConverterMock()->SetStartConversionForRequest(&segments, true);
3510 
3511   commands::Command command;
3512   InsertCharacterChars("aiueo", session.get(), &command);
3513 
3514   command.Clear();
3515   session->Convert(&command);
3516 
3517   command.Clear();
3518   // Convert next
3519   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
3520   ASSERT_TRUE(command.output().has_candidates());
3521 
3522   const commands::Candidates &candidates = command.output().candidates();
3523   EXPECT_EQ("a", candidates.candidate(0).annotation().shortcut());
3524   EXPECT_EQ("s", candidates.candidate(1).annotation().shortcut());
3525 
3526   // Select the second candidate by 's' key when the CapsLock is enabled.
3527   // Note that "CAPS S" means that 's' key is pressed w/o shift key.
3528   // See the description in command.proto.
3529   EXPECT_TRUE(SendKey("CAPS S", session.get(), &command));
3530   EXPECT_TRUE(command.output().consumed());
3531   EXPECT_EQ("アイウエオ", GetComposition(command));
3532 }
3533 
TEST_F(SessionTest,NumpadKey)3534 TEST_F(SessionTest, NumpadKey) {
3535   std::unique_ptr<Session> session(new Session(engine_.get()));
3536   InitSessionToPrecomposition(session.get());
3537   commands::Command command;
3538 
3539   config::Config config;
3540   config.set_numpad_character_form(config::Config::NUMPAD_DIRECT_INPUT);
3541   session->SetConfig(&config);
3542 
3543   // In the Precomposition state, numpad keys should not be consumed.
3544   EXPECT_TRUE(TestSendKey("Numpad1", session.get(), &command));
3545   EXPECT_FALSE(command.output().consumed());
3546   EXPECT_TRUE(SendKey("Numpad1", session.get(), &command));
3547   EXPECT_FALSE(command.output().consumed());
3548 
3549   EXPECT_TRUE(TestSendKey("Add", session.get(), &command));
3550   EXPECT_FALSE(command.output().consumed());
3551   EXPECT_TRUE(SendKey("Add", session.get(), &command));
3552   EXPECT_FALSE(command.output().consumed());
3553 
3554   EXPECT_TRUE(TestSendKey("Equals", session.get(), &command));
3555   EXPECT_FALSE(command.output().consumed());
3556   EXPECT_TRUE(SendKey("Equals", session.get(), &command));
3557   EXPECT_FALSE(command.output().consumed());
3558 
3559   EXPECT_TRUE(TestSendKey("Separator", session.get(), &command));
3560   EXPECT_FALSE(command.output().consumed());
3561   EXPECT_TRUE(SendKey("Separator", session.get(), &command));
3562   EXPECT_FALSE(command.output().consumed());
3563 
3564   EXPECT_TRUE(GetComposition(command).empty());
3565 
3566   config.set_numpad_character_form(config::Config::NUMPAD_HALF_WIDTH);
3567   session->SetConfig(&config);
3568 
3569   // In the Precomposition state, numpad keys should not be consumed.
3570   EXPECT_TRUE(TestSendKey("Numpad1", session.get(), &command));
3571   EXPECT_TRUE(command.output().consumed());
3572   EXPECT_TRUE(SendKey("Numpad1", session.get(), &command));
3573   EXPECT_TRUE(command.output().consumed());
3574   EXPECT_EQ("1", GetComposition(command));
3575 
3576   EXPECT_TRUE(TestSendKey("Add", session.get(), &command));
3577   EXPECT_TRUE(command.output().consumed());
3578   EXPECT_TRUE(SendKey("Add", session.get(), &command));
3579   EXPECT_TRUE(command.output().consumed());
3580   EXPECT_EQ("1+", GetComposition(command));
3581 
3582   EXPECT_TRUE(TestSendKey("Equals", session.get(), &command));
3583   EXPECT_TRUE(command.output().consumed());
3584   EXPECT_TRUE(SendKey("Equals", session.get(), &command));
3585   EXPECT_TRUE(command.output().consumed());
3586   EXPECT_EQ("1+=", GetComposition(command));
3587 
3588   EXPECT_TRUE(TestSendKey("Separator", session.get(), &command));
3589   EXPECT_TRUE(command.output().consumed());
3590   EXPECT_TRUE(SendKey("Separator", session.get(), &command));
3591   EXPECT_TRUE(command.output().consumed());
3592 
3593   EXPECT_TRUE(GetComposition(command).empty());
3594 
3595   // "0" should be treated as full-width "0".
3596   EXPECT_TRUE(TestSendKey("0", session.get(), &command));
3597   EXPECT_TRUE(SendKey("0", session.get(), &command));
3598 
3599   EXPECT_SINGLE_SEGMENT_AND_KEY("0", "0", command);
3600 
3601   // In the Composition state, DIVIDE on the pre-edit should be treated as "/".
3602   EXPECT_TRUE(TestSendKey("Divide", session.get(), &command));
3603   EXPECT_TRUE(SendKey("Divide", session.get(), &command));
3604 
3605   EXPECT_SINGLE_SEGMENT_AND_KEY("0/", "0/", command);
3606 
3607   // In the Composition state, "Numpad0" should be treated as half-width "0".
3608   EXPECT_TRUE(SendKey("Numpad0", session.get(), &command));
3609 
3610   EXPECT_SINGLE_SEGMENT_AND_KEY("0/0", "0/0", command);
3611 
3612   // Separator should be treated as Enter.
3613   EXPECT_TRUE(TestSendKey("Separator", session.get(), &command));
3614   EXPECT_TRUE(SendKey("Separator", session.get(), &command));
3615 
3616   EXPECT_FALSE(command.output().has_preedit());
3617   EXPECT_RESULT("0/0", command);
3618 
3619   // http://b/2097087
3620   EXPECT_TRUE(SendKey("0", session.get(), &command));
3621 
3622   EXPECT_SINGLE_SEGMENT_AND_KEY("0", "0", command);
3623 
3624   EXPECT_TRUE(SendKey("Divide", session.get(), &command));
3625   EXPECT_SINGLE_SEGMENT_AND_KEY("0/", "0/", command);
3626 
3627   EXPECT_TRUE(SendKey("Divide", session.get(), &command));
3628   EXPECT_SINGLE_SEGMENT_AND_KEY("0//", "0//", command);
3629 
3630   EXPECT_TRUE(SendKey("Subtract", session.get(), &command));
3631   EXPECT_TRUE(SendKey("Subtract", session.get(), &command));
3632   EXPECT_TRUE(SendKey("Decimal", session.get(), &command));
3633   EXPECT_TRUE(SendKey("Decimal", session.get(), &command));
3634   EXPECT_SINGLE_SEGMENT_AND_KEY("0//--..", "0//--..", command);
3635 }
3636 
TEST_F(SessionTest,KanaSymbols)3637 TEST_F(SessionTest, KanaSymbols) {
3638   config::Config config;
3639   config.set_punctuation_method(config::Config::COMMA_PERIOD);
3640   config.set_symbol_method(config::Config::CORNER_BRACKET_SLASH);
3641 
3642   std::unique_ptr<Session> session(new Session(engine_.get()));
3643   session->SetConfig(&config);
3644   InitSessionToPrecomposition(session.get());
3645 
3646   {
3647     commands::Command command;
3648     SetSendKeyCommand("<", &command);
3649     command.mutable_input()->mutable_key()->set_key_string("、");
3650     EXPECT_TRUE(session->SendKey(&command));
3651     EXPECT_EQ(static_cast<uint32>(','), command.input().key().key_code());
3652     EXPECT_EQ(",", command.input().key().key_string());
3653     EXPECT_EQ(",", command.output().preedit().segment(0).value());
3654   }
3655   {
3656     commands::Command command;
3657     session->EditCancel(&command);
3658   }
3659   {
3660     commands::Command command;
3661     SetSendKeyCommand("?", &command);
3662     command.mutable_input()->mutable_key()->set_key_string("・");
3663     EXPECT_TRUE(session->SendKey(&command));
3664     EXPECT_EQ(static_cast<uint32>('/'), command.input().key().key_code());
3665     EXPECT_EQ("/", command.input().key().key_string());
3666     EXPECT_EQ("/", command.output().preedit().segment(0).value());
3667   }
3668 }
3669 
TEST_F(SessionTest,InsertCharacterWithShiftKey)3670 TEST_F(SessionTest, InsertCharacterWithShiftKey) {
3671   {  // Basic behavior
3672     std::unique_ptr<Session> session(new Session(engine_.get()));
3673     InitSessionToPrecomposition(session.get());
3674     commands::Command command;
3675     EXPECT_TRUE(SendKey("a", session.get(), &command));
3676     EXPECT_TRUE(SendKey("A", session.get(), &command));  // "あA"
3677     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あAa"
3678     // Shift reverts the input mode to Hiragana.
3679     EXPECT_TRUE(SendKey("Shift", session.get(), &command));
3680     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あAaあ"
3681     // Shift does nothing because the input mode has already been reverted.
3682     EXPECT_TRUE(SendKey("Shift", session.get(), &command));
3683     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あAaああ"
3684     EXPECT_EQ("あAaああ", GetComposition(command));
3685   }
3686 
3687   {  // Revert back to the previous input mode.
3688     std::unique_ptr<Session> session(new Session(engine_.get()));
3689     InitSessionToPrecomposition(session.get());
3690     commands::Command command;
3691     session->InputModeFullKatakana(&command);
3692     EXPECT_EQ(commands::FULL_KATAKANA, command.output().mode());
3693     EXPECT_TRUE(SendKey("a", session.get(), &command));
3694     EXPECT_TRUE(SendKey("A", session.get(), &command));  // "アA"
3695     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "アAa"
3696     // Shift reverts the input mode to Hiragana.
3697     EXPECT_TRUE(SendKey("Shift", session.get(), &command));
3698     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "アAaア"
3699     // Shift does nothing because the input mode has already been reverted.
3700     EXPECT_TRUE(SendKey("Shift", session.get(), &command));
3701     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "アAaアア"
3702     EXPECT_EQ("アAaアア", GetComposition(command));
3703   }
3704 }
3705 
TEST_F(SessionTest,ExitTemporaryAlphanumModeAfterCommitingSugesstion)3706 TEST_F(SessionTest, ExitTemporaryAlphanumModeAfterCommitingSugesstion) {
3707   // This is a unittest against http://b/2977131.
3708   {
3709     std::unique_ptr<Session> session(new Session(engine_.get()));
3710     InitSessionToPrecomposition(session.get());
3711     commands::Command command;
3712     EXPECT_TRUE(SendKey("N", session.get(), &command));
3713     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3714     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3715     // Global mode should be kept as HIRAGANA
3716     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3717 
3718     Segments segments;
3719     Segment *segment = segments.add_segment();
3720     segment->set_key("NFL");
3721     segment->add_candidate()->value = "NFL";
3722     ConversionRequest request;
3723     SetComposer(session.get(), &request);
3724     FillT13Ns(request, &segments);
3725     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3726 
3727     EXPECT_TRUE(session->Convert(&command));
3728     EXPECT_FALSE(command.output().has_candidates());
3729     EXPECT_FALSE(command.output().candidates().has_focused_index());
3730     EXPECT_EQ(0, command.output().candidates().focused_index());
3731     EXPECT_FALSE(command.output().has_result());
3732     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3733     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3734     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3735 
3736     EXPECT_TRUE(SendKey("a", session.get(), &command));
3737     EXPECT_FALSE(command.output().has_candidates());
3738     EXPECT_RESULT("NFL", command);
3739     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3740     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3741     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3742   }
3743 
3744   {
3745     std::unique_ptr<Session> session(new Session(engine_.get()));
3746     InitSessionToPrecomposition(session.get());
3747     commands::Command command;
3748     EXPECT_TRUE(SendKey("N", session.get(), &command));
3749     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3750     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3751     // Global mode should be kept as HIRAGANA
3752     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3753 
3754     Segments segments;
3755     Segment *segment = segments.add_segment();
3756     segment->set_key("NFL");
3757     segment->add_candidate()->value = "NFL";
3758     GetConverterMock()->SetStartPredictionForRequest(&segments, true);
3759 
3760     EXPECT_TRUE(session->PredictAndConvert(&command));
3761     ASSERT_TRUE(command.output().has_candidates());
3762     EXPECT_TRUE(command.output().candidates().has_focused_index());
3763     EXPECT_EQ(0, command.output().candidates().focused_index());
3764     EXPECT_FALSE(command.output().has_result());
3765     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3766     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3767     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3768 
3769     EXPECT_TRUE(SendKey("a", session.get(), &command));
3770     EXPECT_FALSE(command.output().has_candidates());
3771     EXPECT_RESULT("NFL", command);
3772 
3773     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3774     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3775     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3776   }
3777 
3778   {
3779     std::unique_ptr<Session> session(new Session(engine_.get()));
3780     InitSessionToPrecomposition(session.get());
3781     commands::Command command;
3782     EXPECT_TRUE(SendKey("N", session.get(), &command));
3783     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3784     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3785     // Global mode should be kept as HIRAGANA
3786     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3787 
3788     Segments segments;
3789     Segment *segment = segments.add_segment();
3790     segment->set_key("NFL");
3791     segment->add_candidate()->value = "NFL";
3792     ConversionRequest request;
3793     SetComposer(session.get(), &request);
3794     FillT13Ns(request, &segments);
3795     GetConverterMock()->SetStartConversionForRequest(&segments, true);
3796 
3797     EXPECT_TRUE(session->ConvertToHalfASCII(&command));
3798     EXPECT_FALSE(command.output().has_candidates());
3799     EXPECT_FALSE(command.output().candidates().has_focused_index());
3800     EXPECT_EQ(0, command.output().candidates().focused_index());
3801     EXPECT_FALSE(command.output().has_result());
3802     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3803     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3804     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3805 
3806     EXPECT_TRUE(SendKey("a", session.get(), &command));
3807     EXPECT_FALSE(command.output().has_candidates());
3808     EXPECT_RESULT("NFL", command);
3809     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3810     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3811     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3812   }
3813 }
3814 
TEST_F(SessionTest,StatusOutput)3815 TEST_F(SessionTest, StatusOutput) {
3816   {  // Basic behavior
3817     std::unique_ptr<Session> session(new Session(engine_.get()));
3818     InitSessionToPrecomposition(session.get());
3819     commands::Command command;
3820     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あ"
3821     ASSERT_TRUE(command.output().has_status());
3822     EXPECT_TRUE(command.output().status().activated());
3823     // command.output().mode() is going to be obsolete.
3824     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
3825     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3826     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3827 
3828     EXPECT_TRUE(SendKey("A", session.get(), &command));  // "あA"
3829     ASSERT_TRUE(command.output().has_status());
3830     EXPECT_TRUE(command.output().status().activated());
3831     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3832     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3833     // Global mode should be kept as HIRAGANA
3834     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3835 
3836     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あAa"
3837     ASSERT_TRUE(command.output().has_status());
3838     EXPECT_TRUE(command.output().status().activated());
3839     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3840     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3841     // Global mode should be kept as HIRAGANA
3842     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3843 
3844     // Shift reverts the input mode to Hiragana.
3845     EXPECT_TRUE(SendKey("Shift", session.get(), &command));
3846     EXPECT_TRUE(SendKey("a", session.get(), &command));  // "あAaあ"
3847     ASSERT_TRUE(command.output().has_status());
3848     EXPECT_TRUE(command.output().status().activated());
3849     EXPECT_EQ(commands::HIRAGANA, command.output().mode());  // obsolete
3850     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3851     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3852 
3853     EXPECT_TRUE(SendKey("A", session.get(), &command));  // "あAaあA"
3854     ASSERT_TRUE(command.output().has_status());
3855     EXPECT_TRUE(command.output().status().activated());
3856     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3857     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3858     // Global mode should be kept as HIRAGANA
3859     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3860 
3861 #ifndef OS_NACL
3862     // NaCl doesn't support OFF key.
3863 
3864     // When the IME is deactivated, the temporary composition mode is reset.
3865     EXPECT_TRUE(SendKey("OFF", session.get(), &command));  // "あAaあA"
3866     ASSERT_TRUE(command.output().has_status());
3867     EXPECT_FALSE(command.output().status().activated());
3868     // command.output().mode() always returns DIRECT when IME is
3869     // deactivated.  This is the reason why command.output().mode() is
3870     // going to be obsolete.
3871     EXPECT_EQ(commands::DIRECT, command.output().mode());
3872     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
3873     EXPECT_EQ(commands::HIRAGANA, command.output().status().comeback_mode());
3874 #endif  // !OS_NACL
3875   }
3876 
3877   {  // Katakana mode + Shift key
3878     std::unique_ptr<Session> session(new Session(engine_.get()));
3879     InitSessionToPrecomposition(session.get());
3880     commands::Command command;
3881     session->InputModeFullKatakana(&command);
3882     EXPECT_EQ(commands::FULL_KATAKANA, command.output().mode());  // obsolete
3883     EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
3884     EXPECT_EQ(commands::FULL_KATAKANA,
3885               command.output().status().comeback_mode());
3886 
3887     EXPECT_TRUE(SendKey("a", session.get(), &command));
3888     ASSERT_TRUE(command.output().has_status());
3889     EXPECT_TRUE(command.output().status().activated());
3890     EXPECT_EQ(commands::FULL_KATAKANA, command.output().mode());  // obsolete
3891     EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
3892     EXPECT_EQ(commands::FULL_KATAKANA,
3893               command.output().status().comeback_mode());
3894 
3895     EXPECT_TRUE(SendKey("A", session.get(), &command));  // "アA"
3896     ASSERT_TRUE(command.output().has_status());
3897     EXPECT_TRUE(command.output().status().activated());
3898     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());  // obsolete
3899     EXPECT_EQ(commands::HALF_ASCII, command.output().status().mode());
3900     // Global mode should be kept as FULL_KATAKANA
3901     EXPECT_EQ(commands::FULL_KATAKANA,
3902               command.output().status().comeback_mode());
3903 
3904 #ifndef OS_NACL
3905     // NaCl doesn't support OFF key.
3906 
3907     // When the IME is deactivated, the temporary composition mode is reset.
3908     EXPECT_TRUE(SendKey("OFF", session.get(), &command));  // "アA"
3909     ASSERT_TRUE(command.output().has_status());
3910     EXPECT_FALSE(command.output().status().activated());
3911     // command.output().mode() always returns DIRECT when IME is
3912     // deactivated.  This is the reason why command.output().mode() is
3913     // going to be obsolete.
3914     EXPECT_EQ(commands::DIRECT, command.output().mode());
3915     EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
3916     EXPECT_EQ(commands::FULL_KATAKANA,
3917               command.output().status().comeback_mode());
3918 #endif  // !OS_NACL
3919   }
3920 }
3921 
TEST_F(SessionTest,Suggest)3922 TEST_F(SessionTest, Suggest) {
3923   Segments segments_m;
3924   {
3925     segments_m.set_request_type(Segments::SUGGESTION);
3926     Segment *segment;
3927     segment = segments_m.add_segment();
3928     segment->set_key("M");
3929     segment->add_candidate()->value = "MOCHA";
3930     segment->add_candidate()->value = "MOZUKU";
3931   }
3932 
3933   Segments segments_mo;
3934   {
3935     segments_mo.set_request_type(Segments::SUGGESTION);
3936     Segment *segment;
3937     segment = segments_mo.add_segment();
3938     segment->set_key("MO");
3939     segment->add_candidate()->value = "MOCHA";
3940     segment->add_candidate()->value = "MOZUKU";
3941   }
3942 
3943   Segments segments_moz;
3944   {
3945     segments_moz.set_request_type(Segments::SUGGESTION);
3946     Segment *segment;
3947     segment = segments_moz.add_segment();
3948     segment->set_key("MOZ");
3949     segment->add_candidate()->value = "MOZUKU";
3950   }
3951 
3952   std::unique_ptr<Session> session(new Session(engine_.get()));
3953   InitSessionToPrecomposition(session.get());
3954   commands::Command command;
3955   SendKey("M", session.get(), &command);
3956 
3957   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
3958   SendKey("O", session.get(), &command);
3959   ASSERT_TRUE(command.output().has_candidates());
3960   EXPECT_EQ(2, command.output().candidates().candidate_size());
3961   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
3962 
3963   // moz|
3964   GetConverterMock()->SetStartSuggestionForRequest(&segments_moz, true);
3965   SendKey("Z", session.get(), &command);
3966   ASSERT_TRUE(command.output().has_candidates());
3967   EXPECT_EQ(1, command.output().candidates().candidate_size());
3968   EXPECT_EQ("MOZUKU", command.output().candidates().candidate(0).value());
3969 
3970   // mo|
3971   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
3972   SendKey("Backspace", session.get(), &command);
3973   ASSERT_TRUE(command.output().has_candidates());
3974   EXPECT_EQ(2, command.output().candidates().candidate_size());
3975   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
3976 
3977   // m|o
3978   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
3979   command.Clear();
3980   EXPECT_TRUE(session->MoveCursorLeft(&command));
3981   ASSERT_TRUE(command.output().has_candidates());
3982   EXPECT_EQ(2, command.output().candidates().candidate_size());
3983   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
3984 
3985   // mo|
3986   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
3987   command.Clear();
3988   EXPECT_TRUE(session->MoveCursorToEnd(&command));
3989   ASSERT_TRUE(command.output().has_candidates());
3990   EXPECT_EQ(2, command.output().candidates().candidate_size());
3991   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
3992 
3993   // |mo
3994   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
3995   command.Clear();
3996   EXPECT_TRUE(session->MoveCursorToBeginning(&command));
3997   ASSERT_TRUE(command.output().has_candidates());
3998   EXPECT_EQ(2, command.output().candidates().candidate_size());
3999   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
4000 
4001   // m|o
4002   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
4003   command.Clear();
4004   EXPECT_TRUE(session->MoveCursorRight(&command));
4005   ASSERT_TRUE(command.output().has_candidates());
4006   EXPECT_EQ(2, command.output().candidates().candidate_size());
4007   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
4008 
4009   // m|
4010   GetConverterMock()->SetStartSuggestionForRequest(&segments_m, true);
4011   command.Clear();
4012   EXPECT_TRUE(session->Delete(&command));
4013   ASSERT_TRUE(command.output().has_candidates());
4014   EXPECT_EQ(2, command.output().candidates().candidate_size());
4015   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
4016 
4017   Segments segments_m_conv;
4018   {
4019     segments_m_conv.set_request_type(Segments::CONVERSION);
4020     Segment *segment;
4021     segment = segments_m_conv.add_segment();
4022     segment->set_key("M");
4023     segment->add_candidate()->value = "M";
4024     segment->add_candidate()->value = "m";
4025   }
4026   ConversionRequest request_m_conv;
4027   SetComposer(session.get(), &request_m_conv);
4028   FillT13Ns(request_m_conv, &segments_m_conv);
4029   GetConverterMock()->SetStartConversionForRequest(&segments_m_conv, true);
4030   command.Clear();
4031   EXPECT_TRUE(session->Convert(&command));
4032 
4033   GetConverterMock()->SetStartSuggestionForRequest(&segments_m, true);
4034   command.Clear();
4035   EXPECT_TRUE(session->ConvertCancel(&command));
4036   ASSERT_TRUE(command.output().has_candidates());
4037   EXPECT_EQ(2, command.output().candidates().candidate_size());
4038   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
4039 }
4040 
TEST_F(SessionTest,ExpandSuggestion)4041 TEST_F(SessionTest, ExpandSuggestion) {
4042   std::unique_ptr<Session> session(new Session(engine_.get()));
4043   InitSessionToPrecomposition(session.get());
4044   commands::Command command;
4045 
4046   // Prepare suggestion candidates.
4047   Segments segments_m;
4048   {
4049     segments_m.set_request_type(Segments::SUGGESTION);
4050     Segment *segment;
4051     segment = segments_m.add_segment();
4052     segment->set_key("M");
4053     segment->add_candidate()->value = "MOCHA";
4054     segment->add_candidate()->value = "MOZUKU";
4055   }
4056   GetConverterMock()->SetStartSuggestionForRequest(&segments_m, true);
4057 
4058   SendKey("M", session.get(), &command);
4059   ASSERT_TRUE(command.output().has_candidates());
4060   EXPECT_EQ(2, command.output().candidates().candidate_size());
4061 
4062   // Prepare expanded suggestion candidates.
4063   Segments segments_mo;
4064   {
4065     segments_mo.set_request_type(Segments::SUGGESTION);
4066     Segment *segment;
4067     segment = segments_mo.add_segment();
4068     segment->set_key("MO");
4069     segment->add_candidate()->value = "MOZUKU";
4070     segment->add_candidate()->value = "MOZUKUSU";
4071   }
4072   GetConverterMock()->SetStartPredictionForRequest(&segments_mo, true);
4073 
4074   command.Clear();
4075   EXPECT_TRUE(session->ExpandSuggestion(&command));
4076   ASSERT_TRUE(command.output().has_candidates());
4077   // 3 == MOCHA, MOZUKU and MOZUKUSU (duplicate MOZUKU is not counted).
4078   EXPECT_EQ(3, command.output().candidates().candidate_size());
4079   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
4080 }
4081 
TEST_F(SessionTest,ExpandSuggestionDirectMode)4082 TEST_F(SessionTest, ExpandSuggestionDirectMode) {
4083   // On direct mode, ExpandSuggestion() should do nothing.
4084   std::unique_ptr<Session> session(new Session(engine_.get()));
4085   commands::Command command;
4086 
4087   session->IMEOff(&command);
4088   EXPECT_TRUE(session->ExpandSuggestion(&command));
4089   ASSERT_FALSE(command.output().has_candidates());
4090 
4091   // This test expects that ConverterInterface.StartPrediction() is not called
4092   // so SetStartPredictionForRequest() is not called.
4093 }
4094 
TEST_F(SessionTest,ExpandSuggestionConversionMode)4095 TEST_F(SessionTest, ExpandSuggestionConversionMode) {
4096   // On conversion mode, ExpandSuggestion() should do nothing.
4097   std::unique_ptr<Session> session(new Session(engine_.get()));
4098   InitSessionToPrecomposition(session.get());
4099   commands::Command command;
4100 
4101   InsertCharacterChars("aiueo", session.get(), &command);
4102   Segments segments;
4103   ConversionRequest request;
4104   SetComposer(session.get(), &request);
4105   SetAiueo(&segments);
4106   FillT13Ns(request, &segments);
4107   GetConverterMock()->SetStartConversionForRequest(&segments, true);
4108 
4109   command.Clear();
4110   session->Convert(&command);
4111   command.Clear();
4112   session->ConvertNext(&command);
4113 
4114   EXPECT_TRUE(session->ExpandSuggestion(&command));
4115 
4116   // This test expects that ConverterInterface.StartPrediction() is not called
4117   // so SetStartPredictionForRequest() is not called.
4118 }
4119 
TEST_F(SessionTest,CommitCandidate_TypingCorrection)4120 TEST_F(SessionTest, CommitCandidate_TypingCorrection) {
4121   commands::Request request;
4122   request.CopyFrom(*mobile_request_);
4123   request.set_special_romanji_table(Request::QWERTY_MOBILE_TO_HIRAGANA);
4124 
4125   Segments segments_jueri;
4126   segments_jueri.set_request_type(Segments::PARTIAL_SUGGESTION);
4127   Segment *segment;
4128   segment = segments_jueri.add_segment();
4129   const char kJueri[] = "じゅえり";
4130   segment->set_key(kJueri);
4131   Segment::Candidate *candidate = segment->add_candidate();
4132   candidate->key = "くえり";
4133   candidate->content_key = candidate->key;
4134   candidate->value = "クエリ";
4135   candidate->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4136   candidate->consumed_key_size = Util::CharsLen(kJueri);
4137 
4138   std::unique_ptr<Session> session(new Session(engine_.get()));
4139   InitSessionToPrecomposition(session.get(), request);
4140 
4141   commands::Command command;
4142   GetConverterMock()->SetStartSuggestionForRequest(&segments_jueri, true);
4143   InsertCharacterChars("jueri", session.get(), &command);
4144 
4145   ASSERT_TRUE(command.output().has_candidates());
4146   EXPECT_EQ(1, command.output().preedit().segment_size());
4147   EXPECT_EQ(kJueri, command.output().preedit().segment(0).key());
4148   EXPECT_EQ(1, command.output().candidates().candidate_size());
4149   EXPECT_EQ("クエリ", command.output().candidates().candidate(0).value());
4150 
4151   // commit partial suggestion
4152   Segments empty_segments;
4153   GetConverterMock()->SetFinishConversion(&empty_segments, true);
4154   SetSendCommandCommand(commands::SessionCommand::SUBMIT_CANDIDATE, &command);
4155   command.mutable_input()->mutable_command()->set_id(0);
4156   GetConverterMock()->SetStartSuggestionForRequest(&segments_jueri, true);
4157   session->SendCommand(&command);
4158   EXPECT_TRUE(command.output().consumed());
4159   EXPECT_RESULT_AND_KEY("クエリ", "くえり", command);
4160   EXPECT_FALSE(command.output().has_preedit());
4161 }
4162 
TEST_F(SessionTest,MobilePartialSuggestion)4163 TEST_F(SessionTest, MobilePartialSuggestion) {
4164   commands::Request request;
4165   request.CopyFrom(*mobile_request_);
4166   request.set_special_romanji_table(
4167       commands::Request::QWERTY_MOBILE_TO_HIRAGANA);
4168 
4169   Segments segments_wata;
4170   {
4171     segments_wata.set_request_type(Segments::PARTIAL_SUGGESTION);
4172     Segment *segment;
4173     segment = segments_wata.add_segment();
4174     const char kWata[] = "わた";
4175     segment->set_key(kWata);
4176     Segment::Candidate *cand1 = AddCandidate(kWata, "綿", segment);
4177     cand1->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4178     cand1->consumed_key_size = Util::CharsLen(kWata);
4179     Segment::Candidate *cand2 = AddCandidate(kWata, kWata, segment);
4180     cand2->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4181     cand2->consumed_key_size = Util::CharsLen(kWata);
4182   }
4183 
4184   Segments segments_watashino;
4185   {
4186     segments_watashino.set_request_type(Segments::SUGGESTION);
4187     Segment *segment;
4188     segment = segments_watashino.add_segment();
4189     const char kWatashino[] = "わたしの";
4190     segment->set_key(kWatashino);
4191     Segment::Candidate *cand1 = segment->add_candidate();
4192     cand1->value = "私の";
4193     cand1->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4194     cand1->consumed_key_size = Util::CharsLen(kWatashino);
4195     Segment::Candidate *cand2 = segment->add_candidate();
4196     cand2->value = kWatashino;
4197     cand2->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4198     cand2->consumed_key_size = Util::CharsLen(kWatashino);
4199   }
4200 
4201   Segments segments_shino;
4202   {
4203     segments_shino.set_request_type(Segments::SUGGESTION);
4204     Segment *segment;
4205     segment = segments_shino.add_segment();
4206     const char kShino[] = "しの";
4207     segment->set_key(kShino);
4208     Segment::Candidate *candidate;
4209     candidate = AddCandidate("しのみや", "四ノ宮", segment);
4210     candidate->content_key = segment->key();
4211     candidate->attributes = Segment::Candidate::PARTIALLY_KEY_CONSUMED;
4212     candidate->consumed_key_size = Util::CharsLen(kShino);
4213     candidate = AddCandidate(kShino, "shino", segment);
4214   }
4215 
4216   std::unique_ptr<Session> session(new Session(engine_.get()));
4217   InitSessionToPrecomposition(session.get(), request);
4218 
4219   commands::Command command;
4220   GetConverterMock()->SetStartSuggestionForRequest(&segments_watashino, true);
4221   InsertCharacterChars("watashino", session.get(), &command);
4222   ASSERT_TRUE(command.output().has_candidates());
4223   EXPECT_EQ(2, command.output().candidates().candidate_size());
4224   EXPECT_EQ("私の", command.output().candidates().candidate(0).value());
4225 
4226   // partial suggestion for "わた|しの"
4227   GetConverterMock()->SetStartPartialSuggestion(&segments_wata, false);
4228   GetConverterMock()->SetStartPartialSuggestionForRequest(&segments_wata, true);
4229   command.Clear();
4230   EXPECT_TRUE(session->MoveCursorLeft(&command));
4231   command.Clear();
4232   EXPECT_TRUE(session->MoveCursorLeft(&command));
4233   // partial suggestion candidates
4234   ASSERT_TRUE(command.output().has_candidates());
4235   EXPECT_EQ(2, command.output().candidates().candidate_size());
4236   EXPECT_EQ("綿", command.output().candidates().candidate(0).value());
4237 
4238   // commit partial suggestion
4239   SetSendCommandCommand(commands::SessionCommand::SUBMIT_CANDIDATE, &command);
4240   command.mutable_input()->mutable_command()->set_id(0);
4241   GetConverterMock()->SetStartSuggestionForRequest(&segments_shino, true);
4242   session->SendCommand(&command);
4243   EXPECT_TRUE(command.output().consumed());
4244   EXPECT_RESULT_AND_KEY("綿", "わた", command);
4245 
4246   // remaining text in preedit
4247   EXPECT_EQ(2, command.output().preedit().cursor());
4248   EXPECT_SINGLE_SEGMENT("しの", command);
4249 
4250   // Suggestion for new text fills the candidates.
4251   EXPECT_TRUE(command.output().has_candidates());
4252   EXPECT_EQ("四ノ宮", command.output().candidates().candidate(0).value());
4253 }
4254 
TEST_F(SessionTest,ToggleAlphanumericMode)4255 TEST_F(SessionTest, ToggleAlphanumericMode) {
4256   std::unique_ptr<Session> session(new Session(engine_.get()));
4257   InitSessionToPrecomposition(session.get());
4258   commands::Command command;
4259 
4260   {
4261     InsertCharacterChars("a", session.get(), &command);
4262     EXPECT_EQ("あ", GetComposition(command));
4263     EXPECT_TRUE(command.output().has_mode());
4264     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
4265 
4266     command.Clear();
4267     session->ToggleAlphanumericMode(&command);
4268     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4269     InsertCharacterChars("a", session.get(), &command);
4270     EXPECT_EQ("あa", GetComposition(command));
4271     EXPECT_TRUE(command.output().has_mode());
4272     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4273 
4274     command.Clear();
4275     session->ToggleAlphanumericMode(&command);
4276     InsertCharacterChars("a", session.get(), &command);
4277     EXPECT_EQ("あaあ", GetComposition(command));
4278     EXPECT_TRUE(command.output().has_mode());
4279     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
4280   }
4281 
4282   {
4283     // ToggleAlphanumericMode on Precomposition mode should work.
4284     command.Clear();
4285     session->EditCancel(&command);
4286     EXPECT_FALSE(command.output().has_preedit());
4287     EXPECT_TRUE(command.output().has_mode());
4288     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
4289 
4290     session->ToggleAlphanumericMode(&command);
4291     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4292     InsertCharacterChars("a", session.get(), &command);
4293     EXPECT_EQ("a", GetComposition(command));
4294     EXPECT_TRUE(command.output().has_mode());
4295     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4296   }
4297 
4298   {
4299     // A single "n" on Hiragana mode should not converted to "ん" for
4300     // the compatibility with MS-IME.
4301     command.Clear();
4302     session->EditCancel(&command);
4303     EXPECT_FALSE(command.output().has_preedit());
4304     EXPECT_TRUE(command.output().has_mode());
4305     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4306 
4307     session->ToggleAlphanumericMode(&command);
4308     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
4309     InsertCharacterChars("n", session.get(), &command);  // on Hiragana mode
4310     EXPECT_EQ("n", GetComposition(command));
4311 
4312     command.Clear();
4313     session->ToggleAlphanumericMode(&command);
4314     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4315     InsertCharacterChars("a", session.get(), &command);  // on Half ascii mode.
4316     EXPECT_EQ("na", GetComposition(command));
4317   }
4318 
4319   {
4320     // ToggleAlphanumericMode should work even when it is called in
4321     // the conversion state.
4322     command.Clear();
4323     session->EditCancel(&command);
4324     EXPECT_FALSE(command.output().has_preedit());
4325     EXPECT_TRUE(command.output().has_mode());
4326     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4327 
4328     session->InputModeHiragana(&command);
4329     InsertCharacterChars("a", session.get(), &command);  // on Hiragana mode
4330     EXPECT_EQ("あ", GetComposition(command));
4331 
4332     Segments segments;
4333     SetAiueo(&segments);
4334     ConversionRequest request;
4335     SetComposer(session.get(), &request);
4336     FillT13Ns(request, &segments);
4337     GetConverterMock()->SetStartConversionForRequest(&segments, true);
4338 
4339     command.Clear();
4340     session->Convert(&command);
4341 
4342     EXPECT_EQ("あいうえお", GetComposition(command));
4343 
4344     command.Clear();
4345     session->ToggleAlphanumericMode(&command);
4346     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4347 
4348     command.Clear();
4349     session->Commit(&command);
4350 
4351     InsertCharacterChars("a", session.get(), &command);  // on Half ascii mode.
4352     EXPECT_EQ("a", GetComposition(command));
4353   }
4354 }
4355 
TEST_F(SessionTest,InsertSpace)4356 TEST_F(SessionTest, InsertSpace) {
4357   std::unique_ptr<Session> session(new Session(engine_.get()));
4358   InitSessionToPrecomposition(session.get());
4359   commands::Command command;
4360 
4361   commands::KeyEvent space_key;
4362   space_key.set_special_key(commands::KeyEvent::SPACE);
4363 
4364   // Default should be FULL_WIDTH.
4365   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4366   EXPECT_TRUE(session->InsertSpace(&command));
4367   EXPECT_TRUE(command.output().consumed());
4368   EXPECT_FALSE(command.output().has_preedit());
4369   EXPECT_RESULT(" ", command);  // Full-width space
4370 
4371   // Change the setting to HALF_WIDTH.
4372   config::Config config;
4373   config.set_space_character_form(config::Config::FUNDAMENTAL_HALF_WIDTH);
4374   session->SetConfig(&config);
4375   command.Clear();
4376   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4377   EXPECT_TRUE(session->InsertSpace(&command));
4378   EXPECT_FALSE(command.output().consumed());
4379   EXPECT_FALSE(command.output().has_preedit());
4380   EXPECT_FALSE(command.output().has_result());
4381 
4382   // Change the setting to FULL_WIDTH.
4383   config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4384   command.Clear();
4385   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4386   EXPECT_TRUE(session->InsertSpace(&command));
4387   EXPECT_TRUE(command.output().consumed());
4388   EXPECT_FALSE(command.output().has_preedit());
4389   EXPECT_RESULT(" ", command);  // Full-width space
4390 }
4391 
TEST_F(SessionTest,InsertSpaceToggled)4392 TEST_F(SessionTest, InsertSpaceToggled) {
4393   std::unique_ptr<Session> session(new Session(engine_.get()));
4394   InitSessionToPrecomposition(session.get());
4395   commands::Command command;
4396 
4397   commands::KeyEvent space_key;
4398   space_key.set_special_key(commands::KeyEvent::SPACE);
4399 
4400   // Default should be FULL_WIDTH.  So the toggled space should be
4401   // half-width.
4402   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4403   EXPECT_TRUE(session->InsertSpaceToggled(&command));
4404   EXPECT_FALSE(command.output().consumed());
4405   EXPECT_FALSE(command.output().has_preedit());
4406   EXPECT_FALSE(command.output().has_result());
4407 
4408   // Change the setting to HALF_WIDTH.
4409   config::Config config;
4410   config.set_space_character_form(config::Config::FUNDAMENTAL_HALF_WIDTH);
4411   session->SetConfig(&config);
4412   command.Clear();
4413   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4414   EXPECT_TRUE(session->InsertSpaceToggled(&command));
4415   EXPECT_TRUE(command.output().consumed());
4416   EXPECT_FALSE(command.output().has_preedit());
4417   EXPECT_RESULT(" ", command);  // Full-width space
4418 
4419   // Change the setting to FULL_WIDTH.
4420   config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4421   command.Clear();
4422   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4423   EXPECT_TRUE(session->InsertSpaceToggled(&command));
4424   EXPECT_FALSE(command.output().consumed());
4425   EXPECT_FALSE(command.output().has_preedit());
4426   EXPECT_FALSE(command.output().has_result());
4427 }
4428 
TEST_F(SessionTest,InsertSpaceHalfWidth)4429 TEST_F(SessionTest, InsertSpaceHalfWidth) {
4430   std::unique_ptr<Session> session(new Session(engine_.get()));
4431   InitSessionToPrecomposition(session.get());
4432   commands::Command command;
4433 
4434   commands::KeyEvent space_key;
4435   space_key.set_special_key(commands::KeyEvent::SPACE);
4436 
4437   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4438   EXPECT_TRUE(session->InsertSpaceHalfWidth(&command));
4439   EXPECT_FALSE(command.output().consumed());
4440   EXPECT_FALSE(command.output().has_preedit());
4441   EXPECT_FALSE(command.output().has_result());
4442 
4443   EXPECT_TRUE(SendKey("a", session.get(), &command));
4444   EXPECT_EQ("あ", GetComposition(command));
4445 
4446   command.Clear();
4447   EXPECT_TRUE(session->InsertSpaceHalfWidth(&command));
4448   EXPECT_EQ("あ ", GetComposition(command));
4449 
4450   {  // Convert "あ " with dummy conversions.
4451     Segments segments;
4452     segments.add_segment()->add_candidate()->value = "亜 ";
4453     ConversionRequest request;
4454     SetComposer(session.get(), &request);
4455     FillT13Ns(request, &segments);
4456     GetConverterMock()->SetStartConversionForRequest(&segments, true);
4457 
4458     command.Clear();
4459     EXPECT_TRUE(session->Convert(&command));
4460   }
4461 
4462   command.Clear();
4463   EXPECT_TRUE(session->InsertSpaceHalfWidth(&command));
4464   EXPECT_EQ("亜  ", command.output().result().value());
4465   EXPECT_EQ("", GetComposition(command));
4466 }
4467 
TEST_F(SessionTest,InsertSpaceFullWidth)4468 TEST_F(SessionTest, InsertSpaceFullWidth) {
4469   std::unique_ptr<Session> session(new Session(engine_.get()));
4470   InitSessionToPrecomposition(session.get());
4471   commands::Command command;
4472 
4473   commands::KeyEvent space_key;
4474   space_key.set_special_key(commands::KeyEvent::SPACE);
4475 
4476   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4477   EXPECT_TRUE(session->InsertSpaceFullWidth(&command));
4478   EXPECT_TRUE(command.output().consumed());
4479   EXPECT_FALSE(command.output().has_preedit());
4480   EXPECT_RESULT(" ", command);  // Full-width space
4481 
4482   EXPECT_TRUE(SendKey("a", session.get(), &command));
4483   EXPECT_EQ("あ", GetComposition(command));
4484 
4485   command.Clear();
4486   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4487   EXPECT_TRUE(session->InsertSpaceFullWidth(&command));
4488   EXPECT_EQ("あ ",  // full-width space
4489             GetComposition(command));
4490 
4491   {  // Convert "あ " (full-width space) with dummy conversions.
4492     Segments segments;
4493     segments.add_segment()->add_candidate()->value = "亜 ";
4494     ConversionRequest request;
4495     SetComposer(session.get(), &request);
4496     FillT13Ns(request, &segments);
4497     GetConverterMock()->SetStartConversionForRequest(&segments, true);
4498 
4499     command.Clear();
4500     EXPECT_TRUE(session->Convert(&command));
4501   }
4502 
4503   command.Clear();
4504   command.mutable_input()->mutable_key()->CopyFrom(space_key);
4505   EXPECT_TRUE(session->InsertSpaceFullWidth(&command));
4506   EXPECT_EQ("亜  ", command.output().result().value());
4507   EXPECT_EQ("", GetComposition(command));
4508 }
4509 
TEST_F(SessionTest,InsertSpaceWithInputMode)4510 TEST_F(SessionTest, InsertSpaceWithInputMode) {
4511   // First, test against http://b/6027559
4512   config::Config config;
4513   {
4514     const string custom_keymap_table =
4515         "status\tkey\tcommand\n"
4516         "Precomposition\tSpace\tInsertSpace\n"
4517         "Composition\tSpace\tInsertSpace\n";
4518     config.set_session_keymap(config::Config::CUSTOM);
4519     config.set_custom_keymap_table(custom_keymap_table);
4520   }
4521   {
4522     std::unique_ptr<Session> session(new Session(engine_.get()));
4523     session->SetConfig(&config);
4524     InitSessionToPrecomposition(session.get());
4525 
4526     commands::Command command;
4527     EXPECT_TRUE(TestSendKeyWithMode(
4528         "Space", commands::HALF_KATAKANA, session.get(), &command));
4529     EXPECT_FALSE(command.output().consumed());
4530     EXPECT_TRUE(SendKeyWithMode(
4531         "Space", commands::HALF_KATAKANA, session.get(), &command));
4532     // In this case, space key event should not be consumed.
4533     EXPECT_FALSE(command.output().consumed());
4534     EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
4535   }
4536   {
4537     std::unique_ptr<Session> session(new Session(engine_.get()));
4538     session->SetConfig(&config);
4539     InitSessionToPrecomposition(session.get());
4540 
4541     commands::Command command;
4542     EXPECT_TRUE(TestSendKey("a", session.get(), &command));
4543     EXPECT_TRUE(command.output().consumed());
4544     EXPECT_TRUE(SendKey("a", session.get(), &command));
4545     EXPECT_TRUE(command.output().consumed());
4546     EXPECT_PREEDIT("あ", command);
4547     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
4548 
4549     EXPECT_TRUE(TestSendKeyWithMode(
4550         "Space", commands::HALF_KATAKANA, session.get(), &command));
4551     EXPECT_TRUE(command.output().consumed());
4552     EXPECT_TRUE(SendKeyWithMode(
4553         "Space", commands::HALF_KATAKANA, session.get(), &command));
4554     EXPECT_TRUE(command.output().consumed());
4555     EXPECT_PREEDIT("あ ", command);
4556     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
4557   }
4558 
4559   {
4560     const string custom_keymap_table =
4561         "status\tkey\tcommand\n"
4562         "Precomposition\tSpace\tInsertAlternateSpace\n"
4563         "Composition\tSpace\tInsertAlternateSpace\n";
4564     config.set_session_keymap(config::Config::CUSTOM);
4565     config.set_custom_keymap_table(custom_keymap_table);
4566   }
4567   {
4568     std::unique_ptr<Session> session(new Session(engine_.get()));
4569     session->SetConfig(&config);
4570     InitSessionToPrecomposition(session.get());
4571 
4572     commands::Command command;
4573     EXPECT_TRUE(TestSendKeyWithMode(
4574         "Space", commands::HALF_KATAKANA, session.get(), &command));
4575     EXPECT_TRUE(command.output().consumed());
4576     EXPECT_TRUE(SendKeyWithMode(
4577         "Space", commands::HALF_KATAKANA, session.get(), &command));
4578     EXPECT_TRUE(command.output().consumed());
4579     EXPECT_RESULT(" ", command);
4580     EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
4581     EXPECT_EQ(commands::HALF_KATAKANA, command.output().mode());
4582   }
4583   {
4584     std::unique_ptr<Session> session(new Session(engine_.get()));
4585     session->SetConfig(&config);
4586     InitSessionToPrecomposition(session.get());
4587 
4588     commands::Command command;
4589     EXPECT_TRUE(TestSendKey("a", session.get(), &command));
4590     EXPECT_TRUE(command.output().consumed());
4591     EXPECT_TRUE(SendKey("a", session.get(), &command));
4592     EXPECT_TRUE(command.output().consumed());
4593     EXPECT_PREEDIT("あ", command);
4594     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
4595 
4596     EXPECT_TRUE(TestSendKeyWithMode(
4597         "Space", commands::HALF_KATAKANA, session.get(), &command));
4598     EXPECT_TRUE(command.output().consumed());
4599     EXPECT_TRUE(SendKeyWithMode(
4600         "Space", commands::HALF_KATAKANA, session.get(), &command));
4601     EXPECT_TRUE(command.output().consumed());
4602     EXPECT_PREEDIT("あ ", command);  // Full-width space
4603     EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
4604   }
4605 
4606   // Second, the 1st case filed in http://b/2936141
4607   {
4608     const string custom_keymap_table =
4609         "status\tkey\tcommand\n"
4610         "Precomposition\tSpace\tInsertSpace\n"
4611         "Composition\tSpace\tInsertSpace\n";
4612     config.set_session_keymap(config::Config::CUSTOM);
4613     config.set_custom_keymap_table(custom_keymap_table);
4614 
4615     config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4616   }
4617   {
4618     std::unique_ptr<Session> session(new Session(engine_.get()));
4619     session->SetConfig(&config);
4620     InitSessionToPrecomposition(session.get());
4621 
4622     commands::Command command;
4623     EXPECT_TRUE(TestSendKeyWithMode(
4624         "Space", commands::HALF_ASCII, session.get(), &command));
4625     EXPECT_TRUE(command.output().consumed());
4626     command.Clear();
4627     EXPECT_TRUE(SendKeyWithMode(
4628         "Space", commands::HALF_ASCII, session.get(), &command));
4629     EXPECT_TRUE(command.output().consumed());
4630     EXPECT_RESULT(" ", command);
4631     EXPECT_EQ(ImeContext::PRECOMPOSITION, session->context().state());
4632     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4633   }
4634   {
4635     std::unique_ptr<Session> session(new Session(engine_.get()));
4636     session->SetConfig(&config);
4637     InitSessionToPrecomposition(session.get());
4638 
4639     commands::Command command;
4640     EXPECT_TRUE(TestSendKeyWithMode(
4641         "a", commands::HALF_ASCII, session.get(), &command));
4642     EXPECT_TRUE(command.output().consumed());
4643     EXPECT_TRUE(SendKeyWithMode(
4644         "a", commands::HALF_ASCII, session.get(), &command));
4645     EXPECT_TRUE(command.output().consumed());
4646     EXPECT_PREEDIT("a", command);
4647     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4648 
4649     EXPECT_TRUE(TestSendKeyWithMode(
4650         "Space", commands::HALF_ASCII, session.get(), &command));
4651     EXPECT_TRUE(command.output().consumed());
4652     EXPECT_TRUE(SendKeyWithMode(
4653         "Space", commands::HALF_ASCII, session.get(), &command));
4654     EXPECT_TRUE(command.output().consumed());
4655     EXPECT_PREEDIT("a ", command);  // Full-width space
4656     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
4657   }
4658 
4659   // Finally, the 2nd case filed in http://b/2936141
4660   {
4661     const string custom_keymap_table =
4662         "status\tkey\tcommand\n"
4663         "Precomposition\tSpace\tInsertSpace\n"
4664         "Composition\tSpace\tInsertSpace\n";
4665     config.set_session_keymap(config::Config::CUSTOM);
4666     config.set_custom_keymap_table(custom_keymap_table);
4667 
4668     config.set_space_character_form(config::Config::FUNDAMENTAL_HALF_WIDTH);
4669   }
4670   {
4671     std::unique_ptr<Session> session(new Session(engine_.get()));
4672     session->SetConfig(&config);
4673     InitSessionToPrecomposition(session.get());
4674 
4675     commands::Command command;
4676     EXPECT_TRUE(TestSendKeyWithMode(
4677         "Space", commands::FULL_ASCII, session.get(), &command));
4678     EXPECT_FALSE(command.output().consumed());
4679     EXPECT_TRUE(SendKeyWithMode(
4680         "Space", commands::FULL_ASCII, session.get(), &command));
4681     EXPECT_FALSE(command.output().consumed());
4682   }
4683   {
4684     std::unique_ptr<Session> session(new Session(engine_.get()));
4685     session->SetConfig(&config);
4686     InitSessionToPrecomposition(session.get());
4687 
4688     commands::Command command;
4689     EXPECT_TRUE(TestSendKeyWithMode(
4690         "a", commands::FULL_ASCII, session.get(), &command));
4691     EXPECT_TRUE(command.output().consumed());
4692     EXPECT_TRUE(SendKeyWithMode(
4693         "a", commands::FULL_ASCII, session.get(), &command));
4694     EXPECT_TRUE(command.output().consumed());
4695     EXPECT_PREEDIT("a", command);
4696     EXPECT_EQ(commands::FULL_ASCII, command.output().mode());
4697 
4698     EXPECT_TRUE(TestSendKeyWithMode(
4699         "Space", commands::FULL_ASCII, session.get(), &command));
4700     EXPECT_TRUE(command.output().consumed());
4701     EXPECT_TRUE(SendKeyWithMode(
4702         "Space", commands::FULL_ASCII, session.get(), &command));
4703     EXPECT_TRUE(command.output().consumed());
4704     EXPECT_PREEDIT("a ", command);
4705     EXPECT_EQ(commands::FULL_ASCII, command.output().mode());
4706   }
4707 }
4708 
TEST_F(SessionTest,InsertSpaceWithCustomKeyBinding)4709 TEST_F(SessionTest, InsertSpaceWithCustomKeyBinding) {
4710   // This is a unittest against http://b/5872031
4711   config::Config config;
4712   const string custom_keymap_table =
4713       "status\tkey\tcommand\n"
4714       "Precomposition\tSpace\tInsertSpace\n"
4715       "Precomposition\tShift Space\tInsertSpace\n";
4716   config.set_session_keymap(config::Config::CUSTOM);
4717   config.set_custom_keymap_table(custom_keymap_table);
4718   config.set_space_character_form(config::Config::FUNDAMENTAL_HALF_WIDTH);
4719 
4720   std::unique_ptr<Session> session(new Session(engine_.get()));
4721   session->SetConfig(&config);
4722   InitSessionToPrecomposition(session.get());
4723   commands::Command command;
4724 
4725   // A plain space key event dispatched to InsertHalfSpace should be consumed.
4726   SetUndoContext(session.get());
4727   EXPECT_TRUE(TestSendKey("Space", session.get(), &command));
4728   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4729   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4730 
4731   SetUndoContext(session.get());
4732   EXPECT_TRUE(SendKey("Space", session.get(), &command));
4733   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4734   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4735 
4736   // A space key event with any modifier key dispatched to InsertHalfSpace
4737   // should be consumed.
4738   SetUndoContext(session.get());
4739   EXPECT_TRUE(TestSendKey("Shift Space", session.get(), &command));
4740   EXPECT_TRUE(command.output().consumed());
4741   // It is OK not to check |TryUndoAndAssertDoNothing| here because this
4742   // (test) send key event is actually *consumed*.
4743 
4744   EXPECT_TRUE(SendKey("Shift Space", session.get(), &command));
4745   EXPECT_TRUE(command.output().consumed());
4746   EXPECT_FALSE(command.output().has_preedit());
4747   EXPECT_RESULT(" ", command);
4748   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4749 }
4750 
TEST_F(SessionTest,InsertAlternateSpaceWithCustomKeyBinding)4751 TEST_F(SessionTest, InsertAlternateSpaceWithCustomKeyBinding) {
4752   // This is a unittest against http://b/5872031
4753   config::Config config;
4754   const string custom_keymap_table =
4755       "status\tkey\tcommand\n"
4756       "Precomposition\tSpace\tInsertAlternateSpace\n"
4757       "Precomposition\tShift Space\tInsertAlternateSpace\n";
4758   config.set_session_keymap(config::Config::CUSTOM);
4759   config.set_custom_keymap_table(custom_keymap_table);
4760   config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4761 
4762   std::unique_ptr<Session> session(new Session(engine_.get()));
4763   session->SetConfig(&config);
4764   InitSessionToPrecomposition(session.get());
4765   commands::Command command;
4766 
4767   // A plain space key event dispatched to InsertHalfSpace should be consumed.
4768   SetUndoContext(session.get());
4769   EXPECT_TRUE(TestSendKey("Space", session.get(), &command));
4770   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4771   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4772 
4773   SetUndoContext(session.get());
4774   EXPECT_TRUE(SendKey("Space", session.get(), &command));
4775   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4776   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4777 
4778   // A space key event with any modifier key dispatched to InsertHalfSpace
4779   // should be consumed.
4780   SetUndoContext(session.get());
4781   EXPECT_TRUE(TestSendKey("Shift Space", session.get(), &command));
4782   EXPECT_TRUE(command.output().consumed());
4783   // It is OK not to check |TryUndoAndAssertDoNothing| here because this
4784   // (test) send key event is actually *consumed*.
4785 
4786   EXPECT_TRUE(SendKey("Shift Space", session.get(), &command));
4787   EXPECT_TRUE(command.output().consumed());
4788   EXPECT_FALSE(command.output().has_preedit());
4789   EXPECT_RESULT(" ", command);
4790   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4791 }
4792 
TEST_F(SessionTest,InsertSpaceHalfWidthWithCustomKeyBinding)4793 TEST_F(SessionTest, InsertSpaceHalfWidthWithCustomKeyBinding) {
4794   // This is a unittest against http://b/5872031
4795   config::Config config;
4796   const string custom_keymap_table =
4797       "status\tkey\tcommand\n"
4798       "Precomposition\tSpace\tInsertHalfSpace\n"
4799       "Precomposition\tShift Space\tInsertHalfSpace\n";
4800   config.set_session_keymap(config::Config::CUSTOM);
4801   config.set_custom_keymap_table(custom_keymap_table);
4802 
4803   std::unique_ptr<Session> session(new Session(engine_.get()));
4804   session->SetConfig(&config);
4805   InitSessionToPrecomposition(session.get());
4806   commands::Command command;
4807 
4808   // A plain space key event assigned to InsertHalfSpace should be echoed back.
4809   SetUndoContext(session.get());
4810   EXPECT_TRUE(TestSendKey("Space", session.get(), &command));
4811   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4812   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4813 
4814   SetUndoContext(session.get());
4815   EXPECT_TRUE(SendKey("Space", session.get(), &command));
4816   EXPECT_FALSE(command.output().consumed());  // should not be consumed.
4817   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4818 
4819   // A space key event with any modifier key assigned to InsertHalfSpace should
4820   // be consumed.
4821   SetUndoContext(session.get());
4822   EXPECT_TRUE(TestSendKey("Shift Space", session.get(), &command));
4823   EXPECT_TRUE(command.output().consumed());
4824   // It is OK not to check |TryUndoAndAssertDoNothing| here because this
4825   // (test) send key event is actually *consumed*.
4826 
4827   EXPECT_TRUE(SendKey("Shift Space", session.get(), &command));
4828   EXPECT_TRUE(command.output().consumed());
4829   EXPECT_FALSE(command.output().has_preedit());
4830   EXPECT_RESULT(" ", command);
4831   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4832 }
4833 
TEST_F(SessionTest,InsertSpaceFullWidthWithCustomKeyBinding)4834 TEST_F(SessionTest, InsertSpaceFullWidthWithCustomKeyBinding) {
4835   // This is a unittest against http://b/5872031
4836   config::Config config;
4837   const string custom_keymap_table =
4838       "status\tkey\tcommand\n"
4839       "Precomposition\tSpace\tInsertFullSpace\n"
4840       "Precomposition\tShift Space\tInsertFullSpace\n";
4841   config.set_session_keymap(config::Config::CUSTOM);
4842   config.set_custom_keymap_table(custom_keymap_table);
4843 
4844   std::unique_ptr<Session> session(new Session(engine_.get()));
4845   session->SetConfig(&config);
4846   InitSessionToDirect(session.get());
4847 
4848   commands::Command command;
4849 
4850   // A plain space key event assigned to InsertFullSpace should be consumed.
4851   SetUndoContext(session.get());
4852   EXPECT_TRUE(TestSendKey("Space", session.get(), &command));
4853   EXPECT_TRUE(command.output().consumed());
4854   // It is OK not to check |TryUndoAndAssertDoNothing| here because this
4855   // (test) send key event is actually *consumed*.
4856 
4857   EXPECT_TRUE(SendKey("Space", session.get(), &command));
4858   EXPECT_TRUE(command.output().consumed());
4859   EXPECT_FALSE(command.output().has_preedit());
4860   EXPECT_RESULT(" ", command);  // Full-width space
4861   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4862 
4863   // A space key event with any modifier key assigned to InsertFullSpace should
4864   // be consumed.
4865   SetUndoContext(session.get());
4866   EXPECT_TRUE(TestSendKey("Shift Space", session.get(), &command));
4867   EXPECT_TRUE(command.output().consumed());
4868   // It is OK not to check |TryUndoAndAssertDoNothing| here because this
4869   // (test) send key event is actually *consumed*.
4870 
4871   EXPECT_TRUE(SendKey("Shift Space", session.get(), &command));
4872   EXPECT_TRUE(command.output().consumed());
4873   EXPECT_FALSE(command.output().has_preedit());
4874   EXPECT_RESULT(" ", command);  // Full-width space
4875   EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
4876 }
4877 
TEST_F(SessionTest,InsertSpaceInDirectMode)4878 TEST_F(SessionTest, InsertSpaceInDirectMode) {
4879   config::Config config;
4880   const string custom_keymap_table =
4881       "status\tkey\tcommand\n"
4882       "Direct\tCtrl a\tInsertSpace\n"
4883       "Direct\tCtrl b\tInsertAlternateSpace\n"
4884       "Direct\tCtrl c\tInsertHalfSpace\n"
4885       "Direct\tCtrl d\tInsertFullSpace\n";
4886   config.set_session_keymap(config::Config::CUSTOM);
4887   config.set_custom_keymap_table(custom_keymap_table);
4888 
4889   std::unique_ptr<Session> session(new Session(engine_.get()));
4890   session->SetConfig(&config);
4891   InitSessionToDirect(session.get());
4892 
4893   commands::Command command;
4894 
4895   // [InsertSpace] should be echoes back in the direct mode.
4896   EXPECT_TRUE(TestSendKey("Ctrl a", session.get(), &command));
4897   EXPECT_FALSE(command.output().consumed());
4898   EXPECT_FALSE(command.output().has_preedit());
4899   EXPECT_FALSE(command.output().has_result());
4900   EXPECT_TRUE(SendKey("Ctrl a", session.get(), &command));
4901   EXPECT_FALSE(command.output().consumed());
4902   EXPECT_FALSE(command.output().has_preedit());
4903   EXPECT_FALSE(command.output().has_result());
4904 
4905   // [InsertAlternateSpace] should be echoes back in the direct mode.
4906   EXPECT_TRUE(TestSendKey("Ctrl b", session.get(), &command));
4907   EXPECT_FALSE(command.output().consumed());
4908   EXPECT_FALSE(command.output().has_preedit());
4909   EXPECT_FALSE(command.output().has_result());
4910   EXPECT_TRUE(SendKey("Ctrl b", session.get(), &command));
4911   EXPECT_FALSE(command.output().consumed());
4912   EXPECT_FALSE(command.output().has_preedit());
4913   EXPECT_FALSE(command.output().has_result());
4914 
4915   // [InsertHalfSpace] should be echoes back in the direct mode.
4916   EXPECT_TRUE(TestSendKey("Ctrl c", session.get(), &command));
4917   EXPECT_FALSE(command.output().consumed());
4918   EXPECT_FALSE(command.output().has_preedit());
4919   EXPECT_FALSE(command.output().has_result());
4920   EXPECT_TRUE(SendKey("Ctrl c", session.get(), &command));
4921   EXPECT_FALSE(command.output().consumed());
4922   EXPECT_FALSE(command.output().has_preedit());
4923   EXPECT_FALSE(command.output().has_result());
4924 
4925   // [InsertFullSpace] should be echoes back in the direct mode.
4926   EXPECT_TRUE(TestSendKey("Ctrl d", session.get(), &command));
4927   EXPECT_FALSE(command.output().consumed());
4928   EXPECT_FALSE(command.output().has_preedit());
4929   EXPECT_FALSE(command.output().has_result());
4930   EXPECT_TRUE(SendKey("Ctrl d", session.get(), &command));
4931   EXPECT_FALSE(command.output().consumed());
4932   EXPECT_FALSE(command.output().has_preedit());
4933   EXPECT_FALSE(command.output().has_result());
4934 }
4935 
TEST_F(SessionTest,InsertSpaceInCompositionMode)4936 TEST_F(SessionTest, InsertSpaceInCompositionMode) {
4937   // This is a unittest against http://b/5872031
4938   config::Config config;
4939   const string custom_keymap_table =
4940       "status\tkey\tcommand\n"
4941       "Composition\tCtrl a\tInsertSpace\n"
4942       "Composition\tCtrl b\tInsertAlternateSpace\n"
4943       "Composition\tCtrl c\tInsertHalfSpace\n"
4944       "Composition\tCtrl d\tInsertFullSpace\n";
4945   config.set_session_keymap(config::Config::CUSTOM);
4946   config.set_custom_keymap_table(custom_keymap_table);
4947   config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4948 
4949   std::unique_ptr<Session> session(new Session(engine_.get()));
4950   session->SetConfig(&config);
4951   InitSessionToPrecomposition(session.get());
4952   commands::Command command;
4953 
4954   SendKey("a", session.get(), &command);
4955   EXPECT_EQ("あ", GetComposition(command));
4956   EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
4957 
4958   EXPECT_TRUE(TestSendKey("Ctrl a", session.get(), &command));
4959   EXPECT_TRUE(command.output().consumed());
4960 
4961   SendKey("Ctrl a", session.get(), &command);
4962   EXPECT_EQ("あ ", GetComposition(command));
4963 
4964   EXPECT_TRUE(TestSendKey("Ctrl b", session.get(), &command));
4965   EXPECT_TRUE(command.output().consumed());
4966 
4967   SendKey("Ctrl b", session.get(), &command);
4968   EXPECT_EQ("あ  ", GetComposition(command));
4969 
4970   EXPECT_TRUE(TestSendKey("Ctrl c", session.get(), &command));
4971   EXPECT_TRUE(command.output().consumed());
4972 
4973   SendKey("Ctrl c", session.get(), &command);
4974   EXPECT_EQ("あ   ", GetComposition(command));
4975 
4976   EXPECT_TRUE(TestSendKey("Ctrl d", session.get(), &command));
4977   EXPECT_TRUE(command.output().consumed());
4978 
4979   SendKey("Ctrl d", session.get(), &command);
4980   EXPECT_EQ("あ    ", GetComposition(command));
4981 }
4982 
TEST_F(SessionTest,InsertSpaceInConversionMode)4983 TEST_F(SessionTest, InsertSpaceInConversionMode) {
4984   // This is a unittest against http://b/5872031
4985   config::Config config;
4986   const string custom_keymap_table =
4987       "status\tkey\tcommand\n"
4988       "Conversion\tCtrl a\tInsertSpace\n"
4989       "Conversion\tCtrl b\tInsertAlternateSpace\n"
4990       "Conversion\tCtrl c\tInsertHalfSpace\n"
4991       "Conversion\tCtrl d\tInsertFullSpace\n";
4992   config.set_session_keymap(config::Config::CUSTOM);
4993   config.set_custom_keymap_table(custom_keymap_table);
4994   config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
4995 
4996   std::unique_ptr<Session> session(new Session(engine_.get()));
4997   session->SetConfig(&config);
4998 
4999   {
5000     InitSessionToConversionWithAiueo(session.get());
5001     commands::Command command;
5002 
5003     EXPECT_TRUE(TestSendKey("Ctrl a", session.get(), &command));
5004     EXPECT_TRUE(command.output().consumed());
5005 
5006     EXPECT_TRUE(SendKey("Ctrl a", session.get(), &command));
5007     EXPECT_TRUE(GetComposition(command).empty());
5008     ASSERT_TRUE(command.output().has_result());
5009     EXPECT_EQ("あいうえお ", command.output().result().value());
5010     EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
5011   }
5012 
5013   {
5014     InitSessionToConversionWithAiueo(session.get());
5015     commands::Command command;
5016 
5017     EXPECT_TRUE(TestSendKey("Ctrl b", session.get(), &command));
5018     EXPECT_TRUE(command.output().consumed());
5019 
5020     EXPECT_TRUE(SendKey("Ctrl b", session.get(), &command));
5021     EXPECT_TRUE(GetComposition(command).empty());
5022     ASSERT_TRUE(command.output().has_result());
5023     EXPECT_EQ("あいうえお ", command.output().result().value());
5024     EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
5025   }
5026 
5027   {
5028     InitSessionToConversionWithAiueo(session.get());
5029     commands::Command command;
5030 
5031     EXPECT_TRUE(TestSendKey("Ctrl c", session.get(), &command));
5032     EXPECT_TRUE(command.output().consumed());
5033 
5034     EXPECT_TRUE(SendKey("Ctrl c", session.get(), &command));
5035     EXPECT_TRUE(GetComposition(command).empty());
5036     ASSERT_TRUE(command.output().has_result());
5037     EXPECT_EQ("あいうえお ", command.output().result().value());
5038     EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
5039   }
5040 
5041   {
5042     InitSessionToConversionWithAiueo(session.get());
5043     commands::Command command;
5044 
5045     EXPECT_TRUE(TestSendKey("Ctrl d", session.get(), &command));
5046     EXPECT_TRUE(command.output().consumed());
5047 
5048     EXPECT_TRUE(SendKey("Ctrl d", session.get(), &command));
5049     EXPECT_TRUE(GetComposition(command).empty());
5050     ASSERT_TRUE(command.output().has_result());
5051     EXPECT_EQ("あいうえお ", command.output().result().value());
5052     EXPECT_TRUE(TryUndoAndAssertDoNothing(session.get()));
5053   }
5054 }
5055 
TEST_F(SessionTest,InsertSpaceFullWidthOnHalfKanaInput)5056 TEST_F(SessionTest, InsertSpaceFullWidthOnHalfKanaInput) {
5057   std::unique_ptr<Session> session(new Session(engine_.get()));
5058   InitSessionToPrecomposition(session.get());
5059   commands::Command command;
5060 
5061   EXPECT_TRUE(session->InputModeHalfKatakana(&command));
5062   EXPECT_EQ(commands::HALF_KATAKANA, command.output().mode());
5063   InsertCharacterChars("a", session.get(), &command);
5064   EXPECT_EQ("ア", GetComposition(command));
5065 
5066   command.Clear();
5067   commands::KeyEvent space_key;
5068   space_key.set_special_key(commands::KeyEvent::SPACE);
5069   command.mutable_input()->mutable_key()->CopyFrom(space_key);
5070   EXPECT_TRUE(session->InsertSpaceFullWidth(&command));
5071   EXPECT_EQ("ア ", GetComposition(command));  // "ア " (full-width space)
5072 }
5073 
TEST_F(SessionTest,IsFullWidthInsertSpace)5074 TEST_F(SessionTest, IsFullWidthInsertSpace) {
5075   std::unique_ptr<Session> session;
5076   config::Config config;
5077 
5078   { // When |empty_command| does not have |empty_command.key().input()| field,
5079     // the current input mode will be used.
5080 
5081     // Default config -- follow to the current mode.
5082     config.set_space_character_form(config::Config::FUNDAMENTAL_INPUT_MODE);
5083     session.reset(new Session(engine_.get()));
5084     session->SetConfig(&config);
5085     InitSessionToPrecomposition(session.get());
5086 
5087     commands::Input empty_input;
5088 
5089     // Hiragana
5090     commands::Command command;
5091     session->InputModeHiragana(&command);
5092     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5093     // Full-Katakana
5094     command.Clear();
5095     session->InputModeFullKatakana(&command);
5096     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5097     // Half-Katakana
5098     command.Clear();
5099     session->InputModeHalfKatakana(&command);
5100     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5101     // Full-ASCII
5102     command.Clear();
5103     session->InputModeFullASCII(&command);
5104     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5105     // Half-ASCII
5106     command.Clear();
5107     session->InputModeHalfASCII(&command);
5108     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5109     // Direct
5110     command.Clear();
5111     session->IMEOff(&command);
5112     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5113 
5114     // Set config to 'half' -- all mode has to emit half-width space.
5115     config.set_space_character_form(config::Config::FUNDAMENTAL_HALF_WIDTH);
5116     session.reset(new Session(engine_.get()));
5117     session->SetConfig(&config);
5118     InitSessionToPrecomposition(session.get());
5119 
5120     // Hiragana
5121     command.Clear();
5122     session->InputModeHiragana(&command);
5123     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5124     // Full-Katakana
5125     command.Clear();
5126     session->InputModeFullKatakana(&command);
5127     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5128     // Half-Katakana
5129     command.Clear();
5130     session->InputModeHalfKatakana(&command);
5131     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5132     // Full-ASCII
5133     command.Clear();
5134     session->InputModeFullASCII(&command);
5135     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5136     // Half-ASCII
5137     command.Clear();
5138     session->InputModeHalfASCII(&command);
5139     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5140     // Direct
5141     command.Clear();
5142     session->IMEOff(&command);
5143     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5144 
5145     // Set config to 'FULL' -- all mode except for DIRECT emits
5146     // full-width space.
5147     config.set_space_character_form(config::Config::FUNDAMENTAL_FULL_WIDTH);
5148     session.reset(new Session(engine_.get()));
5149     session->SetConfig(&config);
5150     InitSessionToPrecomposition(session.get());
5151 
5152     // Hiragana
5153     command.Clear();
5154     session->InputModeHiragana(&command);
5155     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5156     // Full-Katakana
5157     command.Clear();
5158     session->InputModeFullKatakana(&command);
5159     EXPECT_TRUE(session->IsFullWidthInsertSpace(command.input()));
5160     // Half-Katakana
5161     command.Clear();
5162     session->InputModeHalfKatakana(&command);
5163     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5164     // Full-ASCII
5165     command.Clear();
5166     session->InputModeFullASCII(&command);
5167     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5168     // Half-ASCII
5169     command.Clear();
5170     session->InputModeHalfASCII(&command);
5171     EXPECT_TRUE(session->IsFullWidthInsertSpace(empty_input));
5172     // Direct
5173     command.Clear();
5174     session->IMEOff(&command);
5175     EXPECT_FALSE(session->IsFullWidthInsertSpace(empty_input));
5176   }
5177 
5178   { // When |input| has |input.key().mode()| field,
5179     // the specified input mode by |input| will be used.
5180 
5181     // Default config -- follow to the current mode.
5182     config.set_space_character_form(config::Config::FUNDAMENTAL_INPUT_MODE);
5183     session.reset(new Session(engine_.get()));
5184     session->SetConfig(&config);
5185     InitSessionToPrecomposition(session.get());
5186 
5187     // Use HALF_KATAKANA for the new input mode
5188     commands::Input input;
5189     input.mutable_key()->set_mode(commands::HALF_KATAKANA);
5190 
5191     // Hiragana
5192     commands::Command command;
5193     session->InputModeHiragana(&command);
5194     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5195     // Full-Katakana
5196     command.Clear();
5197     session->InputModeFullKatakana(&command);
5198     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5199     // Half-Katakana
5200     command.Clear();
5201     session->InputModeHalfKatakana(&command);
5202     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5203     // Full-ASCII
5204     command.Clear();
5205     session->InputModeFullASCII(&command);
5206     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5207     // Half-ASCII
5208     command.Clear();
5209     session->InputModeHalfASCII(&command);
5210     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5211     // Direct
5212     command.Clear();
5213     session->IMEOff(&command);
5214     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5215 
5216     // Use FULL_ASCII for the new input mode
5217     input.mutable_key()->set_mode(commands::FULL_ASCII);
5218 
5219     // Hiragana
5220     command.Clear();
5221     session->InputModeHiragana(&command);
5222     EXPECT_TRUE(session->IsFullWidthInsertSpace(input));
5223     // Full-Katakana
5224     command.Clear();
5225     session->InputModeFullKatakana(&command);
5226     EXPECT_TRUE(session->IsFullWidthInsertSpace(input));
5227     // Half-Katakana
5228     command.Clear();
5229     session->InputModeHalfKatakana(&command);
5230     EXPECT_TRUE(session->IsFullWidthInsertSpace(input));
5231     // Full-ASCII
5232     command.Clear();
5233     session->InputModeFullASCII(&command);
5234     EXPECT_TRUE(session->IsFullWidthInsertSpace(input));
5235     // Half-ASCII
5236     command.Clear();
5237     session->InputModeHalfASCII(&command);
5238     EXPECT_TRUE(session->IsFullWidthInsertSpace(input));
5239     // Direct
5240     command.Clear();
5241     session->IMEOff(&command);
5242     EXPECT_FALSE(session->IsFullWidthInsertSpace(input));
5243   }
5244 }
5245 
TEST_F(SessionTest,Issue1951385)5246 TEST_F(SessionTest, Issue1951385) {
5247   // This is a unittest against http://b/1951385
5248   Segments segments;
5249   std::unique_ptr<Session> session(new Session(engine_.get()));
5250   InitSessionToPrecomposition(session.get());
5251   commands::Command command;
5252 
5253   const string exceeded_preedit(500, 'a');
5254   ASSERT_EQ(500, exceeded_preedit.size());
5255   InsertCharacterChars(exceeded_preedit, session.get(), &command);
5256 
5257   ConversionRequest request;
5258   SetComposer(session.get(), &request);
5259   FillT13Ns(request, &segments);
5260   GetConverterMock()->SetStartConversionForRequest(&segments, false);
5261 
5262   command.Clear();
5263   session->ConvertToFullASCII(&command);
5264   EXPECT_FALSE(command.output().has_candidates());
5265 
5266   // The status should remain the preedit status, although the
5267   // previous command was convert.  The next command makes sure that
5268   // the preedit will disappear by canceling the preedit status.
5269   command.Clear();
5270   command.mutable_input()->mutable_key()->set_special_key(
5271       commands::KeyEvent::ESCAPE);
5272   EXPECT_FALSE(command.output().has_preedit());
5273 }
5274 
TEST_F(SessionTest,Issue1978201)5275 TEST_F(SessionTest, Issue1978201) {
5276   // This is a unittest against http://b/1978201
5277   Segments segments;
5278   Segment *segment;
5279   segment = segments.add_segment();
5280   segment->set_key("いんぼう");
5281   segment->add_candidate()->value = "陰謀";
5282   segment->add_candidate()->value = "陰謀論";
5283   segment->add_candidate()->value = "陰謀説";
5284   GetConverterMock()->SetStartPredictionForRequest(&segments, true);
5285 
5286   std::unique_ptr<Session> session(new Session(engine_.get()));
5287   InitSessionToPrecomposition(session.get());
5288   commands::Command command;
5289   EXPECT_TRUE(session->SegmentWidthShrink(&command));
5290 
5291   command.Clear();
5292   ConversionRequest request;
5293   SetComposer(session.get(), &request);
5294   FillT13Ns(request, &segments);
5295   GetConverterMock()->SetStartConversionForRequest(&segments, true);
5296   EXPECT_TRUE(session->Convert(&command));
5297 
5298   command.Clear();
5299   EXPECT_TRUE(session->CommitSegment(&command));
5300   EXPECT_RESULT("陰謀", command);
5301   EXPECT_FALSE(command.output().has_preedit());
5302 }
5303 
TEST_F(SessionTest,Issue1975771)5304 TEST_F(SessionTest, Issue1975771) {
5305   // This is a unittest against http://b/1975771
5306   std::unique_ptr<Session> session(new Session(engine_.get()));
5307   InitSessionToPrecomposition(session.get());
5308 
5309   // Trigger suggest by pressing "a".
5310   Segments segments;
5311   SetAiueo(&segments);
5312   GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
5313 
5314   commands::Command command;
5315   commands::KeyEvent* key_event = command.mutable_input()->mutable_key();
5316   key_event->set_key_code('a');
5317   key_event->set_modifiers(0);  // No modifiers.
5318   EXPECT_TRUE(session->InsertCharacter(&command));
5319 
5320   // Click the first candidate.
5321   SetSendCommandCommand(commands::SessionCommand::SELECT_CANDIDATE, &command);
5322   command.mutable_input()->mutable_command()->set_id(0);
5323   EXPECT_TRUE(session->SendCommand(&command));
5324 
5325   // After select candidate session->status_ should be
5326   // SessionStatus::CONVERSION.
5327 
5328   SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
5329   EXPECT_TRUE(command.output().has_candidates());
5330   // The second candidate should be selected.
5331   EXPECT_EQ(1, command.output().candidates().focused_index());
5332 }
5333 
TEST_F(SessionTest,Issue2029466)5334 TEST_F(SessionTest, Issue2029466) {
5335   // This is a unittest against http://b/2029466
5336   //
5337   // "a<tab><ctrl-N>a" raised an exception because CommitFirstSegment
5338   // did not check if the current status is in conversion or
5339   // precomposition.
5340   std::unique_ptr<Session> session(new Session(engine_.get()));
5341   InitSessionToPrecomposition(session.get());
5342 
5343   commands::Command command;
5344   InsertCharacterChars("a", session.get(), &command);
5345 
5346   // <tab>
5347   Segments segments;
5348   SetAiueo(&segments);
5349   GetConverterMock()->SetStartPredictionForRequest(&segments, true);
5350   command.Clear();
5351   EXPECT_TRUE(session->PredictAndConvert(&command));
5352 
5353   // <ctrl-N>
5354   segments.Clear();
5355   // FinishConversion is expected to return empty Segments.
5356   GetConverterMock()->SetFinishConversion(&segments, true);
5357   command.Clear();
5358   EXPECT_TRUE(session->CommitSegment(&command));
5359 
5360   InsertCharacterChars("a", session.get(), &command);
5361   EXPECT_SINGLE_SEGMENT("あ", command);
5362   EXPECT_FALSE(command.output().has_candidates());
5363 }
5364 
TEST_F(SessionTest,Issue2034943)5365 TEST_F(SessionTest, Issue2034943) {
5366   // This is a unittest against http://b/2029466
5367   //
5368   // The composition should have been reset if CommitSegment submitted
5369   // the all segments (e.g. the size of segments is one).
5370   std::unique_ptr<Session> session(new Session(engine_.get()));
5371   InitSessionToPrecomposition(session.get());
5372   commands::Command command;
5373   InsertCharacterChars("mozu", session.get(), &command);
5374 
5375   {  // Initialize a suggest result triggered by "mozu".
5376     Segments segments;
5377     Segment *segment = segments.add_segment();
5378     segment->set_key("mozu");
5379     Segment::Candidate *candidate;
5380     candidate = segment->add_candidate();
5381     candidate->value = "MOZU";
5382     ConversionRequest request;
5383     SetComposer(session.get(), &request);
5384     FillT13Ns(request, &segments);
5385     GetConverterMock()->SetStartConversionForRequest(&segments, true);
5386   }
5387   // Get conversion
5388   command.Clear();
5389   EXPECT_TRUE(session->Convert(&command));
5390 
5391   // submit segment
5392   command.Clear();
5393   EXPECT_TRUE(session->CommitSegment(&command));
5394 
5395   // The composition should have been reset.
5396   InsertCharacterChars("ku", session.get(), &command);
5397   EXPECT_EQ("く", command.output().preedit().segment(0).value());
5398 }
5399 
TEST_F(SessionTest,Issue2026354)5400 TEST_F(SessionTest, Issue2026354) {
5401   // This is a unittest against http://b/2026354
5402   std::unique_ptr<Session> session(new Session(engine_.get()));
5403   InitSessionToPrecomposition(session.get());
5404 
5405   commands::Command command;
5406   InsertCharacterChars("aiueo", session.get(), &command);
5407 
5408   // Trigger suggest by pressing "a".
5409   Segments segments;
5410   SetAiueo(&segments);
5411   ConversionRequest request;
5412   SetComposer(session.get(), &request);
5413   FillT13Ns(request, &segments);
5414   GetConverterMock()->SetStartConversionForRequest(&segments, true);
5415 
5416   command.Clear();
5417   EXPECT_TRUE(session->Convert(&command));
5418 
5419   //  EXPECT_TRUE(session->ConvertNext(&command));
5420   TestSendKey("Space", session.get(), &command);
5421   EXPECT_PREEDIT("あいうえお", command);
5422   command.mutable_output()->clear_candidates();
5423   EXPECT_FALSE(command.output().has_candidates());
5424 }
5425 
TEST_F(SessionTest,Issue2066906)5426 TEST_F(SessionTest, Issue2066906) {
5427   // This is a unittest against http://b/2066906
5428   Segments segments;
5429   Segment *segment;
5430   Segment::Candidate *candidate;
5431   std::unique_ptr<Session> session(new Session(engine_.get()));
5432   InitSessionToPrecomposition(session.get());
5433 
5434   segment = segments.add_segment();
5435   segment->set_key("a");
5436   candidate = segment->add_candidate();
5437   candidate->value = "abc";
5438   candidate = segment->add_candidate();
5439   candidate->value = "abcdef";
5440   GetConverterMock()->SetStartPredictionForRequest(&segments, true);
5441 
5442   // Prediction with "a"
5443   commands::Command command;
5444   EXPECT_TRUE(session->PredictAndConvert(&command));
5445   EXPECT_FALSE(command.output().has_result());
5446 
5447   // Commit
5448   command.Clear();
5449   EXPECT_TRUE(session->Commit(&command));
5450   EXPECT_RESULT("abc", command);
5451 
5452   GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
5453   InsertCharacterChars("a", session.get(), &command);
5454   EXPECT_FALSE(command.output().has_result());
5455 }
5456 
TEST_F(SessionTest,Issue2187132)5457 TEST_F(SessionTest, Issue2187132) {
5458   // This is a unittest against http://b/2187132
5459   std::unique_ptr<Session> session(new Session(engine_.get()));
5460   InitSessionToPrecomposition(session.get());
5461   commands::Command command;
5462 
5463   // Shift + Ascii triggers temporary input mode switch.
5464   SendKey("A", session.get(), &command);
5465   SendKey("Enter", session.get(), &command);
5466 
5467   // After submission, input mode should be reverted.
5468   SendKey("a", session.get(), &command);
5469   EXPECT_EQ("あ", GetComposition(command));
5470 
5471   command.Clear();
5472   session->EditCancel(&command);
5473   EXPECT_TRUE(GetComposition(command).empty());
5474 
5475   // If a user intentionally switched an input mode, it should remain.
5476   EXPECT_TRUE(session->InputModeHalfASCII(&command));
5477   SendKey("A", session.get(), &command);
5478   SendKey("Enter", session.get(), &command);
5479   SendKey("a", session.get(), &command);
5480   EXPECT_EQ("a", GetComposition(command));
5481 }
5482 
TEST_F(SessionTest,Issue2190364)5483 TEST_F(SessionTest, Issue2190364) {
5484   // This is a unittest against http://b/2190364
5485   config::Config config;
5486   config.set_preedit_method(config::Config::KANA);
5487 
5488   std::unique_ptr<Session> session(new Session(engine_.get()));
5489   session->SetConfig(&config);
5490   InitSessionToPrecomposition(session.get());
5491 
5492   commands::Command command;
5493   session->ToggleAlphanumericMode(&command);
5494 
5495   InsertCharacterCodeAndString('a', "ち", session.get(), &command);
5496   EXPECT_EQ("a", GetComposition(command));
5497 
5498   command.Clear();
5499   session->ToggleAlphanumericMode(&command);
5500   EXPECT_EQ("a", GetComposition(command));
5501 
5502   InsertCharacterCodeAndString('i', "に", session.get(), &command);
5503   EXPECT_EQ("aに", GetComposition(command));
5504 }
5505 
TEST_F(SessionTest,Issue1556649)5506 TEST_F(SessionTest, Issue1556649) {
5507   // This is a unittest against http://b/1556649
5508   std::unique_ptr<Session> session(new Session(engine_.get()));
5509   InitSessionToPrecomposition(session.get());
5510   commands::Command command;
5511   InsertCharacterChars("kudoudesu", session.get(), &command);
5512   EXPECT_EQ("くどうです",
5513             GetComposition(command));
5514   EXPECT_EQ(5, command.output().preedit().cursor());
5515 
5516   command.Clear();
5517   EXPECT_TRUE(session->DisplayAsHalfKatakana(&command));
5518   EXPECT_EQ("クドウデス", GetComposition(command));
5519   EXPECT_EQ(7, command.output().preedit().cursor());
5520 
5521   for (size_t i = 0; i < 7; ++i) {
5522     const size_t expected_pos = 6 - i;
5523     EXPECT_TRUE(SendKey("Left", session.get(), &command));
5524     EXPECT_EQ(expected_pos, command.output().preedit().cursor());
5525   }
5526 }
5527 
TEST_F(SessionTest,Issue1518994)5528 TEST_F(SessionTest, Issue1518994) {
5529   // This is a unittest against http://b/1518994.
5530   // - Can't input space in ascii mode.
5531   {
5532     std::unique_ptr<Session> session(new Session(engine_.get()));
5533     InitSessionToPrecomposition(session.get());
5534     commands::Command command;
5535     EXPECT_TRUE(SendKey("a", session.get(), &command));
5536     command.Clear();
5537     EXPECT_TRUE(session->ToggleAlphanumericMode(&command));
5538     EXPECT_TRUE(SendKey("i", session.get(), &command));
5539     EXPECT_EQ("あi", GetComposition(command));
5540 
5541     EXPECT_TRUE(SendKey("Space", session.get(), &command));
5542     EXPECT_EQ("あi ", GetComposition(command));
5543   }
5544 
5545   {
5546     std::unique_ptr<Session> session(new Session(engine_.get()));
5547     InitSessionToPrecomposition(session.get());
5548     commands::Command command;
5549     EXPECT_TRUE(SendKey("a", session.get(), &command));
5550     EXPECT_TRUE(SendKey("I", session.get(), &command));
5551     EXPECT_EQ("あI", GetComposition(command));
5552 
5553     EXPECT_TRUE(SendKey("Space", session.get(), &command));
5554     EXPECT_EQ("あI ", GetComposition(command));
5555   }
5556 }
5557 
TEST_F(SessionTest,Issue1571043)5558 TEST_F(SessionTest, Issue1571043) {
5559   // This is a unittest against http://b/1571043.
5560   // - Underline of composition is separated.
5561   std::unique_ptr<Session> session(new Session(engine_.get()));
5562   InitSessionToPrecomposition(session.get());
5563   commands::Command command;
5564   InsertCharacterChars("aiu", session.get(), &command);
5565   EXPECT_EQ("あいう", GetComposition(command));
5566 
5567   for (size_t i = 0; i < 3; ++i) {
5568     const size_t expected_pos = 2 - i;
5569     EXPECT_TRUE(SendKey("Left", session.get(), &command));
5570     EXPECT_EQ(expected_pos, command.output().preedit().cursor());
5571     EXPECT_EQ(1, command.output().preedit().segment_size());
5572   }
5573 }
5574 
TEST_F(SessionTest,Issue2217250)5575 TEST_F(SessionTest, Issue2217250) {
5576   // This is a unittest against http://b/2217250.
5577   // Temporary direct input mode through a special sequence such as
5578   // www. continues even after committing them
5579   std::unique_ptr<Session> session(new Session(engine_.get()));
5580   InitSessionToPrecomposition(session.get());
5581   commands::Command command;
5582   InsertCharacterChars("www.", session.get(), &command);
5583   EXPECT_EQ("www.", GetComposition(command));
5584   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5585 
5586   SendKey("Enter", session.get(), &command);
5587   EXPECT_EQ("www.", command.output().result().value());
5588   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5589 }
5590 
TEST_F(SessionTest,Issue2223823)5591 TEST_F(SessionTest, Issue2223823) {
5592   // This is a unittest against http://b/2223823
5593   // Input mode does not recover like MS-IME by single shift key down
5594   // and up.
5595   std::unique_ptr<Session> session(new Session(engine_.get()));
5596   InitSessionToPrecomposition(session.get());
5597   commands::Command command;
5598   SendKey("G", session.get(), &command);
5599   EXPECT_EQ("G", GetComposition(command));
5600   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5601 
5602   SendKey("Shift", session.get(), &command);
5603   EXPECT_EQ("G", GetComposition(command));
5604   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5605 }
5606 
5607 
TEST_F(SessionTest,Issue2223762)5608 TEST_F(SessionTest, Issue2223762) {
5609   // This is a unittest against http://b/2223762.
5610   // - The first space in half-width alphanumeric mode is full-width.
5611   std::unique_ptr<Session> session(new Session(engine_.get()));
5612   InitSessionToPrecomposition(session.get());
5613   commands::Command command;
5614 
5615   EXPECT_TRUE(session->InputModeHalfASCII(&command));
5616   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5617 
5618   EXPECT_TRUE(SendKey("Space", session.get(), &command));
5619   EXPECT_FALSE(command.output().consumed());
5620   EXPECT_FALSE(command.output().has_preedit());
5621   EXPECT_FALSE(command.output().has_result());
5622 }
5623 
5624 #ifndef OS_NACL
5625 // NaCl doesn't support Eisu key
TEST_F(SessionTest,Issue2223755)5626 TEST_F(SessionTest, Issue2223755) {
5627   // This is a unittest against http://b/2223755.
5628   // - F6 and F7 convert space to half-width.
5629 
5630   {  // DisplayAsFullKatakana
5631     std::unique_ptr<Session> session(new Session(engine_.get()));
5632     InitSessionToPrecomposition(session.get());
5633     commands::Command command;
5634 
5635     EXPECT_TRUE(SendKey("a", session.get(), &command));
5636     EXPECT_TRUE(SendKey("Eisu", session.get(), &command));
5637     EXPECT_TRUE(SendKey("Space", session.get(), &command));
5638     EXPECT_TRUE(SendKey("Eisu", session.get(), &command));
5639     EXPECT_TRUE(SendKey("i", session.get(), &command));
5640 
5641     EXPECT_EQ("あ い", GetComposition(command));
5642 
5643     command.Clear();
5644     EXPECT_TRUE(session->DisplayAsFullKatakana(&command));
5645 
5646     EXPECT_EQ("ア イ", GetComposition(command));  // fullwidth space
5647   }
5648 
5649   {  // ConvertToFullKatakana
5650     std::unique_ptr<Session> session(new Session(engine_.get()));
5651     InitSessionToPrecomposition(session.get());
5652     commands::Command command;
5653 
5654     EXPECT_TRUE(SendKey("a", session.get(), &command));
5655     EXPECT_TRUE(SendKey("Eisu", session.get(), &command));
5656     EXPECT_TRUE(SendKey("Space", session.get(), &command));
5657     EXPECT_TRUE(SendKey("Eisu", session.get(), &command));
5658     EXPECT_TRUE(SendKey("i", session.get(), &command));
5659 
5660     EXPECT_EQ("あ い", GetComposition(command));
5661 
5662     {  // Initialize GetConverterMock() to generate t13n candidates.
5663       Segments segments;
5664       Segment *segment;
5665       segments.set_request_type(Segments::CONVERSION);
5666       segment = segments.add_segment();
5667       segment->set_key("あ い");
5668       Segment::Candidate *candidate;
5669       candidate = segment->add_candidate();
5670       candidate->value = "あ い";
5671       ConversionRequest request;
5672       SetComposer(session.get(), &request);
5673       FillT13Ns(request, &segments);
5674       GetConverterMock()->SetStartConversionForRequest(&segments, true);
5675     }
5676 
5677     command.Clear();
5678     EXPECT_TRUE(session->ConvertToFullKatakana(&command));
5679 
5680     EXPECT_EQ("ア イ", GetComposition(command));  // fullwidth space
5681   }
5682 }
5683 #endif  // !OS_NACL
5684 
TEST_F(SessionTest,Issue2269058)5685 TEST_F(SessionTest, Issue2269058) {
5686   // This is a unittest against http://b/2269058.
5687   // - Temporary input mode should not be overridden by a permanent
5688   //   input mode change.
5689   std::unique_ptr<Session> session(new Session(engine_.get()));
5690   InitSessionToPrecomposition(session.get());
5691   commands::Command command;
5692 
5693   EXPECT_TRUE(SendKey("G", session.get(), &command));
5694   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5695 
5696   command.Clear();
5697   EXPECT_TRUE(session->InputModeHalfASCII(&command));
5698   EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5699 
5700   EXPECT_TRUE(SendKey("Shift", session.get(), &command));
5701   EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5702 }
5703 
TEST_F(SessionTest,Issue2272745)5704 TEST_F(SessionTest, Issue2272745) {
5705   // This is a unittest against http://b/2272745.
5706   // A temporary input mode remains when a composition is canceled.
5707   {
5708     std::unique_ptr<Session> session(new Session(engine_.get()));
5709     InitSessionToPrecomposition(session.get());
5710     commands::Command command;
5711 
5712     EXPECT_TRUE(SendKey("G", session.get(), &command));
5713     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5714 
5715     EXPECT_TRUE(SendKey("Backspace", session.get(), &command));
5716     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5717   }
5718 
5719   {
5720     std::unique_ptr<Session> session(new Session(engine_.get()));
5721     InitSessionToPrecomposition(session.get());
5722     commands::Command command;
5723 
5724     EXPECT_TRUE(SendKey("G", session.get(), &command));
5725     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5726 
5727     EXPECT_TRUE(SendKey("Escape", session.get(), &command));
5728     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5729   }
5730 }
5731 
TEST_F(SessionTest,Issue2282319)5732 TEST_F(SessionTest, Issue2282319) {
5733   // This is a unittest against http://b/2282319.
5734   // InsertFullSpace is not working in half-width input mode.
5735   config::Config config;
5736   config.set_session_keymap(config::Config::MSIME);
5737 
5738   std::unique_ptr<Session> session(new Session(engine_.get()));
5739   InitSessionToPrecomposition(session.get());
5740   session->SetConfig(&config);
5741 
5742   commands::Command command;
5743   EXPECT_TRUE(session->InputModeHalfASCII(&command));
5744   EXPECT_TRUE(command.output().consumed());
5745   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
5746 
5747   EXPECT_TRUE(TestSendKey("a", session.get(), &command));
5748   EXPECT_TRUE(command.output().consumed());
5749 
5750   EXPECT_TRUE(SendKey("a", session.get(), &command));
5751   EXPECT_TRUE(command.output().consumed());
5752   EXPECT_PREEDIT("a", command);
5753 
5754   EXPECT_TRUE(TestSendKey("Ctrl Shift Space", session.get(), &command));
5755   EXPECT_TRUE(command.output().consumed());
5756 
5757   EXPECT_TRUE(SendKey("Ctrl Shift Space", session.get(), &command));
5758   EXPECT_TRUE(command.output().consumed());
5759   EXPECT_PREEDIT("a ", command);  // Full-width space
5760 }
5761 
TEST_F(SessionTest,Issue2297060)5762 TEST_F(SessionTest, Issue2297060) {
5763   // This is a unittest against http://b/2297060.
5764   // Ctrl-Space is not working
5765   config::Config config;
5766   config.set_session_keymap(config::Config::MSIME);
5767 
5768   std::unique_ptr<Session> session(new Session(engine_.get()));
5769   InitSessionToPrecomposition(session.get());
5770   session->SetConfig(&config);
5771 
5772   commands::Command command;
5773   EXPECT_TRUE(SendKey("Ctrl Space", session.get(), &command));
5774   EXPECT_FALSE(command.output().consumed());
5775 }
5776 
TEST_F(SessionTest,Issue2379374)5777 TEST_F(SessionTest, Issue2379374) {
5778   // This is a unittest against http://b/2379374.
5779   // Numpad ignores Direct input style when typing after conversion.
5780   std::unique_ptr<Session> session(new Session(engine_.get()));
5781   InitSessionToPrecomposition(session.get());
5782   commands::Command command;
5783 
5784   // Set numpad_character_form with NUMPAD_DIRECT_INPUT
5785   config::Config config;
5786   config.set_numpad_character_form(config::Config::NUMPAD_DIRECT_INPUT);
5787   session->SetConfig(&config);
5788 
5789   Segments segments;
5790   {  // Set mock conversion.
5791     Segment *segment;
5792     Segment::Candidate *candidate;
5793 
5794     segment = segments.add_segment();
5795     segment->set_key("あ");
5796     candidate = segment->add_candidate();
5797     candidate->value = "亜";
5798     ConversionRequest request;
5799     request.set_config(&config);
5800     SetComposer(session.get(), &request);
5801     FillT13Ns(request, &segments);
5802     GetConverterMock()->SetStartConversionForRequest(&segments, true);
5803   }
5804 
5805   EXPECT_TRUE(SendKey("a", session.get(), &command));
5806   EXPECT_EQ("あ", GetComposition(command));
5807 
5808   EXPECT_TRUE(SendKey("Space", session.get(), &command));
5809   EXPECT_EQ("亜", GetComposition(command));
5810 
5811   EXPECT_TRUE(SendKey("Numpad0", session.get(), &command));
5812   EXPECT_TRUE(GetComposition(command).empty());
5813   EXPECT_RESULT_AND_KEY("亜0", "あ0", command);
5814 
5815   // The previous Numpad0 must not affect the current composition.
5816   EXPECT_TRUE(SendKey("a", session.get(), &command));
5817   EXPECT_EQ("あ", GetComposition(command));
5818 }
5819 
TEST_F(SessionTest,Issue2569789)5820 TEST_F(SessionTest, Issue2569789) {
5821   // This is a unittest against http://b/2379374.
5822   // After typing "google", the input mode does not come back to the
5823   // previous input mode.
5824   {
5825     std::unique_ptr<Session> session(new Session(engine_.get()));
5826     InitSessionToPrecomposition(session.get());
5827     commands::Command command;
5828 
5829     InsertCharacterChars("google", session.get(), &command);
5830     EXPECT_EQ("google", GetComposition(command));
5831     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5832 
5833     EXPECT_TRUE(SendKey("enter", session.get(), &command));
5834     ASSERT_TRUE(command.output().has_result());
5835     EXPECT_EQ("google", command.output().result().value());
5836     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5837   }
5838 
5839   {
5840     std::unique_ptr<Session> session(new Session(engine_.get()));
5841     InitSessionToPrecomposition(session.get());
5842     commands::Command command;
5843 
5844     InsertCharacterChars("Google", session.get(), &command);
5845     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5846 
5847     EXPECT_TRUE(SendKey("enter", session.get(), &command));
5848     ASSERT_TRUE(command.output().has_result());
5849     EXPECT_EQ("Google", command.output().result().value());
5850     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5851   }
5852 
5853   {
5854     std::unique_ptr<Session> session(new Session(engine_.get()));
5855     InitSessionToPrecomposition(session.get());
5856     commands::Command command;
5857 
5858     InsertCharacterChars("Google", session.get(), &command);
5859     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5860 
5861     EXPECT_TRUE(SendKey("shift", session.get(), &command));
5862     EXPECT_EQ("Google", GetComposition(command));
5863     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5864 
5865     InsertCharacterChars("aaa", session.get(), &command);
5866     EXPECT_EQ("Googleあああ", GetComposition(command));
5867   }
5868 
5869   {
5870     std::unique_ptr<Session> session(new Session(engine_.get()));
5871     InitSessionToPrecomposition(session.get());
5872     commands::Command command;
5873 
5874     InsertCharacterChars("http", session.get(), &command);
5875     EXPECT_EQ(commands::HALF_ASCII, command.output().mode());
5876 
5877     EXPECT_TRUE(SendKey("enter", session.get(), &command));
5878     ASSERT_TRUE(command.output().has_result());
5879     EXPECT_EQ("http", command.output().result().value());
5880     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
5881   }
5882 }
5883 
TEST_F(SessionTest,Issue2555503)5884 TEST_F(SessionTest, Issue2555503) {
5885   // This is a unittest against http://b/2555503.
5886   // Mode respects the previous character too much.
5887 
5888   std::unique_ptr<Session> session(new Session(engine_.get()));
5889   InitSessionToPrecomposition(session.get());
5890   commands::Command command;
5891   SendKey("a", session.get(), &command);
5892 
5893   command.Clear();
5894   session->InputModeFullKatakana(&command);
5895 
5896   SendKey("i", session.get(), &command);
5897   EXPECT_EQ("あイ", GetComposition(command));
5898 
5899   SendKey("backspace", session.get(), &command);
5900   EXPECT_EQ("あ", GetComposition(command));
5901   EXPECT_EQ(commands::FULL_KATAKANA, command.output().mode());
5902 }
5903 
5904 #ifndef OS_NACL
5905 // NaCl doesn't support hankaku/zenkaku key.
TEST_F(SessionTest,Issue2791640)5906 TEST_F(SessionTest, Issue2791640) {
5907   // This is a unittest against http://b/2791640.
5908   // Existing preedit should be committed when IME is turned off.
5909 
5910   std::unique_ptr<Session> session(new Session(engine_.get()));
5911   InitSessionToPrecomposition(session.get());
5912 
5913   commands::Command command;
5914   SendKey("a", session.get(), &command);
5915   SendKey("hankaku/zenkaku", session.get(), &command);
5916 
5917   ASSERT_TRUE(command.output().consumed());
5918 
5919   ASSERT_TRUE(command.output().has_result());
5920   EXPECT_EQ("あ", command.output().result().value());
5921   EXPECT_EQ(commands::DIRECT, command.output().mode());
5922 
5923   ASSERT_FALSE(command.output().has_preedit());
5924 }
5925 #endif  // !OS_NACL
5926 
5927 #ifndef OS_NACL
5928 // NaCl doesn't support hankaku/zenkaku key.
TEST_F(SessionTest,CommitExistingPreeditWhenIMEIsTurnedOff)5929 TEST_F(SessionTest, CommitExistingPreeditWhenIMEIsTurnedOff) {
5930   // Existing preedit should be committed when IME is turned off.
5931 
5932   // Check "hankaku/zenkaku"
5933   {
5934     std::unique_ptr<Session> session(new Session(engine_.get()));
5935     InitSessionToPrecomposition(session.get());
5936 
5937     commands::Command command;
5938     SendKey("a", session.get(), &command);
5939     SendKey("hankaku/zenkaku", session.get(), &command);
5940 
5941     ASSERT_TRUE(command.output().consumed());
5942 
5943     ASSERT_TRUE(command.output().has_result());
5944     EXPECT_EQ("あ", command.output().result().value());
5945     EXPECT_EQ(commands::DIRECT, command.output().mode());
5946 
5947     ASSERT_FALSE(command.output().has_preedit());
5948   }
5949 
5950   // Check "kanji"
5951   {
5952     std::unique_ptr<Session> session(new Session(engine_.get()));
5953     InitSessionToPrecomposition(session.get());
5954 
5955     commands::Command command;
5956     SendKey("a", session.get(), &command);
5957     SendKey("kanji", session.get(), &command);
5958 
5959     ASSERT_TRUE(command.output().consumed());
5960 
5961     ASSERT_TRUE(command.output().has_result());
5962     EXPECT_EQ("あ", command.output().result().value());
5963     EXPECT_EQ(commands::DIRECT, command.output().mode());
5964 
5965     ASSERT_FALSE(command.output().has_preedit());
5966   }
5967 }
5968 #endif  // !OS_NACL
5969 
TEST_F(SessionTest,SendKeyDirectInputStateTest)5970 TEST_F(SessionTest, SendKeyDirectInputStateTest) {
5971   // InputModeChange commands from direct mode are supported only for Windows
5972   // for now.
5973 #ifdef OS_WIN
5974   config::Config config;
5975   const string custom_keymap_table =
5976       "status\tkey\tcommand\n"
5977       "DirectInput\tHiragana\tInputModeHiragana\n";
5978   config.set_session_keymap(config::Config::CUSTOM);
5979   config.set_custom_keymap_table(custom_keymap_table);
5980 
5981   std::unique_ptr<Session> session(new Session(engine_.get()));
5982   session->SetConfig(&config);
5983   InitSessionToDirect(session.get());
5984   commands::Command command;
5985 
5986   EXPECT_TRUE(SendKey("Hiragana", session.get(), &command));
5987   EXPECT_TRUE(SendKey("a", session.get(), &command));
5988   EXPECT_SINGLE_SEGMENT("あ", command);
5989 #endif  // OS_WIN
5990 }
5991 
TEST_F(SessionTest,HandlingDirectInputTableAttribute)5992 TEST_F(SessionTest, HandlingDirectInputTableAttribute) {
5993   composer::Table table;
5994   table.AddRuleWithAttributes("ka", "か", "",
5995                               composer::DIRECT_INPUT);
5996   table.AddRuleWithAttributes("tt", "っ", "t",
5997                               composer::DIRECT_INPUT);
5998   table.AddRuleWithAttributes("ta", "た", "",
5999                               composer::NO_TABLE_ATTRIBUTE);
6000 
6001   Session session(engine_.get());
6002   InitSessionToPrecomposition(&session);
6003   session.get_internal_composer_only_for_unittest()->SetTable(&table);
6004 
6005   commands::Command command;
6006   SendKey("k", &session, &command);
6007   EXPECT_FALSE(command.output().has_result());
6008 
6009   SendKey("a", &session, &command);
6010   EXPECT_RESULT("か", command);
6011 
6012   SendKey("t", &session, &command);
6013   EXPECT_FALSE(command.output().has_result());
6014 
6015   SendKey("t", &session, &command);
6016   EXPECT_FALSE(command.output().has_result());
6017 
6018   SendKey("a", &session, &command);
6019   EXPECT_RESULT("った", command);
6020 }
6021 
TEST_F(SessionTest,IMEOnWithModeTest)6022 TEST_F(SessionTest, IMEOnWithModeTest) {
6023   {
6024     std::unique_ptr<Session> session(new Session(engine_.get()));
6025     InitSessionToDirect(session.get());
6026 
6027     commands::Command command;
6028     command.mutable_input()->mutable_key()->set_mode(
6029         commands::HIRAGANA);
6030     EXPECT_TRUE(session->IMEOn(&command));
6031     EXPECT_TRUE(command.output().has_consumed());
6032     EXPECT_TRUE(command.output().consumed());
6033     EXPECT_TRUE(command.output().has_mode());
6034     EXPECT_EQ(commands::HIRAGANA,
6035               command.output().mode());
6036     SendKey("a", session.get(), &command);
6037     EXPECT_SINGLE_SEGMENT("あ", command);
6038   }
6039   {
6040     std::unique_ptr<Session> session(new Session(engine_.get()));
6041     InitSessionToDirect(session.get());
6042 
6043     commands::Command command;
6044     command.mutable_input()->mutable_key()->set_mode(
6045         commands::FULL_KATAKANA);
6046     EXPECT_TRUE(session->IMEOn(&command));
6047     EXPECT_TRUE(command.output().has_mode());
6048     EXPECT_EQ(commands::FULL_KATAKANA,
6049               command.output().mode());
6050     SendKey("a", session.get(), &command);
6051     EXPECT_SINGLE_SEGMENT("ア", command);
6052   }
6053   {
6054     std::unique_ptr<Session> session(new Session(engine_.get()));
6055     InitSessionToDirect(session.get());
6056 
6057     commands::Command command;
6058     command.mutable_input()->mutable_key()->set_mode(
6059         commands::HALF_KATAKANA);
6060     EXPECT_TRUE(session->IMEOn(&command));
6061     EXPECT_TRUE(command.output().has_mode());
6062     EXPECT_EQ(commands::HALF_KATAKANA,
6063               command.output().mode());
6064     SendKey("a", session.get(), &command);
6065     // "ア" (half-width Katakana)
6066     EXPECT_SINGLE_SEGMENT("ア", command);
6067   }
6068   {
6069     std::unique_ptr<Session> session(new Session(engine_.get()));
6070     InitSessionToDirect(session.get());
6071 
6072     commands::Command command;
6073     command.mutable_input()->mutable_key()->set_mode(
6074         commands::FULL_ASCII);
6075     EXPECT_TRUE(session->IMEOn(&command));
6076     EXPECT_TRUE(command.output().has_mode());
6077     EXPECT_EQ(commands::FULL_ASCII,
6078               command.output().mode());
6079     SendKey("a", session.get(), &command);
6080     EXPECT_SINGLE_SEGMENT("a", command);
6081   }
6082   {
6083     std::unique_ptr<Session> session(new Session(engine_.get()));
6084     InitSessionToDirect(session.get());
6085 
6086     commands::Command command;
6087     command.mutable_input()->mutable_key()->set_mode(
6088         commands::HALF_ASCII);
6089     EXPECT_TRUE(session->IMEOn(&command));
6090     EXPECT_TRUE(command.output().has_mode());
6091     EXPECT_EQ(commands::HALF_ASCII,
6092               command.output().mode());
6093     SendKey("a", session.get(), &command);
6094     EXPECT_SINGLE_SEGMENT("a", command);
6095   }
6096 }
6097 
TEST_F(SessionTest,InputModeConsumed)6098 TEST_F(SessionTest, InputModeConsumed) {
6099   std::unique_ptr<Session> session(new Session(engine_.get()));
6100   InitSessionToPrecomposition(session.get());
6101   commands::Command command;
6102   EXPECT_TRUE(session->InputModeHiragana(&command));
6103   EXPECT_TRUE(command.output().consumed());
6104   EXPECT_EQ(mozc::commands::HIRAGANA, command.output().mode());
6105   command.Clear();
6106   EXPECT_TRUE(session->InputModeFullKatakana(&command));
6107   EXPECT_TRUE(command.output().consumed());
6108   EXPECT_EQ(mozc::commands::FULL_KATAKANA, command.output().mode());
6109   command.Clear();
6110   EXPECT_TRUE(session->InputModeHalfKatakana(&command));
6111   EXPECT_TRUE(command.output().consumed());
6112   EXPECT_EQ(mozc::commands::HALF_KATAKANA, command.output().mode());
6113   command.Clear();
6114   EXPECT_TRUE(session->InputModeFullASCII(&command));
6115   EXPECT_TRUE(command.output().consumed());
6116   EXPECT_EQ(mozc::commands::FULL_ASCII, command.output().mode());
6117   command.Clear();
6118   EXPECT_TRUE(session->InputModeHalfASCII(&command));
6119   EXPECT_TRUE(command.output().consumed());
6120   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
6121 }
6122 
TEST_F(SessionTest,InputModeConsumedForTestSendKey)6123 TEST_F(SessionTest, InputModeConsumedForTestSendKey) {
6124   // This test is only for Windows, because InputModeHiragana bound
6125   // with Hiragana key is only supported on Windows yet.
6126 #ifdef OS_WIN
6127   config::Config config;
6128   config.set_session_keymap(config::Config::MSIME);
6129 
6130   std::unique_ptr<Session> session(new Session(engine_.get()));
6131   session->SetConfig(&config);
6132   InitSessionToPrecomposition(session.get());
6133   // In MSIME keymap, Hiragana is assigned for
6134   // ImputModeHiragana in Precomposition.
6135 
6136   commands::Command command;
6137   EXPECT_TRUE(TestSendKey("Hiragana", session.get(), &command));
6138   EXPECT_TRUE(command.output().consumed());
6139 #endif  // OS_WIN
6140 }
6141 
TEST_F(SessionTest,InputModeOutputHasComposition)6142 TEST_F(SessionTest, InputModeOutputHasComposition) {
6143   std::unique_ptr<Session> session(new Session(engine_.get()));
6144   InitSessionToPrecomposition(session.get());
6145   commands::Command command;
6146   SendKey("a", session.get(), &command);
6147   EXPECT_SINGLE_SEGMENT("あ", command);
6148 
6149   command.Clear();
6150   EXPECT_TRUE(session->InputModeHiragana(&command));
6151   EXPECT_TRUE(command.output().consumed());
6152   EXPECT_EQ(mozc::commands::HIRAGANA, command.output().mode());
6153   EXPECT_SINGLE_SEGMENT("あ", command);
6154 
6155   command.Clear();
6156   EXPECT_TRUE(session->InputModeFullKatakana(&command));
6157   EXPECT_TRUE(command.output().consumed());
6158   EXPECT_EQ(mozc::commands::FULL_KATAKANA, command.output().mode());
6159   EXPECT_SINGLE_SEGMENT("あ", command);
6160 
6161   command.Clear();
6162   EXPECT_TRUE(session->InputModeHalfKatakana(&command));
6163   EXPECT_TRUE(command.output().consumed());
6164   EXPECT_EQ(mozc::commands::HALF_KATAKANA, command.output().mode());
6165   EXPECT_SINGLE_SEGMENT("あ", command);
6166 
6167   command.Clear();
6168   EXPECT_TRUE(session->InputModeFullASCII(&command));
6169   EXPECT_TRUE(command.output().consumed());
6170   EXPECT_EQ(mozc::commands::FULL_ASCII, command.output().mode());
6171   EXPECT_SINGLE_SEGMENT("あ", command);
6172 
6173   command.Clear();
6174   EXPECT_TRUE(session->InputModeHalfASCII(&command));
6175   EXPECT_TRUE(command.output().consumed());
6176   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
6177   EXPECT_SINGLE_SEGMENT("あ", command);
6178 }
6179 
TEST_F(SessionTest,InputModeOutputHasCandidates)6180 TEST_F(SessionTest, InputModeOutputHasCandidates) {
6181   std::unique_ptr<Session> session(new Session(engine_.get()));
6182   InitSessionToPrecomposition(session.get());
6183 
6184   Segments segments;
6185   SetAiueo(&segments);
6186   ConversionRequest request;
6187   SetComposer(session.get(), &request);
6188   FillT13Ns(request, &segments);
6189   GetConverterMock()->SetStartConversionForRequest(&segments, true);
6190 
6191   commands::Command command;
6192   InsertCharacterChars("aiueo", session.get(), &command);
6193 
6194   command.Clear();
6195   session->Convert(&command);
6196   session->ConvertNext(&command);
6197   EXPECT_TRUE(command.output().has_candidates());
6198   EXPECT_TRUE(command.output().has_preedit());
6199 
6200   command.Clear();
6201   EXPECT_TRUE(session->InputModeHiragana(&command));
6202   EXPECT_TRUE(command.output().consumed());
6203   EXPECT_EQ(mozc::commands::HIRAGANA, command.output().mode());
6204   EXPECT_TRUE(command.output().has_candidates());
6205   EXPECT_TRUE(command.output().has_preedit());
6206 
6207   command.Clear();
6208   EXPECT_TRUE(session->InputModeFullKatakana(&command));
6209   EXPECT_TRUE(command.output().consumed());
6210   EXPECT_EQ(mozc::commands::FULL_KATAKANA, command.output().mode());
6211   EXPECT_TRUE(command.output().has_candidates());
6212   EXPECT_TRUE(command.output().has_preedit());
6213 
6214   command.Clear();
6215   EXPECT_TRUE(session->InputModeHalfKatakana(&command));
6216   EXPECT_TRUE(command.output().consumed());
6217   EXPECT_EQ(mozc::commands::HALF_KATAKANA, command.output().mode());
6218   EXPECT_TRUE(command.output().has_candidates());
6219   EXPECT_TRUE(command.output().has_preedit());
6220 
6221   command.Clear();
6222   EXPECT_TRUE(session->InputModeFullASCII(&command));
6223   EXPECT_TRUE(command.output().consumed());
6224   EXPECT_EQ(mozc::commands::FULL_ASCII, command.output().mode());
6225   EXPECT_TRUE(command.output().has_candidates());
6226   EXPECT_TRUE(command.output().has_preedit());
6227 
6228   command.Clear();
6229   EXPECT_TRUE(session->InputModeHalfASCII(&command));
6230   EXPECT_TRUE(command.output().consumed());
6231   EXPECT_EQ(mozc::commands::HALF_ASCII, command.output().mode());
6232   EXPECT_TRUE(command.output().has_candidates());
6233   EXPECT_TRUE(command.output().has_preedit());
6234 }
6235 
6236 #ifndef OS_NACL
6237 // NaCl doesn't support KeyEvent::ON|OFF.
TEST_F(SessionTest,PerformedCommand)6238 TEST_F(SessionTest, PerformedCommand) {
6239   std::unique_ptr<Session> session(new Session(engine_.get()));
6240   InitSessionToPrecomposition(session.get());
6241 
6242   {
6243     commands::Command command;
6244     // IMEOff
6245     EXPECT_STATS_NOT_EXIST("Performed_Precomposition_IMEOff");
6246     SendSpecialKey(commands::KeyEvent::OFF, session.get(), &command);
6247     EXPECT_COUNT_STATS("Performed_Precomposition_IMEOff", 1);
6248   }
6249   {
6250     commands::Command command;
6251     // IMEOn
6252     EXPECT_STATS_NOT_EXIST("Performed_Direct_IMEOn");
6253     SendSpecialKey(commands::KeyEvent::ON, session.get(), &command);
6254     EXPECT_COUNT_STATS("Performed_Direct_IMEOn", 1);
6255   }
6256   {
6257     commands::Command command;
6258     // 'a'
6259     EXPECT_STATS_NOT_EXIST("Performed_Precomposition_InsertCharacter");
6260     SendKey("a", session.get(), &command);
6261     EXPECT_COUNT_STATS("Performed_Precomposition_InsertCharacter", 1);
6262   }
6263   {
6264     // SetStartConversion for changing state to Convert.
6265     Segments segments;
6266     SetAiueo(&segments);
6267     ConversionRequest request;
6268     SetComposer(session.get(), &request);
6269     FillT13Ns(request, &segments);
6270     GetConverterMock()->SetStartConversionForRequest(&segments, true);
6271     commands::Command command;
6272     // SPACE
6273     EXPECT_STATS_NOT_EXIST("Performed_Composition_Convert");
6274     SendSpecialKey(commands::KeyEvent::SPACE, session.get(), &command);
6275     EXPECT_COUNT_STATS("Performed_Composition_Convert", 1);
6276   }
6277   {
6278     commands::Command command;
6279     // ENTER
6280     EXPECT_STATS_NOT_EXIST("Performed_Conversion_Commit");
6281     SendSpecialKey(commands::KeyEvent::ENTER, session.get(), &command);
6282     EXPECT_COUNT_STATS("Performed_Conversion_Commit", 1);
6283   }
6284 }
6285 #endif  // !OS_NACL
6286 
TEST_F(SessionTest,ResetContext)6287 TEST_F(SessionTest, ResetContext) {
6288   std::unique_ptr<MockConverterEngineForReset> engine(
6289       new MockConverterEngineForReset);
6290   ConverterMockForReset *convertermock = engine->mutable_converter_mock();
6291 
6292   std::unique_ptr<Session> session(new Session(engine.get()));
6293   InitSessionToPrecomposition(session.get());
6294   commands::Command command;
6295 
6296   session->ResetContext(&command);
6297   EXPECT_FALSE(command.output().consumed());
6298   EXPECT_TRUE(convertermock->reset_conversion_called());
6299 
6300   convertermock->Reset();
6301   EXPECT_TRUE(SendKey("A", session.get(), &command));
6302   command.Clear();
6303   session->ResetContext(&command);
6304   EXPECT_TRUE(command.output().consumed());
6305   EXPECT_TRUE(convertermock->reset_conversion_called());
6306 }
6307 
TEST_F(SessionTest,ClearUndoOnResetContext)6308 TEST_F(SessionTest, ClearUndoOnResetContext) {
6309   std::unique_ptr<Session> session(new Session(engine_.get()));
6310   InitSessionToPrecomposition(session.get());
6311 
6312   // Undo requires capability DELETE_PRECEDING_TEXT.
6313   commands::Capability capability;
6314   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
6315   session->set_client_capability(capability);
6316 
6317   commands::Command command;
6318   Segments segments;
6319 
6320   {  // Create segments
6321     InsertCharacterChars("aiueo", session.get(), &command);
6322     ConversionRequest request;
6323     SetComposer(session.get(), &request);
6324     SetAiueo(&segments);
6325     // Don't use FillT13Ns(). It makes platform dependent segments.
6326     // TODO(hsumita): Makes FillT13Ns() independent from platforms.
6327     Segment::Candidate *candidate;
6328     candidate = segments.mutable_segment(0)->add_candidate();
6329     candidate->value = "aiueo";
6330     candidate = segments.mutable_segment(0)->add_candidate();
6331     candidate->value = "AIUEO";
6332   }
6333 
6334   {
6335     GetConverterMock()->SetStartConversionForRequest(&segments, true);
6336     command.Clear();
6337     session->Convert(&command);
6338     EXPECT_FALSE(command.output().has_result());
6339     EXPECT_SINGLE_SEGMENT("あいうえお", command);
6340 
6341     GetConverterMock()->SetCommitSegmentValue(&segments, true);
6342     command.Clear();
6343     session->Commit(&command);
6344     EXPECT_FALSE(command.output().has_preedit());
6345     EXPECT_RESULT("あいうえお", command);
6346 
6347     command.Clear();
6348     session->ResetContext(&command);
6349 
6350     command.Clear();
6351     session->Undo(&command);
6352     // After reset, undo shouldn't run.
6353     EXPECT_FALSE(command.output().has_preedit());
6354   }
6355 }
6356 
TEST_F(SessionTest,IssueResetConversion)6357 TEST_F(SessionTest, IssueResetConversion) {
6358   std::unique_ptr<MockConverterEngineForReset> engine(
6359       new MockConverterEngineForReset);
6360   ConverterMockForReset *convertermock = engine->mutable_converter_mock();
6361 
6362   std::unique_ptr<Session> session(new Session(engine.get()));
6363   InitSessionToPrecomposition(session.get());
6364   commands::Command command;
6365 
6366   // any meaneangless key calls ResetConversion
6367   EXPECT_FALSE(convertermock->reset_conversion_called());
6368   EXPECT_TRUE(SendKey("enter", session.get(), &command));
6369   EXPECT_TRUE(convertermock->reset_conversion_called());
6370 
6371   convertermock->Reset();
6372   EXPECT_FALSE(convertermock->reset_conversion_called());
6373   EXPECT_TRUE(SendKey("space", session.get(), &command));
6374   EXPECT_TRUE(convertermock->reset_conversion_called());
6375 }
6376 
TEST_F(SessionTest,IssueRevert)6377 TEST_F(SessionTest, IssueRevert) {
6378   std::unique_ptr<MockConverterEngineForRevert> engine(
6379       new MockConverterEngineForRevert);
6380   ConverterMockForRevert *convertermock = engine->mutable_converter_mock();
6381 
6382   std::unique_ptr<Session> session(new Session(engine.get()));
6383   InitSessionToPrecomposition(session.get());
6384   commands::Command command;
6385 
6386   // changes the state to PRECOMPOSITION
6387   session->IMEOn(&command);
6388 
6389   session->Revert(&command);
6390 
6391   EXPECT_FALSE(command.output().consumed());
6392   EXPECT_TRUE(convertermock->revert_conversion_called());
6393 }
6394 
6395 // Undo command must call RervertConversion
TEST_F(SessionTest,Issue3428520)6396 TEST_F(SessionTest, Issue3428520) {
6397   std::unique_ptr<MockConverterEngineForRevert> engine(
6398       new MockConverterEngineForRevert);
6399   ConverterMockForRevert *convertermock = engine->mutable_converter_mock();
6400 
6401   std::unique_ptr<Session> session(new Session(engine.get()));
6402   InitSessionToPrecomposition(session.get());
6403 
6404   // Undo requires capability DELETE_PRECEDING_TEXT.
6405   commands::Capability capability;
6406   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
6407   session->set_client_capability(capability);
6408 
6409   commands::Command command;
6410   Segments segments;
6411 
6412   InsertCharacterChars("aiueo", session.get(), &command);
6413   ConversionRequest request;
6414   SetComposer(session.get(), &request);
6415   SetAiueo(&segments);
6416   FillT13Ns(request, &segments);
6417   convertermock->SetStartConversionForRequest(&segments, true);
6418 
6419   command.Clear();
6420   session->Convert(&command);
6421   EXPECT_FALSE(command.output().has_result());
6422   EXPECT_SINGLE_SEGMENT("あいうえお", command);
6423 
6424   convertermock->SetCommitSegmentValue(&segments, true);
6425   command.Clear();
6426   session->Commit(&command);
6427   EXPECT_FALSE(command.output().has_preedit());
6428   EXPECT_RESULT("あいうえお", command);
6429 
6430   command.Clear();
6431   session->Undo(&command);
6432 
6433   // After check the status of revert_conversion_called.
6434   EXPECT_TRUE(convertermock->revert_conversion_called());
6435 }
6436 
6437 // Revert command must clear the undo context.
TEST_F(SessionTest,Issue5742293)6438 TEST_F(SessionTest, Issue5742293) {
6439   config::Config config;
6440   config.set_session_keymap(config::Config::MSIME);
6441 
6442   std::unique_ptr<Session> session(new Session(engine_.get()));
6443   session->SetConfig(&config);
6444   InitSessionToPrecomposition(session.get());
6445 
6446   // Undo requires capability DELETE_PRECEDING_TEXT.
6447   commands::Capability capability;
6448   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
6449   session->set_client_capability(capability);
6450 
6451 
6452   SetUndoContext(session.get());
6453 
6454   commands::Command command;
6455 
6456   // BackSpace key event issues Revert command, which should clear the undo
6457   // context.
6458   EXPECT_TRUE(SendKey("Backspace", session.get(), &command));
6459 
6460   // Ctrl+BS should be consumed as UNDO.
6461   EXPECT_TRUE(TestSendKey("Ctrl Backspace", session.get(), &command));
6462 
6463   EXPECT_FALSE(command.output().consumed());
6464 }
6465 
TEST_F(SessionTest,AutoConversion)6466 TEST_F(SessionTest, AutoConversion) {
6467   Segments segments;
6468   SetAiueo(&segments);
6469   ConversionRequest default_request;
6470   FillT13Ns(default_request, &segments);
6471   GetConverterMock()->SetStartConversionForRequest(&segments, true);
6472 
6473   // Auto Off
6474   config::Config config;
6475   config.set_use_auto_conversion(false);
6476   {
6477     std::unique_ptr<Session> session(new Session(engine_.get()));
6478     session->SetConfig(&config);
6479     InitSessionToPrecomposition(session.get());
6480     commands::Command command;
6481 
6482     // The last "." is a triggering key for auto conversion
6483     InsertCharacterChars("tesuto.", session.get(), &command);
6484 
6485     EXPECT_SINGLE_SEGMENT_AND_KEY("てすと。", "てすと。", command);
6486   }
6487   {
6488     std::unique_ptr<Session> session(new Session(engine_.get()));
6489     session->SetConfig(&config);
6490     InitSessionToPrecomposition(session.get());
6491     commands::Command command;
6492 
6493     // The last "." is a triggering key for auto conversion
6494     InsertCharacterString("てすと。", "wrs/", session.get(), &command);
6495 
6496     EXPECT_SINGLE_SEGMENT_AND_KEY("てすと。", "てすと。", command);
6497   }
6498 
6499   // Auto On
6500   config.set_use_auto_conversion(true);
6501   {
6502     std::unique_ptr<Session> session(new Session(engine_.get()));
6503     session->SetConfig(&config);
6504     InitSessionToPrecomposition(session.get());
6505 
6506     commands::Command command;
6507 
6508     // The last "." is a triggering key for auto conversion
6509     InsertCharacterChars("tesuto.", session.get(), &command);
6510 
6511     EXPECT_SINGLE_SEGMENT_AND_KEY("あいうえお", "あいうえお", command);
6512   }
6513   {
6514     std::unique_ptr<Session> session(new Session(engine_.get()));
6515     session->SetConfig(&config);
6516     InitSessionToPrecomposition(session.get());
6517 
6518     commands::Command command;
6519 
6520     // The last "." is a triggering key for auto conversion
6521     InsertCharacterString("てすと。", "wrs/", session.get(), &command);
6522 
6523     EXPECT_SINGLE_SEGMENT_AND_KEY("あいうえお", "あいうえお", command);
6524   }
6525 
6526   // Don't trigger auto conversion for the pattern number + "."
6527   {
6528     std::unique_ptr<Session> session(new Session(engine_.get()));
6529     session->SetConfig(&config);
6530     InitSessionToPrecomposition(session.get());
6531     commands::Command command;
6532 
6533     // The last "." is a triggering key for auto conversion
6534     InsertCharacterChars("123.", session.get(), &command);
6535 
6536     EXPECT_SINGLE_SEGMENT_AND_KEY("123.", "123.", command);
6537   }
6538 
6539   // Don't trigger auto conversion for the ".."
6540   {
6541     std::unique_ptr<Session> session(new Session(engine_.get()));
6542     session->SetConfig(&config);
6543     InitSessionToPrecomposition(session.get());
6544     commands::Command command;
6545 
6546     // The last "." is a triggering key for auto conversion
6547     InsertCharacterChars("..", session.get(), &command);
6548 
6549     EXPECT_SINGLE_SEGMENT_AND_KEY("。。", "。。", command);
6550   }
6551 
6552   {
6553     std::unique_ptr<Session> session(new Session(engine_.get()));
6554     session->SetConfig(&config);
6555     InitSessionToPrecomposition(session.get());
6556     commands::Command command;
6557 
6558     // The last "." is a triggering key for auto conversion
6559     InsertCharacterString("123。", "123.", session.get(), &command);
6560 
6561     EXPECT_SINGLE_SEGMENT_AND_KEY("123.", "123.", command);
6562   }
6563 
6564   // Don't trigger auto conversion for "." only.
6565   {
6566     std::unique_ptr<Session> session(new Session(engine_.get()));
6567     session->SetConfig(&config);
6568     InitSessionToPrecomposition(session.get());
6569     commands::Command command;
6570 
6571     // The last "." is a triggering key for auto conversion
6572     InsertCharacterChars(".", session.get(), &command);
6573 
6574     EXPECT_SINGLE_SEGMENT_AND_KEY("。", "。", command);
6575   }
6576 
6577   {
6578     std::unique_ptr<Session> session(new Session(engine_.get()));
6579     session->SetConfig(&config);
6580     InitSessionToPrecomposition(session.get());
6581     commands::Command command;
6582 
6583     // The last "." is a triggering key for auto conversion
6584     InsertCharacterString("。", "/", session.get(), &command);
6585 
6586     EXPECT_SINGLE_SEGMENT_AND_KEY("。", "。", command);
6587   }
6588 
6589   // Do auto conversion even if romanji-table is modified.
6590   {
6591     std::unique_ptr<Session> session(new Session(engine_.get()));
6592     session->SetConfig(&config);
6593     InitSessionToPrecomposition(session.get());
6594 
6595     // Modify romanji-table to convert "zz" -> "。"
6596     composer::Table zz_table;
6597     zz_table.AddRule("te", "て", "");
6598     zz_table.AddRule("su", "す", "");
6599     zz_table.AddRule("to", "と", "");
6600     zz_table.AddRule("zz", "。", "");
6601     session->get_internal_composer_only_for_unittest()->SetTable(&zz_table);
6602 
6603     // The last "zz" is converted to "." and triggering key for auto conversion
6604     commands::Command command;
6605     InsertCharacterChars("tesutozz", session.get(), &command);
6606 
6607     EXPECT_SINGLE_SEGMENT_AND_KEY("あいうえお", "あいうえお", command);
6608   }
6609 
6610   {
6611     const char trigger_key[] = ".,?!";
6612 
6613     // try all possible patterns.
6614     for (int kana_mode = 0; kana_mode < 2; ++kana_mode) {
6615       for (int onoff = 0; onoff < 2; ++onoff) {
6616         for (int pattern = 0; pattern <= 16; ++pattern) {
6617           config.set_use_auto_conversion(onoff != 0);
6618           config.set_auto_conversion_key(pattern);
6619 
6620           int flag[4];
6621           flag[0] = static_cast<int>(
6622               config.auto_conversion_key() &
6623               config::Config::AUTO_CONVERSION_KUTEN);
6624           flag[1] = static_cast<int>(
6625               config.auto_conversion_key() &
6626               config::Config::AUTO_CONVERSION_TOUTEN);
6627           flag[2] = static_cast<int>(
6628               config.auto_conversion_key() &
6629               config::Config::AUTO_CONVERSION_QUESTION_MARK);
6630           flag[3] = static_cast<int>(
6631               config.auto_conversion_key() &
6632               config::Config::AUTO_CONVERSION_EXCLAMATION_MARK);
6633 
6634           for (int i = 0; i < 4; ++i) {
6635             std::unique_ptr<Session> session(new Session(engine_.get()));
6636             session->SetConfig(&config);
6637             InitSessionToPrecomposition(session.get());
6638             commands::Command command;
6639 
6640             if (kana_mode) {
6641               string key = "てすと";
6642               key += trigger_key[i];
6643               InsertCharacterString(key, "wst/", session.get(), &command);
6644             } else {
6645               string key = "tesuto";
6646               key += trigger_key[i];
6647               InsertCharacterChars(key, session.get(), &command);
6648             }
6649             EXPECT_TRUE(command.output().has_preedit());
6650             EXPECT_EQ(1, command.output().preedit().segment_size());
6651             EXPECT_TRUE(command.output().preedit().segment(0).has_value());
6652             EXPECT_TRUE(command.output().preedit().segment(0).has_key());
6653 
6654             if (onoff > 0 && flag[i] > 0) {
6655               EXPECT_EQ("あいうえお",
6656                         command.output().preedit().segment(0).key());
6657             } else {
6658               // Not "あいうえお"
6659               EXPECT_NE("あいうえお",
6660                         command.output().preedit().segment(0).key());
6661             }
6662           }
6663         }
6664       }
6665     }
6666   }
6667 }
6668 
TEST_F(SessionTest,InputSpaceWithKatakanaMode)6669 TEST_F(SessionTest, InputSpaceWithKatakanaMode) {
6670   // This is a unittest against http://b/3203944.
6671   // Input mode should not be changed when a space key is typed.
6672   std::unique_ptr<Session> session(new Session(engine_.get()));
6673   InitSessionToPrecomposition(session.get());
6674 
6675   commands::Command command;
6676   EXPECT_TRUE(session->InputModeHiragana(&command));
6677   EXPECT_TRUE(command.output().consumed());
6678   EXPECT_EQ(mozc::commands::HIRAGANA, command.output().mode());
6679 
6680   SetSendKeyCommand("Space", &command);
6681   command.mutable_input()->mutable_key()->set_mode(commands::FULL_KATAKANA);
6682   EXPECT_TRUE(session->SendKey(&command));
6683   EXPECT_TRUE(command.output().consumed());
6684   EXPECT_RESULT(" ", command);
6685   EXPECT_EQ(mozc::commands::FULL_KATAKANA, command.output().mode());
6686 }
6687 
TEST_F(SessionTest,AlphanumericOfSSH)6688 TEST_F(SessionTest, AlphanumericOfSSH) {
6689   // This is a unittest against http://b/3199626
6690   // 'ssh' (っsh) + F10 should be 'ssh'.
6691   std::unique_ptr<Session> session(new Session(engine_.get()));
6692   InitSessionToPrecomposition(session.get());
6693 
6694   commands::Command command;
6695   InsertCharacterChars("ssh", session.get(), &command);
6696   EXPECT_EQ("っsh", GetComposition(command));
6697 
6698   Segments segments;
6699   // Set a dummy segments for ConvertToHalfASCII.
6700   {
6701     Segment *segment;
6702     segment = segments.add_segment();
6703     segment->set_key("っsh");
6704 
6705     segment->add_candidate()->value = "[SSH]";
6706   }
6707   ConversionRequest request;
6708   SetComposer(session.get(), &request);
6709   FillT13Ns(request, &segments);
6710   GetConverterMock()->SetStartConversionForRequest(&segments, true);
6711 
6712   command.Clear();
6713   EXPECT_TRUE(session->ConvertToHalfASCII(&command));
6714   EXPECT_SINGLE_SEGMENT("ssh", command);
6715 }
6716 
TEST_F(SessionTest,KeitaiInput_toggle)6717 TEST_F(SessionTest, KeitaiInput_toggle) {
6718   config::Config config;
6719   config.set_session_keymap(config::Config::MSIME);
6720   std::unique_ptr<Session> session(new Session(engine_.get()));
6721   session->SetConfig(&config);
6722 
6723   InitSessionToPrecomposition(session.get(), *mobile_request_);
6724   commands::Command command;
6725 
6726   SendKey("1", session.get(), &command);
6727   // "あ|"
6728   EXPECT_EQ("あ", command.output().preedit().segment(0).value());
6729   EXPECT_EQ(1, command.output().preedit().cursor());
6730 
6731   SendKey("1", session.get(), &command);
6732   // "い|"
6733   EXPECT_EQ("い", command.output().preedit().segment(0).value());
6734   EXPECT_EQ(1, command.output().preedit().cursor());
6735 
6736   SendKey("1", session.get(), &command);
6737   SendKey("1", session.get(), &command);
6738   SendKey("1", session.get(), &command);
6739   SendKey("1", session.get(), &command);
6740   SendKey("1", session.get(), &command);
6741   SendKey("1", session.get(), &command);
6742   SendKey("1", session.get(), &command);
6743   SendKey("1", session.get(), &command);
6744   SendKey("1", session.get(), &command);
6745   EXPECT_EQ("あ", command.output().preedit().segment(0).value());
6746   EXPECT_EQ(1, command.output().preedit().cursor());
6747 
6748   SendKey("2", session.get(), &command);
6749   EXPECT_EQ("あか",
6750             command.output().preedit().segment(0).value());
6751   EXPECT_EQ(2, command.output().preedit().cursor());
6752 
6753   SendKey("2", session.get(), &command);
6754   EXPECT_EQ("あき",
6755             command.output().preedit().segment(0).value());
6756   EXPECT_EQ(2, command.output().preedit().cursor());
6757 
6758   SendKey("*", session.get(), &command);
6759   EXPECT_EQ("あぎ",
6760             command.output().preedit().segment(0).value());
6761   EXPECT_EQ(2, command.output().preedit().cursor());
6762 
6763   SendKey("*", session.get(), &command);
6764   EXPECT_EQ("あき",
6765             command.output().preedit().segment(0).value());
6766   EXPECT_EQ(2, command.output().preedit().cursor());
6767 
6768   SendKey("3", session.get(), &command);
6769   EXPECT_EQ("あきさ",
6770             command.output().preedit().segment(0).value());
6771   EXPECT_EQ(3, command.output().preedit().cursor());
6772 
6773   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6774   EXPECT_EQ("あきさ",
6775             command.output().preedit().segment(0).value());
6776   EXPECT_EQ(3, command.output().preedit().cursor());
6777 
6778   SendKey("3", session.get(), &command);
6779   EXPECT_EQ("あきささ",
6780             command.output().preedit().segment(0).value());
6781   EXPECT_EQ(4, command.output().preedit().cursor());
6782 
6783   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6784   EXPECT_EQ("あきささ",
6785             command.output().preedit().segment(0).value());
6786   EXPECT_EQ(3, command.output().preedit().cursor());
6787 
6788   SendKey("4", session.get(), &command);
6789   EXPECT_EQ("あきさたさ",
6790             command.output().preedit().segment(0).value());
6791   EXPECT_EQ(4, command.output().preedit().cursor());
6792 
6793   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6794   EXPECT_EQ("あきさたさ",
6795             command.output().preedit().segment(0).value());
6796   EXPECT_EQ(3, command.output().preedit().cursor());
6797 
6798   SendKey("*", session.get(), &command);
6799   EXPECT_EQ("あきざたさ",
6800             command.output().preedit().segment(0).value());
6801   EXPECT_EQ(3, command.output().preedit().cursor());
6802 
6803   // Test for End key
6804   SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6805   SendKey("6", session.get(), &command);
6806   SendKey("6", session.get(), &command);
6807   SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6808   SendKey("6", session.get(), &command);
6809   SendKey("*", session.get(), &command);
6810   EXPECT_EQ("あきざたさひば",
6811       command.output().preedit().segment(0).value());
6812   EXPECT_EQ(7, command.output().preedit().cursor());
6813 
6814   // Test for Right key
6815   SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6816   SendKey("6", session.get(), &command);
6817   SendKey("6", session.get(), &command);
6818   SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6819   SendKey("6", session.get(), &command);
6820   SendKey("*", session.get(), &command);
6821   EXPECT_EQ("あきざたさひばひば",
6822       command.output().preedit().segment(0).value());
6823   EXPECT_EQ(9, command.output().preedit().cursor());
6824 
6825   // Test for Left key
6826   SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6827   SendKey("6", session.get(), &command);
6828   SendKey("6", session.get(), &command);
6829   EXPECT_EQ("あきざたさひばひばひ",
6830       command.output().preedit().segment(0).value());
6831   SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6832   SendKey("6", session.get(), &command);
6833   EXPECT_EQ("あきざたさひばひばはひ",
6834       command.output().preedit().segment(0).value());
6835   SendKey("*", session.get(), &command);
6836   EXPECT_EQ("あきざたさひばひばばひ",
6837       command.output().preedit().segment(0).value());
6838   EXPECT_EQ(10, command.output().preedit().cursor());
6839 
6840   // Test for Home key
6841   SendSpecialKey(commands::KeyEvent::HOME, session.get(), &command);
6842   EXPECT_EQ("あきざたさひばひばばひ",
6843       command.output().preedit().segment(0).value());
6844   SendKey("6", session.get(), &command);
6845   SendKey("*", session.get(), &command);
6846   EXPECT_EQ("ばあきざたさひばひばばひ",
6847       command.output().preedit().segment(0).value());
6848   EXPECT_EQ(1, command.output().preedit().cursor());
6849 
6850   SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6851   SendKey("5", session.get(), &command);
6852   EXPECT_EQ("ばあきざたさひばひばばひな",
6853       command.output().preedit().segment(0).value());
6854   SendKey("*", session.get(), &command);  // no effect
6855   EXPECT_EQ("ばあきざたさひばひばばひな",
6856       command.output().preedit().segment(0).value());
6857   EXPECT_EQ(13, command.output().preedit().cursor());
6858 }
6859 
TEST_F(SessionTest,KeitaiInput_flick)6860 TEST_F(SessionTest, KeitaiInput_flick) {
6861   config::Config config;
6862   config.set_session_keymap(config::Config::MSIME);
6863   commands::Command command;
6864 
6865   {
6866     std::unique_ptr<Session> session(new Session(engine_.get()));
6867     session->SetConfig(&config);
6868     InitSessionToPrecomposition(session.get(), *mobile_request_);
6869     InsertCharacterCodeAndString('6', "は", session.get(), &command);
6870     InsertCharacterCodeAndString('3', "し", session.get(), &command);
6871     SendKey("*", session.get(), &command);
6872     InsertCharacterCodeAndString('3', "ょ", session.get(), &command);
6873     InsertCharacterCodeAndString('1', "う", session.get(), &command);
6874     EXPECT_EQ("はじょう",
6875         command.output().preedit().segment(0).value());
6876   }
6877 
6878   {
6879     std::unique_ptr<Session> session(new Session(engine_.get()));
6880     session->SetConfig(&config);
6881     InitSessionToPrecomposition(session.get(), *mobile_request_);
6882 
6883     SendKey("6", session.get(), &command);
6884     SendKey("3", session.get(), &command);
6885     SendKey("3", session.get(), &command);
6886     SendKey("*", session.get(), &command);
6887     InsertCharacterCodeAndString('3', "ょ", session.get(), &command);
6888     InsertCharacterCodeAndString('1', "う", session.get(), &command);
6889     EXPECT_EQ("はじょう", command.output().preedit().segment(0).value());
6890   }
6891 
6892   {
6893     std::unique_ptr<Session> session(new Session(engine_.get()));
6894     session->SetConfig(&config);
6895     InitSessionToPrecomposition(session.get(), *mobile_request_);
6896 
6897     SendKey("1", session.get(), &command);
6898     SendKey("2", session.get(), &command);
6899     SendKey("3", session.get(), &command);
6900     SendKey("3", session.get(), &command);
6901     EXPECT_EQ("あかし", command.output().preedit().segment(0).value());
6902     InsertCharacterCodeAndString('5', "の", session.get(), &command);
6903     InsertCharacterCodeAndString('2', "く", session.get(), &command);
6904     InsertCharacterCodeAndString('3', "し", session.get(), &command);
6905     EXPECT_EQ("あかしのくし", command.output().preedit().segment(0).value());
6906     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6907     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6908     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6909     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6910     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6911     SendKey("9", session.get(), &command);
6912     SendKey("9", session.get(), &command);
6913     SendKey("9", session.get(), &command);
6914     SendKey("9", session.get(), &command);
6915     SendKey("9", session.get(), &command);
6916     SendKey("9", session.get(), &command);
6917     SendKey("9", session.get(), &command);
6918     SendKey("9", session.get(), &command);
6919     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6920     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6921     InsertCharacterCodeAndString('0', "ん", session.get(), &command);
6922     SendSpecialKey(commands::KeyEvent::END, session.get(), &command);
6923     SendKey("1", session.get(), &command);
6924     SendKey("1", session.get(), &command);
6925     SendKey("1", session.get(), &command);
6926     SendKey("*", session.get(), &command);
6927     SendSpecialKey(commands::KeyEvent::LEFT, session.get(), &command);
6928     InsertCharacterCodeAndString('8', "ゆ", session.get(), &command);
6929     SendKey("*", session.get(), &command);
6930     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6931     SendKey("*", session.get(), &command);
6932     SendKey("*", session.get(), &command);
6933     EXPECT_EQ("あるかしんのくしゅう",
6934               command.output().preedit().segment(0).value());
6935     SendSpecialKey(commands::KeyEvent::HOME, session.get(), &command);
6936     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6937     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6938     InsertCharacterCodeAndString('6', "は", session.get(), &command);
6939     SendKey("*", session.get(), &command);
6940     SendKey("*", session.get(), &command);
6941     SendKey("*", session.get(), &command);
6942     SendKey("*", session.get(), &command);
6943     SendKey("*", session.get(), &command);
6944     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6945     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6946     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6947     SendSpecialKey(commands::KeyEvent::RIGHT, session.get(), &command);
6948     SendKey("6", session.get(), &command);
6949     SendKey("6", session.get(), &command);
6950     SendKey("6", session.get(), &command);
6951     EXPECT_EQ("あるぱかしんのふくしゅう",
6952               command.output().preedit().segment(0).value());
6953   }
6954 }
6955 
TEST_F(SessionTest,CommitCandidateAt2ndOf3Segments)6956 TEST_F(SessionTest, CommitCandidateAt2ndOf3Segments) {
6957   std::unique_ptr<Session> session(new Session(engine_.get()));
6958   InitSessionToPrecomposition(session.get());
6959 
6960   ConversionRequest request;
6961   SetComposer(session.get(), &request);
6962 
6963   commands::Command command;
6964   InsertCharacterChars("nekonoshippowonuita", session.get(), &command);
6965 
6966   {  // Segments as conversion result.
6967     Segments segments;
6968     Segment *segment;
6969     Segment::Candidate *candidate;
6970 
6971     segment = segments.add_segment();
6972     segment->set_key("ねこの");
6973     candidate = segment->add_candidate();
6974     candidate->value = "猫の";
6975 
6976     segment = segments.add_segment();
6977     segment->set_key("しっぽを");
6978     candidate = segment->add_candidate();
6979     candidate->value = "しっぽを";
6980 
6981     segment = segments.add_segment();
6982     segment->set_key("ぬいた");
6983     candidate = segment->add_candidate();
6984     candidate->value = "抜いた";
6985 
6986     GetConverterMock()->SetStartConversionForRequest(&segments, true);
6987   }
6988 
6989   command.Clear();
6990   session->Convert(&command);
6991   // "[猫の]|しっぽを|抜いた"
6992 
6993   command.Clear();
6994   session->SegmentFocusRight(&command);
6995   // "猫の|[しっぽを]|抜いた"
6996 
6997   {  // Segments as result of CommitHeadToFocusedSegments
6998     Segments segments;
6999     Segment *segment;
7000     Segment::Candidate *candidate;
7001 
7002     segment = segments.add_segment();
7003     segment->set_key("ぬいた");
7004     candidate = segment->add_candidate();
7005     candidate->value = "抜いた";
7006 
7007     GetConverterMock()->SetCommitSegments(&segments, true);
7008   }
7009 
7010   command.Clear();
7011   command.mutable_input()->mutable_command()->set_id(0);
7012   ASSERT_TRUE(session->CommitCandidate(&command));
7013   EXPECT_PREEDIT("抜いた", command);
7014   EXPECT_SINGLE_SEGMENT_AND_KEY("抜いた", "ぬいた", command);
7015   EXPECT_RESULT("猫のしっぽを", command);
7016 }
7017 
TEST_F(SessionTest,CommitCandidateAt3rdOf3Segments)7018 TEST_F(SessionTest, CommitCandidateAt3rdOf3Segments) {
7019   std::unique_ptr<Session> session(new Session(engine_.get()));
7020   InitSessionToPrecomposition(session.get());
7021 
7022   ConversionRequest request;
7023   SetComposer(session.get(), &request);
7024 
7025   commands::Command command;
7026   InsertCharacterChars("nekonoshippowonuita", session.get(), &command);
7027 
7028   {  // Segments as conversion result.
7029     Segments segments;
7030     Segment *segment;
7031     Segment::Candidate *candidate;
7032 
7033     segment = segments.add_segment();
7034     segment->set_key("ねこの");
7035     candidate = segment->add_candidate();
7036     candidate->value = "猫の";
7037 
7038     segment = segments.add_segment();
7039     segment->set_key("しっぽを");
7040     candidate = segment->add_candidate();
7041     candidate->value = "しっぽを";
7042 
7043     segment = segments.add_segment();
7044     segment->set_key("ぬいた");
7045     candidate = segment->add_candidate();
7046     candidate->value = "抜いた";
7047 
7048     GetConverterMock()->SetStartConversionForRequest(&segments, true);
7049   }
7050 
7051   command.Clear();
7052   session->Convert(&command);
7053   // "[猫の]|しっぽを|抜いた"
7054 
7055   command.Clear();
7056   session->SegmentFocusRight(&command);
7057   session->SegmentFocusRight(&command);
7058   // "猫の|しっぽを|[抜いた]"
7059 
7060   {  // Segments as result of CommitHeadToFocusedSegments
7061     Segments segments;
7062     GetConverterMock()->SetCommitSegments(&segments, true);
7063   }
7064 
7065   command.Clear();
7066   command.mutable_input()->mutable_command()->set_id(0);
7067   ASSERT_TRUE(session->CommitCandidate(&command));
7068   EXPECT_FALSE(command.output().has_preedit());
7069   EXPECT_RESULT("猫のしっぽを抜いた" , command);
7070 }
7071 
TEST_F(SessionTest,CommitCandidate_suggestion)7072 TEST_F(SessionTest, CommitCandidate_suggestion) {
7073   std::unique_ptr<Session> session(new Session(engine_.get()));
7074   InitSessionToPrecomposition(session.get(), *mobile_request_);
7075 
7076   Segments segments_mo;
7077   {
7078     segments_mo.set_request_type(Segments::SUGGESTION);
7079     Segment *segment;
7080     segment = segments_mo.add_segment();
7081     segment->set_key("MO");
7082     AddCandidate("MOCHA", "MOCHA", segment);
7083     AddCandidate("MOZUKU", "MOZUKU", segment);
7084   }
7085 
7086   commands::Command command;
7087   SendKey("M", session.get(), &command);
7088   command.Clear();
7089   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
7090   SendKey("O", session.get(), &command);
7091   ASSERT_TRUE(command.output().has_candidates());
7092   EXPECT_EQ(2, command.output().candidates().candidate_size());
7093   EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
7094 
7095   GetConverterMock()->SetFinishConversion(
7096       std::unique_ptr<Segments>(new Segments).get(), true);
7097   SetSendCommandCommand(commands::SessionCommand::SUBMIT_CANDIDATE, &command);
7098   command.mutable_input()->mutable_command()->set_id(1);
7099   session->SendCommand(&command);
7100   EXPECT_TRUE(command.output().consumed());
7101   EXPECT_RESULT_AND_KEY("MOZUKU", "MOZUKU", command);
7102   EXPECT_FALSE(command.output().has_preedit());
7103   // Zero query suggestion fills the candidates.
7104   EXPECT_TRUE(command.output().has_candidates());
7105   EXPECT_EQ(0, command.output().preedit().cursor());
7106 }
7107 
FindCandidateID(const commands::Candidates & candidates,const string & value,int * id)7108 bool FindCandidateID(const commands::Candidates &candidates,
7109                       const string &value, int *id) {
7110   CHECK(id);
7111   for (size_t i = 0; i < candidates.candidate_size(); ++i) {
7112     const commands::Candidates::Candidate &candidate =
7113         candidates.candidate(i);
7114     if (candidate.value() == value) {
7115       *id = candidate.id();
7116       return true;
7117     }
7118   }
7119   return false;
7120 }
7121 
FindCandidateIDs(const commands::Candidates & candidates,const string & value,std::vector<int> * ids)7122 void FindCandidateIDs(const commands::Candidates &candidates,
7123                       const string &value, std::vector<int> *ids) {
7124   CHECK(ids);
7125   ids->clear();
7126   for (size_t i = 0; i < candidates.candidate_size(); ++i) {
7127     const commands::Candidates::Candidate &candidate =
7128         candidates.candidate(i);
7129     LOG(INFO) <<  candidate.value();
7130     if (candidate.value() == value) {
7131       ids->push_back(candidate.id());
7132     }
7133   }
7134 }
7135 
TEST_F(SessionTest,CommitCandidate_T13N)7136 TEST_F(SessionTest, CommitCandidate_T13N) {
7137   std::unique_ptr<Session> session(new Session(engine_.get()));
7138   InitSessionToPrecomposition(session.get(), *mobile_request_);
7139 
7140   {
7141     Segments segments;
7142     segments.set_request_type(Segments::SUGGESTION);
7143 
7144     Segment *segment;
7145     segment = segments.add_segment();
7146     segment->set_key("tok");
7147     AddCandidate("tok", "tok", segment);
7148     AddMetaCandidate("tok", "tok", segment);
7149     AddMetaCandidate("tok", "TOK", segment);
7150     AddMetaCandidate("tok", "Tok", segment);
7151     EXPECT_EQ("tok", segment->candidate(-1).value);
7152     EXPECT_EQ("TOK", segment->candidate(-2).value);
7153     EXPECT_EQ("Tok", segment->candidate(-3).value);
7154 
7155     GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
7156   }
7157 
7158   {
7159     Segments segments;
7160     segments.set_request_type(Segments::PREDICTION);
7161 
7162     Segment *segment;
7163     segment = segments.add_segment();
7164     segment->set_key("tok");
7165     AddCandidate("tok", "tok", segment);
7166     AddMetaCandidate("tok", "tok", segment);
7167     AddMetaCandidate("tok", "TOK", segment);
7168     AddMetaCandidate("tok", "Tok", segment);
7169     EXPECT_EQ("tok", segment->candidate(-1).value);
7170     EXPECT_EQ("TOK", segment->candidate(-2).value);
7171     EXPECT_EQ("Tok", segment->candidate(-3).value);
7172     GetConverterMock()->SetStartPredictionForRequest(&segments, true);
7173   }
7174 
7175   commands::Command command;
7176   SendKey("k", session.get(), &command);
7177   ASSERT_TRUE(command.output().has_candidates());
7178   int id = 0;
7179 #if defined(OS_WIN) || defined(OS_MACOSX)
7180   // meta candidates are in cascading window
7181   EXPECT_FALSE(FindCandidateID(command.output().candidates(), "TOK", &id));
7182 #else
7183   EXPECT_TRUE(FindCandidateID(command.output().candidates(), "TOK", &id));
7184   GetConverterMock()->SetFinishConversion(
7185       std::unique_ptr<Segments>(new Segments).get(), true);
7186   SetSendCommandCommand(commands::SessionCommand::SUBMIT_CANDIDATE, &command);
7187   command.mutable_input()->mutable_command()->set_id(id);
7188   session->SendCommand(&command);
7189   EXPECT_TRUE(command.output().consumed());
7190   EXPECT_RESULT("TOK", command);
7191   EXPECT_FALSE(command.output().has_preedit());
7192   EXPECT_EQ(0, command.output().preedit().cursor());
7193 #endif
7194 }
7195 
TEST_F(SessionTest,RequestConvertReverse)7196 TEST_F(SessionTest, RequestConvertReverse) {
7197   std::unique_ptr<Session> session(new Session(engine_.get()));
7198   InitSessionToPrecomposition(session.get());
7199 
7200   commands::Command command;
7201   EXPECT_TRUE(session->RequestConvertReverse(&command));
7202   EXPECT_FALSE(command.output().has_result());
7203   EXPECT_FALSE(command.output().has_deletion_range());
7204   EXPECT_TRUE(command.output().has_callback());
7205   EXPECT_TRUE(command.output().callback().has_session_command());
7206   EXPECT_EQ(commands::SessionCommand::CONVERT_REVERSE,
7207             command.output().callback().session_command().type());
7208 }
7209 
TEST_F(SessionTest,ConvertReverse)7210 TEST_F(SessionTest, ConvertReverse) {
7211   std::unique_ptr<Session> session(new Session(engine_.get()));
7212   InitSessionToPrecomposition(session.get());
7213   const char kKanjiAiueo[] = "阿伊宇江於";
7214   commands::Command command;
7215   SetupCommandForReverseConversion(kKanjiAiueo, command.mutable_input());
7216   SetupMockForReverseConversion(kKanjiAiueo, "あいうえお");
7217 
7218   EXPECT_TRUE(session->SendCommand(&command));
7219   EXPECT_TRUE(command.output().consumed());
7220   EXPECT_EQ(kKanjiAiueo,
7221             command.output().preedit().segment(0).value());
7222   EXPECT_EQ(kKanjiAiueo,
7223             command.output().all_candidate_words().candidates(0).value());
7224   EXPECT_TRUE(command.output().has_candidates());
7225   EXPECT_GT(command.output().candidates().candidate_size(), 0);
7226 }
7227 
TEST_F(SessionTest,EscapeFromConvertReverse)7228 TEST_F(SessionTest, EscapeFromConvertReverse) {
7229   std::unique_ptr<Session> session(new Session(engine_.get()));
7230   InitSessionToPrecomposition(session.get());
7231   const char kKanjiAiueo[] = "阿伊宇江於";
7232 
7233   commands::Command command;
7234   SetupCommandForReverseConversion(kKanjiAiueo, command.mutable_input());
7235   SetupMockForReverseConversion(kKanjiAiueo, "あいうえお");
7236 
7237   EXPECT_TRUE(session->SendCommand(&command));
7238   EXPECT_TRUE(command.output().consumed());
7239   EXPECT_EQ(kKanjiAiueo, GetComposition(command));
7240 
7241   SendKey("ESC", session.get(), &command);
7242 
7243   // KANJI should be converted into HIRAGANA in pre-edit state.
7244   EXPECT_SINGLE_SEGMENT("あいうえお", command);
7245 
7246   SendKey("ESC", session.get(), &command);
7247 
7248   // Fixed KANJI should be output
7249   EXPECT_FALSE(command.output().has_preedit());
7250   EXPECT_RESULT(kKanjiAiueo, command);
7251 }
7252 
TEST_F(SessionTest,SecondEscapeFromConvertReverse)7253 TEST_F(SessionTest, SecondEscapeFromConvertReverse) {
7254   std::unique_ptr<Session> session(new Session(engine_.get()));
7255   InitSessionToPrecomposition(session.get());
7256   const char kKanjiAiueo[] = "阿伊宇江於";
7257   commands::Command command;
7258   SetupCommandForReverseConversion(kKanjiAiueo, command.mutable_input());
7259   SetupMockForReverseConversion(kKanjiAiueo, "あいうえお");
7260 
7261   EXPECT_TRUE(session->SendCommand(&command));
7262   EXPECT_TRUE(command.output().consumed());
7263   EXPECT_EQ(kKanjiAiueo, GetComposition(command));
7264 
7265   SendKey("ESC", session.get(), &command);
7266   SendKey("ESC", session.get(), &command);
7267 
7268   EXPECT_FALSE(command.output().has_preedit());
7269   // When a reverse conversion is canceled, the converter sets the
7270   // original text into |command.output().result().key()|.
7271   EXPECT_RESULT_AND_KEY(kKanjiAiueo, kKanjiAiueo, command);
7272 
7273   SendKey("a", session.get(), &command);
7274   EXPECT_EQ("あ", GetComposition(command));
7275 
7276   SendKey("ESC", session.get(), &command);
7277   EXPECT_FALSE(command.output().has_preedit());
7278   EXPECT_FALSE(command.output().has_result());
7279 }
7280 
TEST_F(SessionTest,SecondEscapeFromConvertReverse_Issue5687022)7281 TEST_F(SessionTest, SecondEscapeFromConvertReverse_Issue5687022) {
7282   // This is a unittest against http://b/5687022
7283   std::unique_ptr<Session> session(new Session(engine_.get()));
7284   InitSessionToPrecomposition(session.get());
7285   const char kInput[] = "abcde";
7286   const char kReading[] = "abcde";
7287 
7288   commands::Command command;
7289   SetupCommandForReverseConversion(kInput, command.mutable_input());
7290   SetupMockForReverseConversion(kInput, kReading);
7291 
7292   EXPECT_TRUE(session->SendCommand(&command));
7293   EXPECT_TRUE(command.output().consumed());
7294   EXPECT_EQ(kInput, GetComposition(command));
7295 
7296   SendKey("ESC", session.get(), &command);
7297   SendKey("ESC", session.get(), &command);
7298 
7299   EXPECT_FALSE(command.output().has_preedit());
7300   // When a reverse conversion is canceled, the converter sets the
7301   // original text into |result().key()|.
7302   EXPECT_RESULT_AND_KEY(kInput, kInput, command);
7303 }
7304 
TEST_F(SessionTest,SecondEscapeFromConvertReverseKeepsOriginalText)7305 TEST_F(SessionTest, SecondEscapeFromConvertReverseKeepsOriginalText) {
7306   // Second escape from ConvertReverse should restore the original text
7307   // without any text normalization even if the input text contains any
7308   // special characters which Mozc usually do normalization.
7309 
7310   std::unique_ptr<Session> session(new Session(engine_.get()));
7311   InitSessionToPrecomposition(session.get());
7312   const char kInput[] = "ゔ";
7313 
7314   commands::Command command;
7315   SetupCommandForReverseConversion(kInput, command.mutable_input());
7316   SetupMockForReverseConversion(kInput, kInput);
7317 
7318   EXPECT_TRUE(session->SendCommand(&command));
7319   EXPECT_TRUE(command.output().consumed());
7320   EXPECT_EQ(kInput, GetComposition(command));
7321 
7322   SendKey("ESC", session.get(), &command);
7323   SendKey("ESC", session.get(), &command);
7324 
7325   EXPECT_FALSE(command.output().has_preedit());
7326 
7327   // When a reverse conversion is canceled, the converter sets the
7328   // original text into |result().key()|.
7329   EXPECT_RESULT_AND_KEY(kInput, kInput, command);
7330 }
7331 
TEST_F(SessionTest,EscapeFromCompositionAfterConvertReverse)7332 TEST_F(SessionTest, EscapeFromCompositionAfterConvertReverse) {
7333   std::unique_ptr<Session> session(new Session(engine_.get()));
7334   InitSessionToPrecomposition(session.get());
7335   const char kKanjiAiueo[] = "阿伊宇江於";
7336 
7337   commands::Command command;
7338   SetupCommandForReverseConversion(kKanjiAiueo, command.mutable_input());
7339   SetupMockForReverseConversion(kKanjiAiueo, "あいうえお");
7340 
7341   // Conversion Reverse
7342   EXPECT_TRUE(session->SendCommand(&command));
7343   EXPECT_TRUE(command.output().consumed());
7344   EXPECT_EQ(kKanjiAiueo, GetComposition(command));
7345 
7346   session->Commit(&command);
7347 
7348   EXPECT_RESULT(kKanjiAiueo, command);
7349 
7350   // Escape in composition state
7351   SendKey("a", session.get(), &command);
7352   EXPECT_EQ("あ", GetComposition(command));
7353 
7354   SendKey("ESC", session.get(), &command);
7355   EXPECT_FALSE(command.output().has_preedit());
7356   EXPECT_FALSE(command.output().has_result());
7357 }
7358 
TEST_F(SessionTest,ConvertReverseFromOffState)7359 TEST_F(SessionTest, ConvertReverseFromOffState) {
7360   std::unique_ptr<Session> session(new Session(engine_.get()));
7361   InitSessionToPrecomposition(session.get());
7362   const string kanji_aiueo = "阿伊宇江於";
7363 
7364   // IMEOff
7365   commands::Command command;
7366   SendSpecialKey(commands::KeyEvent::OFF, session.get(), &command);
7367 
7368   SetupCommandForReverseConversion(kanji_aiueo, command.mutable_input());
7369   SetupMockForReverseConversion(kanji_aiueo, "あいうえお");
7370   EXPECT_TRUE(session->SendCommand(&command));
7371   EXPECT_TRUE(command.output().consumed());
7372 }
7373 
TEST_F(SessionTest,DCHECKFailureAfterConvertReverse)7374 TEST_F(SessionTest, DCHECKFailureAfterConvertReverse) {
7375   // This is a unittest against http://b/5145295.
7376   std::unique_ptr<Session> session(new Session(engine_.get()));
7377   InitSessionToPrecomposition(session.get());
7378 
7379   commands::Command command;
7380   SetupCommandForReverseConversion("あいうえお", command.mutable_input());
7381   SetupMockForReverseConversion("あいうえお", "あいうえお");
7382   EXPECT_TRUE(session->SendCommand(&command));
7383   EXPECT_TRUE(command.output().consumed());
7384   EXPECT_EQ("あいうえお", command.output().preedit().segment(0).value());
7385   EXPECT_EQ("あいうえお",
7386       command.output().all_candidate_words().candidates(0).value());
7387   EXPECT_TRUE(command.output().has_candidates());
7388   EXPECT_GT(command.output().candidates().candidate_size(), 0);
7389 
7390   SendKey("ESC", session.get(), &command);
7391   SendKey("a", session.get(), &command);
7392   EXPECT_EQ("あいうえおあ", command.output().preedit().segment(0).value());
7393   EXPECT_FALSE(command.output().has_result());
7394 }
7395 
TEST_F(SessionTest,LaunchTool)7396 TEST_F(SessionTest, LaunchTool) {
7397   std::unique_ptr<Session> session(new Session(engine_.get()));
7398 
7399   {
7400     commands::Command command;
7401     EXPECT_TRUE(session->LaunchConfigDialog(&command));
7402     EXPECT_EQ(commands::Output::CONFIG_DIALOG,
7403               command.output().launch_tool_mode());
7404     EXPECT_TRUE(command.output().consumed());
7405   }
7406 
7407   {
7408     commands::Command command;
7409     EXPECT_TRUE(session->LaunchDictionaryTool(&command));
7410     EXPECT_EQ(commands::Output::DICTIONARY_TOOL,
7411               command.output().launch_tool_mode());
7412     EXPECT_TRUE(command.output().consumed());
7413   }
7414 
7415   {
7416     commands::Command command;
7417     EXPECT_TRUE(session->LaunchWordRegisterDialog(&command));
7418     EXPECT_EQ(commands::Output::WORD_REGISTER_DIALOG,
7419               command.output().launch_tool_mode());
7420     EXPECT_TRUE(command.output().consumed());
7421   }
7422 }
7423 
TEST_F(SessionTest,NotZeroQuerySuggest)7424 TEST_F(SessionTest, NotZeroQuerySuggest) {
7425   std::unique_ptr<Session> session(new Session(engine_.get()));
7426   InitSessionToPrecomposition(session.get());
7427 
7428   // Disable zero query suggest.
7429   commands::Request request;
7430   request.set_zero_query_suggestion(false);
7431   session->SetRequest(&request);
7432 
7433   // Type "google".
7434   commands::Command command;
7435   InsertCharacterChars("google", session.get(), &command);
7436   EXPECT_EQ("google", GetComposition(command));
7437 
7438   // Set up a mock suggestion result.
7439   Segments segments;
7440   segments.set_request_type(Segments::SUGGESTION);
7441   Segment *segment;
7442   segment = segments.add_segment();
7443   segment->set_key("");
7444   segment->add_candidate()->value = "search";
7445   segment->add_candidate()->value = "input";
7446   GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
7447 
7448   // Commit composition and zero query suggest should not be invoked.
7449   command.Clear();
7450   session->Commit(&command);
7451   EXPECT_EQ("google", command.output().result().value());
7452   EXPECT_EQ("", GetComposition(command));
7453   EXPECT_FALSE(command.output().has_candidates());
7454 
7455   const ImeContext &context = session->context();
7456   EXPECT_EQ(ImeContext::PRECOMPOSITION, context.state());
7457 }
7458 
TEST_F(SessionTest,ZeroQuerySuggest)7459 TEST_F(SessionTest, ZeroQuerySuggest) {
7460   {  // Commit
7461     Session session(engine_.get());
7462     commands::Request request;
7463     SetupZeroQuerySuggestionReady(true, &session, &request);
7464 
7465     commands::Command command;
7466     session.Commit(&command);
7467     EXPECT_EQ("GOOGLE", command.output().result().value());
7468     EXPECT_EQ("", GetComposition(command));
7469     EXPECT_TRUE(command.output().has_candidates());
7470     EXPECT_EQ(2, command.output().candidates().candidate_size());
7471     EXPECT_EQ("search", command.output().candidates().candidate(0).value());
7472     EXPECT_EQ("input", command.output().candidates().candidate(1).value());
7473     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7474   }
7475 
7476   {  // CommitSegment
7477     Session session(engine_.get());
7478     commands::Request request;
7479     SetupZeroQuerySuggestionReady(true, &session, &request);
7480 
7481     commands::Command command;
7482     session.CommitSegment(&command);
7483     EXPECT_EQ("GOOGLE", command.output().result().value());
7484     EXPECT_EQ("", GetComposition(command));
7485     EXPECT_TRUE(command.output().has_candidates());
7486     EXPECT_EQ(2, command.output().candidates().candidate_size());
7487     EXPECT_EQ("search", command.output().candidates().candidate(0).value());
7488     EXPECT_EQ("input", command.output().candidates().candidate(1).value());
7489     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7490   }
7491 
7492   {  // CommitCandidate
7493     Session session(engine_.get());
7494     commands::Request request;
7495     SetupZeroQuerySuggestionReady(true, &session, &request);
7496 
7497     commands::Command command;
7498     SetSendCommandCommand(commands::SessionCommand::SUBMIT_CANDIDATE, &command);
7499     command.mutable_input()->mutable_command()->set_id(0);
7500     session.SendCommand(&command);
7501 
7502     EXPECT_EQ("GOOGLE", command.output().result().value());
7503     EXPECT_EQ("", GetComposition(command));
7504     EXPECT_TRUE(command.output().has_candidates());
7505     EXPECT_EQ(2, command.output().candidates().candidate_size());
7506     EXPECT_EQ("search", command.output().candidates().candidate(0).value());
7507     EXPECT_EQ("input", command.output().candidates().candidate(1).value());
7508     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7509   }
7510 
7511   {  // CommitFirstSuggestion
7512     Session session(engine_.get());
7513     InitSessionToPrecomposition(&session);
7514 
7515     // Enable zero query suggest.
7516     commands::Request request;
7517     request.set_zero_query_suggestion(true);
7518     session.SetRequest(&request);
7519 
7520     // Type "g".
7521     commands::Command command;
7522     InsertCharacterChars("g", &session, &command);
7523 
7524     {
7525       // Set up a mock conversion result.
7526       Segments segments;
7527       segments.set_request_type(Segments::SUGGESTION);
7528       Segment *segment;
7529       segment = segments.add_segment();
7530       segment->set_key("");
7531       segment->add_candidate()->value = "google";
7532       GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
7533     }
7534 
7535     command.Clear();
7536     InsertCharacterChars("o", &session, &command);
7537 
7538     {
7539       // Set up a mock suggestion result.
7540       Segments segments;
7541       segments.set_request_type(Segments::SUGGESTION);
7542       Segment *segment;
7543       segment = segments.add_segment();
7544       segment->set_key("");
7545       segment->add_candidate()->value = "search";
7546       segment->add_candidate()->value = "input";
7547       GetConverterMock()->SetStartSuggestionForRequest(&segments, true);
7548     }
7549 
7550     command.Clear();
7551     Segments empty_segments;
7552     GetConverterMock()->SetFinishConversion(&empty_segments, true);
7553     session.CommitFirstSuggestion(&command);
7554     EXPECT_EQ("google", command.output().result().value());
7555     EXPECT_EQ("", GetComposition(command));
7556     EXPECT_TRUE(command.output().has_candidates());
7557     EXPECT_EQ(2, command.output().candidates().candidate_size());
7558     EXPECT_EQ("search", command.output().candidates().candidate(0).value());
7559     EXPECT_EQ("input", command.output().candidates().candidate(1).value());
7560     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7561   }
7562 }
7563 
TEST_F(SessionTest,CommandsAfterZeroQuerySuggest)7564 TEST_F(SessionTest, CommandsAfterZeroQuerySuggest) {
7565   {  // Cancel command should close the candidate window.
7566     Session session(engine_.get());
7567     commands::Request request;
7568     commands::Command command;
7569     SetupZeroQuerySuggestion(&session, &request, &command);
7570 
7571     command.Clear();
7572     session.EditCancel(&command);
7573     EXPECT_TRUE(command.output().consumed());
7574     EXPECT_FALSE(command.output().has_preedit());
7575     EXPECT_FALSE(command.output().has_result());
7576     EXPECT_EQ("", GetComposition(command));
7577     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7578   }
7579 
7580   {  // PredictAndConvert should select the first candidate.
7581     Session session(engine_.get());
7582     commands::Request request;
7583     commands::Command command;
7584     SetupZeroQuerySuggestion(&session, &request, &command);
7585 
7586     command.Clear();
7587     session.PredictAndConvert(&command);
7588     EXPECT_TRUE(command.output().consumed());
7589     EXPECT_FALSE(command.output().has_result());
7590     // "search" is the first suggest candidate.
7591     EXPECT_PREEDIT("search", command);
7592     EXPECT_EQ(ImeContext::CONVERSION, session.context().state());
7593   }
7594 
7595   {  // CommitFirstSuggestion should insert the first candidate.
7596     Session session(engine_.get());
7597     commands::Request request;
7598     commands::Command command;
7599     SetupZeroQuerySuggestion(&session, &request, &command);
7600 
7601     command.Clear();
7602     // FinishConversion is expected to return empty Segments.
7603     GetConverterMock()->SetFinishConversion(
7604         std::unique_ptr<Segments>(new Segments).get(), true);
7605     session.CommitFirstSuggestion(&command);
7606     EXPECT_TRUE(command.output().consumed());
7607     EXPECT_FALSE(command.output().has_preedit());
7608     EXPECT_EQ("", GetComposition(command));
7609     // "search" is the first suggest candidate.
7610     EXPECT_RESULT("search", command);
7611     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7612   }
7613 
7614   {  // Space should be inserted directly.
7615     Session session(engine_.get());
7616     commands::Request request;
7617     commands::Command command;
7618     SetupZeroQuerySuggestion(&session, &request, &command);
7619 
7620     SendKey("Space", &session, &command);
7621     EXPECT_TRUE(command.output().consumed());
7622     EXPECT_FALSE(command.output().has_preedit());
7623     EXPECT_EQ("", GetComposition(command));
7624     EXPECT_RESULT(" ", command);  // Full-width space
7625     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7626   }
7627 
7628   {  // 'a' should be inserted in the composition.
7629     Session session(engine_.get());
7630     commands::Request request;
7631     commands::Command command;
7632     SetupZeroQuerySuggestion(&session, &request, &command);
7633     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
7634 
7635     SendKey("a", &session, &command);
7636     EXPECT_TRUE(command.output().consumed());
7637     EXPECT_FALSE(command.output().has_result());
7638     EXPECT_EQ(commands::HIRAGANA, command.output().mode());
7639     EXPECT_PREEDIT("あ", command);
7640     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
7641   }
7642 
7643   {  // Enter should be inserted directly.
7644     Session session(engine_.get());
7645     commands::Request request;
7646     commands::Command command;
7647     SetupZeroQuerySuggestion(&session, &request, &command);
7648 
7649     SendKey("Enter", &session, &command);
7650     EXPECT_FALSE(command.output().consumed());
7651     EXPECT_FALSE(command.output().has_preedit());
7652     EXPECT_FALSE(command.output().has_result());
7653     EXPECT_EQ("", GetComposition(command));
7654     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7655   }
7656 
7657   {  // Right should be inserted directly.
7658     Session session(engine_.get());
7659     commands::Request request;
7660     commands::Command command;
7661     SetupZeroQuerySuggestion(&session, &request, &command);
7662 
7663     SendKey("Right", &session, &command);
7664     EXPECT_FALSE(command.output().consumed());
7665     EXPECT_FALSE(command.output().has_preedit());
7666     EXPECT_FALSE(command.output().has_result());
7667     EXPECT_EQ("", GetComposition(command));
7668     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
7669   }
7670 
7671   {  // SelectCnadidate command should work with zero query suggestion.
7672     Session session(engine_.get());
7673     commands::Request request;
7674     commands::Command command;
7675     SetupZeroQuerySuggestion(&session, &request, &command);
7676 
7677     // Send SELECT_CANDIDATE command.
7678     const int first_id = command.output().candidates().candidate(0).id();
7679     SetSendCommandCommand(commands::SessionCommand::SELECT_CANDIDATE, &command);
7680     command.mutable_input()->mutable_command()->set_id(first_id);
7681     EXPECT_TRUE(session.SendCommand(&command));
7682 
7683     EXPECT_TRUE(command.output().consumed());
7684     EXPECT_FALSE(command.output().has_result());
7685     // "search" is the first suggest candidate.
7686     EXPECT_PREEDIT("search", command);
7687     EXPECT_EQ(ImeContext::CONVERSION, session.context().state());
7688   }
7689 }
7690 
TEST_F(SessionTest,Issue4437420)7691 TEST_F(SessionTest, Issue4437420) {
7692   Session session(engine_.get());
7693   InitSessionToPrecomposition(&session);
7694   commands::Command command;
7695   commands::Request request;
7696   // Creates overriding config.
7697   config::Config overriding_config;
7698   overriding_config.set_session_keymap(config::Config::MOBILE);
7699   // Change to 12keys-halfascii mode.
7700   SwitchInputMode(commands::HALF_ASCII, &session);
7701 
7702   command.Clear();
7703   request.set_special_romanji_table(
7704       commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
7705   session.SetRequest(&request);
7706   std::unique_ptr<composer::Table> table(new composer::Table());
7707   table->InitializeWithRequestAndConfig(
7708       request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7709   session.SetTable(table.get());
7710   // Type "2*" to produce "A".
7711   SetSendKeyCommand("2", &command);
7712   command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7713   session.SendKey(&command);
7714   SetSendKeyCommand("*", &command);
7715   command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7716   session.SendKey(&command);
7717   EXPECT_EQ("A", GetComposition(command));
7718 
7719   // Change to 12keys-halfascii mode.
7720   SwitchInputMode(commands::HALF_ASCII, &session);
7721 
7722   command.Clear();
7723   request.set_special_romanji_table(
7724       commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
7725   session.SetRequest(&request);
7726   table.reset(new composer::Table());
7727   table->InitializeWithRequestAndConfig(
7728       request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7729   session.SetTable(table.get());
7730   // Type "2" to produce "Aa".
7731   SetSendKeyCommand("2", &command);
7732   command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7733   session.SendKey(&command);
7734   EXPECT_EQ("Aa", GetComposition(command));
7735   command.Clear();
7736 }
7737 
7738 // If undo context is empty, key event for UNDO should be echoed back. b/5553298
TEST_F(SessionTest,Issue5553298)7739 TEST_F(SessionTest, Issue5553298) {
7740   Session session(engine_.get());
7741   InitSessionToPrecomposition(&session);
7742 
7743   // Undo requires capability DELETE_PRECEDING_TEXT.
7744   commands::Capability capability;
7745   capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
7746   session.set_client_capability(capability);
7747 
7748   commands::Command command;
7749   session.ResetContext(&command);
7750 
7751   SetSendKeyCommand("Ctrl Backspace", &command);
7752   command.mutable_input()->mutable_config()->set_session_keymap(
7753       config::Config::MSIME);
7754   session.TestSendKey(&command);
7755   EXPECT_FALSE(command.output().consumed());
7756 
7757   SetSendKeyCommand("Ctrl Backspace", &command);
7758   command.mutable_input()->mutable_config()->set_session_keymap(
7759       config::Config::MSIME);
7760   session.SendKey(&command);
7761   EXPECT_FALSE(command.output().consumed());
7762 }
7763 
TEST_F(SessionTest,UndoKeyAction)7764 TEST_F(SessionTest, UndoKeyAction) {
7765   commands::Command command;
7766   commands::Request request;
7767   // Creates overriding config.
7768   config::Config overriding_config;
7769   overriding_config.set_session_keymap(config::Config::MOBILE);
7770   // Test in half width ascii mode.
7771   {
7772     Session session(engine_.get());
7773     InitSessionToPrecomposition(&session);
7774 
7775     // Change to 12keys-halfascii mode.
7776     SwitchInputMode(commands::HALF_ASCII, &session);
7777 
7778     command.Clear();
7779     request.set_special_romanji_table(
7780         commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
7781     session.SetRequest(&request);
7782     composer::Table table;
7783     table.InitializeWithRequestAndConfig(
7784         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7785     session.SetTable(&table);
7786 
7787     // Type "2" to produce "a".
7788     SetSendKeyCommand("2", &command);
7789     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7790     session.SendKey(&command);
7791     EXPECT_EQ("a", GetComposition(command));
7792 
7793     // Type "2" again to produce "b".
7794     SetSendKeyCommand("2", &command);
7795     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7796     session.SendKey(&command);
7797     EXPECT_EQ("b", GetComposition(command));
7798 
7799     // Push UNDO key to reproduce "a".
7800     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7801     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7802     session.SendCommand(&command);
7803     EXPECT_EQ("a", GetComposition(command));
7804     EXPECT_TRUE(command.output().consumed());
7805 
7806     // Push UNDO key again to produce "2".
7807     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7808     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7809     session.SendCommand(&command);
7810     EXPECT_EQ("2", GetComposition(command));
7811     EXPECT_TRUE(command.output().consumed());
7812     command.Clear();
7813   }
7814 
7815   // Test in Hiaragana-mode.
7816   {
7817     Session session(engine_.get());
7818     InitSessionToPrecomposition(&session);
7819 
7820     // Change to 12keys-Hiragana mode.
7821     SwitchInputMode(commands::HIRAGANA, &session);
7822 
7823     command.Clear();
7824     request.set_special_romanji_table(
7825         commands::Request::TWELVE_KEYS_TO_HIRAGANA);
7826     session.SetRequest(&request);
7827     composer::Table table;
7828     table.InitializeWithRequestAndConfig(
7829         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7830     session.SetTable(&table);
7831     // Type "33{<}{<}" to produce "さ"->"し"->"さ"->"そ".
7832     SetSendKeyCommand("3", &command);
7833     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7834     session.SendKey(&command);
7835     EXPECT_EQ("さ", GetComposition(command));
7836 
7837     SetSendKeyCommand("3", &command);
7838     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7839     session.SendKey(&command);
7840     EXPECT_EQ("し", GetComposition(command));
7841 
7842     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7843     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7844     session.SendCommand(&command);
7845     EXPECT_EQ("さ", GetComposition(command));
7846     EXPECT_TRUE(command.output().consumed());
7847     command.Clear();
7848 
7849     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7850     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7851     session.SendCommand(&command);
7852     EXPECT_EQ("そ", GetComposition(command));
7853     EXPECT_TRUE(command.output().consumed());
7854     command.Clear();
7855   }
7856 
7857   // Test to do nothing for voiced sounds.
7858   {
7859     Session session(engine_.get());
7860     InitSessionToPrecomposition(&session);
7861 
7862     // Change to 12keys-Hiragana mode.
7863     SwitchInputMode(commands::HIRAGANA, &session);
7864 
7865     command.Clear();
7866     request.set_special_romanji_table(
7867         commands::Request::TWELVE_KEYS_TO_HIRAGANA);
7868     session.SetRequest(&request);
7869     composer::Table table;
7870     table.InitializeWithRequestAndConfig(
7871         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7872     session.SetTable(&table);
7873     // Type "3*{<}*{<}", and composition should change
7874     // "さ"->"ざ"->(No change)->"さ"->(No change).
7875     SetSendKeyCommand("3", &command);
7876     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7877     session.SendKey(&command);
7878     EXPECT_EQ("さ", GetComposition(command));
7879 
7880     SetSendKeyCommand("*", &command);
7881     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7882     session.SendKey(&command);
7883     EXPECT_EQ("ざ", GetComposition(command));
7884 
7885     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7886     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7887     session.SendCommand(&command);
7888     EXPECT_EQ("ざ", GetComposition(command));
7889     EXPECT_TRUE(command.output().consumed());
7890 
7891 
7892     SetSendKeyCommand("*", &command);
7893     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7894     session.SendKey(&command);
7895     EXPECT_EQ("さ", GetComposition(command));
7896     command.Clear();
7897 
7898     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7899     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7900     session.SendCommand(&command);
7901     EXPECT_EQ("さ", GetComposition(command));
7902     EXPECT_TRUE(command.output().consumed());
7903     command.Clear();
7904   }
7905 
7906   // Test to make nothing newly in preedit for empty composition.
7907   {
7908     Session session(engine_.get());
7909     InitSessionToPrecomposition(&session);
7910 
7911     // Change to 12keys-Hiragana mode.
7912     SwitchInputMode(commands::HIRAGANA, &session);
7913 
7914     command.Clear();
7915     request.set_special_romanji_table(
7916         commands::Request::TWELVE_KEYS_TO_HIRAGANA);
7917     session.SetRequest(&request);
7918     composer::Table table;
7919     table.InitializeWithRequestAndConfig(
7920         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
7921     session.SetTable(&table);
7922     // Type "{<}" and do nothing
7923     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7924     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7925     session.SendCommand(&command);
7926 
7927     EXPECT_FALSE(command.output().has_preedit());
7928 
7929     command.Clear();
7930   }
7931 
7932   // Test of acting as UNDO key. Almost same as the first section in Undo test.
7933   {
7934     Session session(engine_.get());
7935     InitSessionToPrecomposition(&session);
7936 
7937     commands::Capability capability;
7938     capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
7939     session.set_client_capability(capability);
7940 
7941     Segments segments;
7942     InsertCharacterChars("aiueo", &session, &command);
7943     ConversionRequest request;
7944     SetComposer(&session, &request);
7945     SetAiueo(&segments);
7946     Segment::Candidate *candidate;
7947     candidate = segments.mutable_segment(0)->add_candidate();
7948     candidate->value = "aiueo";
7949     candidate = segments.mutable_segment(0)->add_candidate();
7950     candidate->value = "AIUEO";
7951 
7952     GetConverterMock()->SetStartConversionForRequest(&segments, true);
7953     command.Clear();
7954     session.Convert(&command);
7955     EXPECT_FALSE(command.output().has_result());
7956     EXPECT_PREEDIT("あいうえお", command);
7957 
7958     GetConverterMock()->SetCommitSegmentValue(&segments, true);
7959     command.Clear();
7960     session.Commit(&command);
7961     EXPECT_FALSE(command.output().has_preedit());
7962     EXPECT_RESULT("あいうえお", command);
7963 
7964     command.Clear();
7965     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7966     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
7967     session.SendCommand(&command);
7968     EXPECT_FALSE(command.output().has_result());
7969     EXPECT_TRUE(command.output().has_deletion_range());
7970     EXPECT_EQ(-5, command.output().deletion_range().offset());
7971     EXPECT_EQ(5, command.output().deletion_range().length());
7972     EXPECT_PREEDIT("あいうえお", command);
7973     EXPECT_TRUE(command.output().consumed());
7974 
7975     // Undo twice - do nothing and keep the previous status.
7976     command.Clear();
7977     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
7978     session.SendCommand(&command);
7979     EXPECT_FALSE(command.output().has_result());
7980     EXPECT_FALSE(command.output().has_deletion_range());
7981     EXPECT_PREEDIT("あいうえお", command);
7982     EXPECT_TRUE(command.output().consumed());
7983   }
7984 
7985   // Do not UNDO even if UNDO stack is not empty if it is in COMPOSITE state.
7986   {
7987     Session session(engine_.get());
7988     InitSessionToPrecomposition(&session);
7989 
7990     // Change to 12keys-Hiragana mode.
7991     SwitchInputMode(commands::HIRAGANA, &session);
7992 
7993     command.Clear();
7994     request.set_special_romanji_table(
7995         commands::Request::TWELVE_KEYS_TO_HIRAGANA);
7996     session.SetRequest(&request);
7997     composer::Table table;
7998     table.InitializeWithRequestAndConfig(
7999         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
8000     session.SetTable(&table);
8001 
8002     // commit "あ" to push UNDO stack
8003     SetSendKeyCommand("1", &command);
8004     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
8005     session.SendKey(&command);
8006     EXPECT_EQ("あ", GetComposition(command));
8007     command.Clear();
8008 
8009     session.Commit(&command);
8010     EXPECT_FALSE(command.output().has_preedit());
8011     EXPECT_RESULT("あ", command);
8012 
8013     // Produce "か" in composition.
8014     SetSendKeyCommand("2", &command);
8015     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
8016     session.SendKey(&command);
8017     EXPECT_EQ("か", GetComposition(command));
8018     EXPECT_TRUE(command.output().consumed());
8019     command.Clear();
8020 
8021     // Send UNDO_OR_REWIND key, then get "こ" in composition
8022     SetSendCommandCommand(commands::SessionCommand::UNDO_OR_REWIND, &command);
8023     command.mutable_input()->mutable_config()->CopyFrom(overriding_config);
8024     session.SendCommand(&command);
8025     EXPECT_PREEDIT("こ", command);
8026     EXPECT_TRUE(command.output().consumed());
8027     command.Clear();
8028   }
8029 }
8030 
TEST_F(SessionTest,DedupAfterUndo)8031 TEST_F(SessionTest, DedupAfterUndo) {
8032   commands::Command command;
8033   {
8034     Session session(mock_data_engine_.get());
8035     InitSessionToPrecomposition(&session, *mobile_request_);
8036 
8037     // Undo requires capability DELETE_PRECEDING_TEXT.
8038     commands::Capability capability;
8039     capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
8040     session.set_client_capability(capability);
8041 
8042     SwitchInputMode(commands::HIRAGANA, &session);
8043 
8044     commands::Request request(*mobile_request_);
8045     request.set_special_romanji_table(
8046         commands::Request::TWELVE_KEYS_TO_HIRAGANA);
8047     session.SetRequest(&request);
8048 
8049     composer::Table table;
8050     table.InitializeWithRequestAndConfig(
8051         request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
8052     session.SetTable(&table);
8053 
8054     // Type "!" to produce "!".
8055     SetSendKeyCommand("!", &command);
8056     session.SendKey(&command);
8057     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8058     EXPECT_EQ("!", GetComposition(command));
8059 
8060     ASSERT_TRUE(command.output().has_candidates());
8061 
8062     std::vector<int> ids;
8063     FindCandidateIDs(command.output().candidates(), "!", &ids);
8064     EXPECT_GE(1, ids.size());
8065 
8066     FindCandidateIDs(command.output().candidates(), "!", &ids);
8067     EXPECT_GE(1, ids.size());
8068 
8069     const int candidate_size_before_undo =
8070         command.output().candidates().candidate_size();
8071 
8072     command.Clear();
8073     session.CommitFirstSuggestion(&command);
8074     EXPECT_FALSE(command.output().has_preedit());
8075     EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
8076 
8077     command.Clear();
8078     session.Undo(&command);
8079     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8080     EXPECT_TRUE(command.output().has_deletion_range());
8081     ASSERT_TRUE(command.output().has_candidates());
8082 
8083     FindCandidateIDs(command.output().candidates(), "!", &ids);
8084     EXPECT_GE(1, ids.size());
8085 
8086     FindCandidateIDs(command.output().candidates(), "!", &ids);
8087     EXPECT_GE(1, ids.size());
8088 
8089     EXPECT_EQ(command.output().candidates().candidate_size(),
8090               candidate_size_before_undo);
8091   }
8092 }
8093 
TEST_F(SessionTest,TemporaryKeyMapChange)8094 TEST_F(SessionTest, TemporaryKeyMapChange) {
8095   config::Config config(config::ConfigHandler::DefaultConfig());
8096   config.set_session_keymap(config::Config::ATOK);
8097 
8098   // Session created with keymap ATOK
8099   Session session(engine_.get());
8100   session.SetConfig(&config);
8101   InitSessionToPrecomposition(&session);
8102   EXPECT_EQ(config::Config::ATOK, session.context().keymap());
8103 
8104   // TestSendKey with keymap MOBLE
8105   commands::Command command;
8106   SetSendKeyCommand("G", &command);
8107   command.mutable_input()->mutable_config()->set_session_keymap(
8108       config::Config::MOBILE);
8109   session.TestSendKey(&command);
8110   EXPECT_TRUE(command.output().consumed());
8111   EXPECT_EQ(config::Config::MOBILE, session.context().keymap());
8112 
8113   // TestSendKey without keymap
8114   TestSendKey("G", &session, &command);
8115   EXPECT_TRUE(command.output().consumed());
8116   EXPECT_EQ(config::Config::ATOK, session.context().keymap());
8117 }
8118 
TEST_F(SessionTest,MoveCursor)8119 TEST_F(SessionTest, MoveCursor) {
8120   std::unique_ptr<Session> session(new Session(engine_.get()));
8121   InitSessionToPrecomposition(session.get());
8122   commands::Command command;
8123 
8124   InsertCharacterChars("MOZUKU", session.get(), &command);
8125   EXPECT_EQ(6, command.output().preedit().cursor());
8126   session->MoveCursorLeft(&command);
8127   EXPECT_EQ(5, command.output().preedit().cursor());
8128   command.mutable_input()->mutable_command()->set_cursor_position(3);
8129   session->MoveCursorTo(&command);
8130   EXPECT_EQ(3, command.output().preedit().cursor());
8131   session->MoveCursorRight(&command);
8132   EXPECT_EQ(4, command.output().preedit().cursor());
8133 }
8134 
TEST_F(SessionTest,MoveCursorRightWithCommit)8135 TEST_F(SessionTest, MoveCursorRightWithCommit) {
8136   std::unique_ptr<Session> session(new Session(engine_.get()));
8137   commands::Request request;
8138   request.CopyFrom(*mobile_request_);
8139   request.set_special_romanji_table(
8140       commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII);
8141   request.set_crossing_edge_behavior(
8142       commands::Request::COMMIT_WITHOUT_CONSUMING);
8143   InitSessionToPrecomposition(session.get(), request);
8144   commands::Command command;
8145 
8146   InsertCharacterChars("MOZC", session.get(), &command);
8147   EXPECT_EQ(4, command.output().preedit().cursor());
8148   command.Clear();
8149   session->MoveCursorLeft(&command);
8150   EXPECT_EQ(3, command.output().preedit().cursor());
8151   command.Clear();
8152   session->MoveCursorRight(&command);
8153   EXPECT_EQ(4, command.output().preedit().cursor());
8154   command.Clear();
8155   session->MoveCursorRight(&command);
8156   EXPECT_FALSE(command.output().consumed());
8157   ASSERT_TRUE(command.output().has_result());
8158   EXPECT_EQ(commands::Result_ResultType_STRING,
8159             command.output().result().type());
8160   EXPECT_EQ("MOZC", command.output().result().value());
8161   EXPECT_EQ(0, command.output().result().cursor_offset());
8162 }
8163 
TEST_F(SessionTest,MoveCursorLeftWithCommit)8164 TEST_F(SessionTest, MoveCursorLeftWithCommit) {
8165   std::unique_ptr<Session> session(new Session(engine_.get()));
8166   commands::Request request;
8167   request.CopyFrom(*mobile_request_);
8168   request.set_special_romanji_table(
8169       commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII);
8170   request.set_crossing_edge_behavior(
8171       commands::Request::COMMIT_WITHOUT_CONSUMING);
8172   InitSessionToPrecomposition(session.get(), request);
8173   commands::Command command;
8174 
8175   InsertCharacterChars("MOZC", session.get(), &command);
8176   EXPECT_EQ(4, command.output().preedit().cursor());
8177   command.Clear();
8178   session->MoveCursorLeft(&command);
8179   EXPECT_EQ(3, command.output().preedit().cursor());
8180   command.Clear();
8181   session->MoveCursorLeft(&command);
8182   EXPECT_EQ(2, command.output().preedit().cursor());
8183   command.Clear();
8184   session->MoveCursorLeft(&command);
8185   EXPECT_EQ(1, command.output().preedit().cursor());
8186   command.Clear();
8187   session->MoveCursorLeft(&command);
8188   EXPECT_EQ(0, command.output().preedit().cursor());
8189   command.Clear();
8190 
8191   session->MoveCursorLeft(&command);
8192   EXPECT_FALSE(command.output().consumed());
8193   ASSERT_TRUE(command.output().has_result());
8194   EXPECT_EQ(commands::Result_ResultType_STRING,
8195             command.output().result().type());
8196   EXPECT_EQ("MOZC", command.output().result().value());
8197   EXPECT_EQ(-4, command.output().result().cursor_offset());
8198 }
8199 
TEST_F(SessionTest,MoveCursorRightWithCommitWithZeroQuerySuggestion)8200 TEST_F(SessionTest, MoveCursorRightWithCommitWithZeroQuerySuggestion) {
8201   std::unique_ptr<Session> session(new Session(engine_.get()));
8202   commands::Request request(*mobile_request_);
8203   request.set_special_romanji_table(
8204       commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII);
8205   request.set_crossing_edge_behavior(
8206       commands::Request::COMMIT_WITHOUT_CONSUMING);
8207   SetupZeroQuerySuggestionReady(true, session.get(), &request);
8208   commands::Command command;
8209 
8210   InsertCharacterChars("GOOGLE", session.get(), &command);
8211   EXPECT_EQ(6, command.output().preedit().cursor());
8212   command.Clear();
8213 
8214   session->MoveCursorRight(&command);
8215   EXPECT_FALSE(command.output().consumed());
8216   ASSERT_TRUE(command.output().has_result());
8217   EXPECT_EQ(commands::Result_ResultType_STRING,
8218             command.output().result().type());
8219   EXPECT_EQ("GOOGLE", command.output().result().value());
8220   EXPECT_EQ(0, command.output().result().cursor_offset());
8221   EXPECT_TRUE(command.output().has_candidates());
8222   EXPECT_EQ(2, command.output().candidates().candidate_size());
8223 }
8224 
TEST_F(SessionTest,MoveCursorLeftWithCommitWithZeroQuerySuggestion)8225 TEST_F(SessionTest, MoveCursorLeftWithCommitWithZeroQuerySuggestion) {
8226   std::unique_ptr<Session> session(new Session(engine_.get()));
8227   commands::Request request(*mobile_request_);
8228   request.set_special_romanji_table(
8229       commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII);
8230   request.set_crossing_edge_behavior(
8231       commands::Request::COMMIT_WITHOUT_CONSUMING);
8232   SetupZeroQuerySuggestionReady(true, session.get(), &request);
8233   commands::Command command;
8234 
8235   InsertCharacterChars("GOOGLE", session.get(), &command);
8236   EXPECT_EQ(6, command.output().preedit().cursor());
8237   command.Clear();
8238   for (int i = 5; i >= 0; --i) {
8239     session->MoveCursorLeft(&command);
8240     EXPECT_EQ(i, command.output().preedit().cursor());
8241     command.Clear();
8242   }
8243 
8244   session->MoveCursorLeft(&command);
8245   EXPECT_FALSE(command.output().consumed());
8246   ASSERT_TRUE(command.output().has_result());
8247   EXPECT_EQ(commands::Result_ResultType_STRING,
8248             command.output().result().type());
8249   EXPECT_EQ("GOOGLE", command.output().result().value());
8250   EXPECT_EQ(-6, command.output().result().cursor_offset());
8251   EXPECT_FALSE(command.output().has_candidates());
8252 }
8253 
TEST_F(SessionTest,CommitHead)8254 TEST_F(SessionTest, CommitHead) {
8255   std::unique_ptr<Session> session(new Session(engine_.get()));
8256   composer::Table table;
8257   table.AddRule("mo", "も", "");
8258   table.AddRule("zu", "ず", "");
8259 
8260   session->get_internal_composer_only_for_unittest()->SetTable(&table);
8261 
8262   InitSessionToPrecomposition(session.get());
8263   commands::Command command;
8264 
8265   InsertCharacterChars("moz", session.get(), &command);
8266   EXPECT_EQ("もz", GetComposition(command));
8267   command.Clear();
8268   session->CommitHead(1, &command);
8269   EXPECT_EQ(commands::Result_ResultType_STRING,
8270             command.output().result().type());
8271   EXPECT_EQ("も", command.output().result().value());
8272   EXPECT_EQ("z", GetComposition(command));
8273   InsertCharacterChars("u", session.get(), &command);
8274   EXPECT_EQ("ず", GetComposition(command));
8275 }
8276 
TEST_F(SessionTest,PasswordWithToggleAlpabetInput)8277 TEST_F(SessionTest, PasswordWithToggleAlpabetInput) {
8278   std::unique_ptr<Session> session(new Session(engine_.get()));
8279 
8280   commands::Request request;
8281   request.CopyFrom(*mobile_request_);
8282   request.set_special_romanji_table(
8283       commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
8284 
8285   InitSessionToPrecomposition(session.get(), request);
8286 
8287   // Change to 12keys-halfascii mode.
8288   SwitchInputFieldType(commands::Context::PASSWORD, session.get());
8289   SwitchInputMode(commands::HALF_ASCII, session.get());
8290 
8291   commands::Command command;
8292   SendKey("2", session.get(), &command);
8293   EXPECT_EQ("a", GetComposition(command));
8294   EXPECT_EQ(1, command.output().preedit().cursor());
8295 
8296   SendKey("2", session.get(), &command);
8297   EXPECT_EQ("b", GetComposition(command));
8298   EXPECT_EQ(1, command.output().preedit().cursor());
8299 
8300   // cursor key commits the preedit.
8301   SendKey("right", session.get(), &command);
8302   // "b"
8303   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8304   EXPECT_EQ("b", command.output().result().value());
8305   EXPECT_EQ("", GetComposition(command));
8306   EXPECT_EQ(0, command.output().preedit().cursor());
8307 
8308   SendKey("2", session.get(), &command);
8309   // "b[a]"
8310   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8311   EXPECT_EQ("a", GetComposition(command));
8312   EXPECT_EQ(1, command.output().preedit().cursor());
8313 
8314   SendKey("4", session.get(), &command);
8315   // ba[g]
8316   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8317   EXPECT_EQ("a", command.output().result().value());
8318   EXPECT_EQ("g", GetComposition(command));
8319   EXPECT_EQ(1, command.output().preedit().cursor());
8320 
8321   // cursor key commits the preedit.
8322   SendKey("left", session.get(), &command);
8323   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8324   EXPECT_EQ("g", command.output().result().value());
8325   EXPECT_EQ(0, command.output().preedit().segment_size());
8326   EXPECT_EQ(0, command.output().preedit().cursor());
8327 }
8328 
TEST_F(SessionTest,SwitchInputFieldType)8329 TEST_F(SessionTest, SwitchInputFieldType) {
8330   std::unique_ptr<Session> session(new Session(engine_.get()));
8331   InitSessionToPrecomposition(session.get());
8332 
8333   // initial state is NORMAL
8334   EXPECT_EQ(commands::Context::NORMAL,
8335             session->context().composer().GetInputFieldType());
8336 
8337   {
8338     SCOPED_TRACE("Switch input field type to PASSWORD");
8339     SwitchInputFieldType(commands::Context::PASSWORD, session.get());
8340   }
8341   {
8342     SCOPED_TRACE("Switch input field type to NORMAL");
8343     SwitchInputFieldType(commands::Context::NORMAL, session.get());
8344   }
8345 }
8346 
TEST_F(SessionTest,CursorKeysInPasswordMode)8347 TEST_F(SessionTest, CursorKeysInPasswordMode) {
8348   std::unique_ptr<Session> session(new Session(engine_.get()));
8349 
8350   commands::Request request;
8351   request.CopyFrom(*mobile_request_);
8352   request.set_special_romanji_table(commands::Request::DEFAULT_TABLE);
8353   session->SetRequest(&request);
8354 
8355   InitSessionToPrecomposition(session.get(), request);
8356 
8357   SwitchInputFieldType(commands::Context::PASSWORD, session.get());
8358   SwitchInputMode(commands::HALF_ASCII, session.get());
8359 
8360   commands::Command command;
8361   // cursor key commits the preedit without moving system cursor.
8362   SendKey("m", session.get(), &command);
8363   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8364   command.Clear();
8365   session->MoveCursorLeft(&command);
8366   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8367   EXPECT_EQ("m", command.output().result().value());
8368   EXPECT_EQ("", GetComposition(command));
8369   VLOG(0) << command.DebugString();
8370   EXPECT_EQ(0, command.output().preedit().cursor());
8371   EXPECT_TRUE(command.output().consumed());
8372 
8373   SendKey("o", session.get(), &command);
8374   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8375   command.Clear();
8376   session->MoveCursorRight(&command);
8377   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8378   EXPECT_EQ("o", command.output().result().value());
8379   EXPECT_EQ("", GetComposition(command));
8380   EXPECT_EQ(0, command.output().preedit().cursor());
8381   EXPECT_TRUE(command.output().consumed());
8382 
8383   SendKey("z", session.get(), &command);
8384   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8385   SetSendCommandCommand(commands::SessionCommand::MOVE_CURSOR, &command);
8386   command.mutable_input()->mutable_command()->set_cursor_position(3);
8387   session->MoveCursorTo(&command);
8388   EXPECT_EQ("z", command.output().result().value());
8389   EXPECT_EQ("", GetComposition(command));
8390   EXPECT_EQ(0, command.output().preedit().cursor());
8391   EXPECT_TRUE(command.output().consumed());
8392 }
8393 
TEST_F(SessionTest,BackKeyCommitsPreeditInPasswordMode)8394 TEST_F(SessionTest, BackKeyCommitsPreeditInPasswordMode) {
8395   std::unique_ptr<Session> session(new Session(engine_.get()));
8396   InitSessionToPrecomposition(session.get());
8397   commands::Command command;
8398   commands::Request request;
8399 
8400   request.set_zero_query_suggestion(false);
8401   request.set_special_romanji_table(commands::Request::DEFAULT_TABLE);
8402   session->SetRequest(&request);
8403 
8404   composer::Table table;
8405   table.InitializeWithRequestAndConfig(
8406       request, config::ConfigHandler::DefaultConfig(), mock_data_manager_);
8407   session->SetTable(&table);
8408 
8409   SwitchInputFieldType(commands::Context::PASSWORD, session.get());
8410   SwitchInputMode(commands::HALF_ASCII, session.get());
8411 
8412   SendKey("m", session.get(), &command);
8413   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8414   EXPECT_EQ("m", GetComposition(command));
8415   SendKey("esc", session.get(), &command);
8416   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8417   EXPECT_EQ("m", command.output().result().value());
8418   EXPECT_EQ("", GetComposition(command));
8419   EXPECT_FALSE(command.output().consumed());
8420 
8421   SendKey("o", session.get(), &command);
8422   SendKey("z", session.get(), &command);
8423   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8424   EXPECT_EQ("o", command.output().result().value());
8425   EXPECT_EQ("z", GetComposition(command));
8426   SendKey("esc", session.get(), &command);
8427   EXPECT_EQ(commands::Result::STRING, command.output().result().type());
8428   EXPECT_EQ("z", command.output().result().value());
8429   EXPECT_EQ("", GetComposition(command));
8430   EXPECT_FALSE(command.output().consumed());
8431 
8432   // in normal mode, preedit is cleared without commit.
8433   SwitchInputFieldType(commands::Context::NORMAL, session.get());
8434 
8435   SendKey("m", session.get(), &command);
8436   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8437   EXPECT_EQ("m", GetComposition(command));
8438   SendKey("esc", session.get(), &command);
8439   EXPECT_TRUE(command.output().consumed());
8440   EXPECT_EQ(commands::Result::NONE, command.output().result().type());
8441   EXPECT_FALSE(command.output().has_preedit());
8442 }
8443 
TEST_F(SessionTest,EditCancel)8444 TEST_F(SessionTest, EditCancel) {
8445   Session session(engine_.get());
8446   InitSessionToPrecomposition(&session);
8447 
8448   Segments segments_mo;
8449   {
8450     segments_mo.set_request_type(Segments::SUGGESTION);
8451     Segment *segment;
8452     segment = segments_mo.add_segment();
8453     segment->set_key("MO");
8454     segment->add_candidate()->value = "MOCHA";
8455     segment->add_candidate()->value = "MOZUKU";
8456   }
8457 
8458   {  // Cancel of Suggestion
8459     commands::Command command;
8460     SendKey("M", &session, &command);
8461 
8462     GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
8463     SendKey("O", &session, &command);
8464     ASSERT_TRUE(command.output().has_candidates());
8465     EXPECT_EQ(2, command.output().candidates().candidate_size());
8466     EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
8467 
8468     command.Clear();
8469     session.EditCancel(&command);
8470     EXPECT_EQ("", GetComposition(command));
8471     EXPECT_EQ(0, command.output().candidates().candidate_size());
8472     EXPECT_FALSE(command.output().has_result());
8473   }
8474 
8475   {  // Cancel of Reverse conversion
8476     commands::Command command;
8477 
8478     // "[MO]" is a converted string like Kanji.
8479     // "MO" is an input string like Hiragana.
8480     SetupCommandForReverseConversion("[MO]", command.mutable_input());
8481     SetupMockForReverseConversion("[MO]", "MO");
8482     EXPECT_TRUE(session.SendCommand(&command));
8483 
8484     command.Clear();
8485     GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
8486     session.ConvertCancel(&command);
8487     ASSERT_TRUE(command.output().has_candidates());
8488     EXPECT_EQ(2, command.output().candidates().candidate_size());
8489     EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
8490 
8491     command.Clear();
8492     session.EditCancel(&command);
8493     EXPECT_EQ("", GetComposition(command));
8494     EXPECT_EQ(0, command.output().candidates().candidate_size());
8495     // test case against b/5566728
8496     EXPECT_RESULT("[MO]", command);
8497   }
8498 }
8499 
TEST_F(SessionTest,ImeOff)8500 TEST_F(SessionTest, ImeOff) {
8501   std::unique_ptr<MockConverterEngineForReset> engine(
8502       new MockConverterEngineForReset);
8503   ConverterMockForReset *convertermock = engine->mutable_converter_mock();
8504 
8505   convertermock->Reset();
8506   std::unique_ptr<Session> session(new Session(engine.get()));
8507   InitSessionToPrecomposition(session.get());
8508   commands::Command command;
8509   session->IMEOff(&command);
8510 
8511   EXPECT_TRUE(convertermock->reset_conversion_called());
8512 }
8513 
TEST_F(SessionTest,EditCancelAndIMEOff)8514 TEST_F(SessionTest, EditCancelAndIMEOff) {
8515   config::Config config;
8516   {
8517     const string custom_keymap_table =
8518         "status\tkey\tcommand\n"
8519         "Precomposition\thankaku/zenkaku\tCancelAndIMEOff\n"
8520         "Composition\thankaku/zenkaku\tCancelAndIMEOff\n"
8521         "Conversion\thankaku/zenkaku\tCancelAndIMEOff\n";
8522     config.set_session_keymap(config::Config::CUSTOM);
8523     config.set_custom_keymap_table(custom_keymap_table);
8524   }
8525 
8526   Segments segments_mo;
8527   {
8528     segments_mo.set_request_type(Segments::SUGGESTION);
8529     Segment *segment;
8530     segment = segments_mo.add_segment();
8531     segment->set_key("MO");
8532     segment->add_candidate()->value = "MOCHA";
8533     segment->add_candidate()->value = "MOZUKU";
8534   }
8535 
8536   {  // Cancel of Precomposition and deactivate IME
8537     Session session(engine_.get());
8538     session.SetConfig(&config);
8539     InitSessionToPrecomposition(&session);
8540 
8541     commands::Command command;
8542     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8543     EXPECT_TRUE(command.output().consumed());
8544 
8545     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8546     EXPECT_TRUE(command.output().consumed());
8547     EXPECT_EQ("", GetComposition(command));
8548     EXPECT_EQ(0, command.output().candidates().candidate_size());
8549     EXPECT_FALSE(command.output().has_result());
8550     ASSERT_TRUE(command.output().has_status());
8551     EXPECT_FALSE(command.output().status().activated());
8552   }
8553 
8554   {  // Cancel of Composition and deactivate IME
8555     Session session(engine_.get());
8556     session.SetConfig(&config);
8557     InitSessionToPrecomposition(&session);
8558 
8559     commands::Command command;
8560     SendKey("M", &session, &command);
8561 
8562     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8563     EXPECT_TRUE(command.output().consumed());
8564 
8565     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8566     EXPECT_TRUE(command.output().consumed());
8567     EXPECT_EQ("", GetComposition(command));
8568     EXPECT_EQ(0, command.output().candidates().candidate_size());
8569     EXPECT_FALSE(command.output().has_result());
8570     ASSERT_TRUE(command.output().has_status());
8571     EXPECT_FALSE(command.output().status().activated());
8572   }
8573 
8574   {  // Cancel of Suggestion and deactivate IME
8575     Session session(engine_.get());
8576     session.SetConfig(&config);
8577     InitSessionToPrecomposition(&session);
8578 
8579     commands::Command command;
8580     SendKey("M", &session, &command);
8581 
8582     GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
8583     SendKey("O", &session, &command);
8584     ASSERT_TRUE(command.output().has_candidates());
8585     EXPECT_EQ(2, command.output().candidates().candidate_size());
8586     EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
8587 
8588     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8589     EXPECT_TRUE(command.output().consumed());
8590 
8591     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8592     EXPECT_TRUE(command.output().consumed());
8593     EXPECT_EQ("", GetComposition(command));
8594     EXPECT_EQ(0, command.output().candidates().candidate_size());
8595     EXPECT_FALSE(command.output().has_result());
8596     ASSERT_TRUE(command.output().has_status());
8597     EXPECT_FALSE(command.output().status().activated());
8598   }
8599 
8600   {  // Cancel of Conversion and deactivate IME
8601     Session session(engine_.get());
8602     session.SetConfig(&config);
8603     InitSessionToConversionWithAiueo(&session);
8604 
8605     commands::Command command;
8606     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8607     EXPECT_TRUE(command.output().consumed());
8608 
8609     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8610     EXPECT_TRUE(command.output().consumed());
8611     EXPECT_EQ("", GetComposition(command));
8612     EXPECT_EQ(0, command.output().candidates().candidate_size());
8613     EXPECT_FALSE(command.output().has_result());
8614     ASSERT_TRUE(command.output().has_status());
8615     EXPECT_FALSE(command.output().status().activated());
8616   }
8617 
8618   {  // Cancel of Reverse conversion and deactivate IME
8619     Session session(engine_.get());
8620     session.SetConfig(&config);
8621     InitSessionToPrecomposition(&session);
8622 
8623     commands::Command command;
8624 
8625     // "[MO]" is a converted string like Kanji.
8626     // "MO" is an input string like Hiragana.
8627     SetupCommandForReverseConversion("[MO]", command.mutable_input());
8628     SetupMockForReverseConversion("[MO]", "MO");
8629     EXPECT_TRUE(session.SendCommand(&command));
8630 
8631     command.Clear();
8632     GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
8633     session.ConvertCancel(&command);
8634     ASSERT_TRUE(command.output().has_candidates());
8635     EXPECT_EQ(2, command.output().candidates().candidate_size());
8636     EXPECT_EQ("MOCHA", command.output().candidates().candidate(0).value());
8637 
8638     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8639     EXPECT_TRUE(command.output().consumed());
8640 
8641     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8642     EXPECT_TRUE(command.output().consumed());
8643     EXPECT_EQ("", GetComposition(command));
8644     EXPECT_EQ(0, command.output().candidates().candidate_size());
8645     EXPECT_RESULT("[MO]", command);
8646     ASSERT_TRUE(command.output().has_status());
8647     EXPECT_FALSE(command.output().status().activated());
8648   }
8649 }
8650 
8651 // TODO(matsuzakit): Update the expected result when b/5955618 is fixed.
TEST_F(SessionTest,CancelInPasswordMode_Issue5955618)8652 TEST_F(SessionTest, CancelInPasswordMode_Issue5955618) {
8653   config::Config config;
8654   {
8655     const string custom_keymap_table =
8656         "status\tkey\tcommand\n"
8657         "Precomposition\tESC\tCancel\n"
8658         "Composition\tESC\tCancel\n"
8659         "Conversion\tESC\tCancel\n";
8660     config.set_session_keymap(config::Config::CUSTOM);
8661     config.set_custom_keymap_table(custom_keymap_table);
8662   }
8663   Segments segments_mo;
8664   {
8665     segments_mo.set_request_type(Segments::SUGGESTION);
8666     Segment *segment;
8667     segment = segments_mo.add_segment();
8668     segment->set_key("MO");
8669     segment->add_candidate()->value = "MOCHA";
8670     segment->add_candidate()->value = "MOZUKU";
8671   }
8672 
8673   {  // Cancel of Precomposition in password field
8674      // Basically this is unusual because there is no character to be canceled
8675      // when Precomposition state.
8676     Session session(engine_.get());
8677     session.SetConfig(&config);
8678     InitSessionToPrecomposition(&session);
8679     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8680 
8681     commands::Command command;
8682     EXPECT_TRUE(TestSendKey("ESC", &session, &command));
8683     EXPECT_TRUE(command.output().consumed());  // should be consumed, anyway.
8684 
8685     EXPECT_TRUE(SendKey("ESC", &session, &command));
8686     // This behavior is the bug of b/5955618.
8687     // The result of TestSendKey and SendKey should be the same in terms of
8688     // |consumed()|.
8689     EXPECT_FALSE(command.output().consumed())
8690         << "Congrats! b/5955618 seems to be fixed";
8691   }
8692 
8693   {  // Cancel of Composition in password field
8694     Session session(engine_.get());
8695     session.SetConfig(&config);
8696     InitSessionToPrecomposition(&session);
8697     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8698 
8699     commands::Command command;
8700     EXPECT_TRUE(TestSendKey("ESC", &session, &command));
8701     EXPECT_TRUE(command.output().consumed());
8702 
8703     EXPECT_TRUE(SendKey("ESC", &session, &command));
8704     // This behavior is the bug of b/5955618.
8705     // The result of TestSendKey and SendKey should be the same in terms of
8706     // |consumed()|.
8707     EXPECT_FALSE(command.output().consumed())
8708         << "Congrats! b/5955618 seems to be fixed";
8709   }
8710 
8711   {  // Cancel of Conversion in password field
8712     Session session(engine_.get());
8713     session.SetConfig(&config);
8714     InitSessionToConversionWithAiueo(&session);
8715     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8716 
8717     // Actualy this works well because Cancel command in conversion mode
8718     // is mapped into ConvertCancel not EditCancel.
8719     commands::Command command;
8720     EXPECT_TRUE(TestSendKey("ESC", &session, &command));
8721     EXPECT_TRUE(command.output().consumed());
8722     EXPECT_TRUE(SendKey("ESC", &session, &command));
8723     EXPECT_TRUE(command.output().consumed());
8724     EXPECT_FALSE(command.output().has_result());
8725 
8726     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8727   }
8728 
8729   {  // Cancel of Reverse conversion in password field
8730     Session session(engine_.get());
8731     session.SetConfig(&config);
8732     InitSessionToPrecomposition(&session);
8733     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8734 
8735     commands::Command command;
8736 
8737     // "[MO]" is a converted string like Kanji.
8738     // "MO" is an input string like Hiragana.
8739     SetupCommandForReverseConversion("[MO]", command.mutable_input());
8740     SetupMockForReverseConversion("[MO]", "MO");
8741     EXPECT_TRUE(session.SendCommand(&command));
8742 
8743     // Actualy this works well because Cancel command in conversion mode
8744     // is mapped into ConvertCancel not EditCancel.
8745     EXPECT_TRUE(TestSendKey("ESC", &session, &command));
8746     EXPECT_TRUE(command.output().consumed());
8747     EXPECT_TRUE(SendKey("ESC", &session, &command));
8748     EXPECT_TRUE(command.output().consumed());
8749     EXPECT_FALSE(command.output().has_result());
8750     EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8751 
8752     // The second escape key will be mapped into EditCancel.
8753     EXPECT_TRUE(TestSendKey("ESC", &session, &command));
8754     EXPECT_TRUE(command.output().consumed());
8755     EXPECT_TRUE(SendKey("ESC", &session, &command));
8756     // This behavior is the bug of b/5955618.
8757     EXPECT_FALSE(command.output().consumed())
8758         << "Congrats! b/5955618 seems to be fixed";
8759     EXPECT_RESULT("[MO]", command);
8760   }
8761 }
8762 
8763 // TODO(matsuzakit): Update the expected result when b/5955618 is fixed.
TEST_F(SessionTest,CancelAndIMEOffInPasswordMode_Issue5955618)8764 TEST_F(SessionTest, CancelAndIMEOffInPasswordMode_Issue5955618) {
8765   config::Config config;
8766   {
8767     const string custom_keymap_table =
8768         "status\tkey\tcommand\n"
8769         "Precomposition\thankaku/zenkaku\tCancelAndIMEOff\n"
8770         "Composition\thankaku/zenkaku\tCancelAndIMEOff\n"
8771         "Conversion\thankaku/zenkaku\tCancelAndIMEOff\n";
8772     config.set_session_keymap(config::Config::CUSTOM);
8773     config.set_custom_keymap_table(custom_keymap_table);
8774   }
8775   Segments segments_mo;
8776   {
8777     segments_mo.set_request_type(Segments::SUGGESTION);
8778     Segment *segment;
8779     segment = segments_mo.add_segment();
8780     segment->set_key("MO");
8781     segment->add_candidate()->value = "MOCHA";
8782     segment->add_candidate()->value = "MOZUKU";
8783   }
8784 
8785   {  // Cancel of Precomposition and deactivate IME in password field.
8786     Session session(engine_.get());
8787     session.SetConfig(&config);
8788     InitSessionToPrecomposition(&session);
8789     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8790 
8791     commands::Command command;
8792     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8793     EXPECT_TRUE(command.output().consumed());
8794 
8795     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8796     // This behavior is the bug of b/5955618.
8797     // The result of TestSendKey and SendKey should be the same in terms of
8798     // |consumed()|.
8799     EXPECT_FALSE(command.output().consumed())
8800         << "Congrats! b/5955618 seems to be fixed";
8801     EXPECT_EQ("", GetComposition(command));
8802     EXPECT_EQ(0, command.output().candidates().candidate_size());
8803     EXPECT_FALSE(command.output().has_result());
8804     // Current behavior seems to be a bug.
8805     // This command should deactivate the IME.
8806     ASSERT_FALSE(command.output().has_status())
8807         << "Congrats! b/5955618 seems to be fixed.";
8808     // Ideally the following condition should be satisfied.
8809     // EXPECT_FALSE(command.output().status().activated());
8810   }
8811 
8812   {  // Cancel of Composition and deactivate IME in password field
8813     Session session(engine_.get());
8814     session.SetConfig(&config);
8815     InitSessionToPrecomposition(&session);
8816     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8817 
8818     commands::Command command;
8819     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8820     EXPECT_TRUE(command.output().consumed());
8821 
8822     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8823     // This behavior is the bug of b/5955618.
8824     // The result of TestSendKey and SendKey should be the same in terms of
8825     // |consumed()|.
8826     EXPECT_FALSE(command.output().consumed())
8827         << "Congrats! b/5955618 seems to be fixed";
8828     EXPECT_EQ("", GetComposition(command));
8829     EXPECT_EQ(0, command.output().candidates().candidate_size());
8830     EXPECT_FALSE(command.output().has_result());
8831     // Following behavior seems to be a bug.
8832     // This command should deactivate the IME.
8833     ASSERT_FALSE(command.output().has_status())
8834         << "Congrats! b/5955618 seems to be fixed.";
8835     // Ideally the following condition should be satisfied.
8836     // EXPECT_FALSE(command.output().status().activated());
8837   }
8838 
8839   {  // Cancel of Conversion and deactivate IME in password field
8840     Session session(engine_.get());
8841     session.SetConfig(&config);
8842     InitSessionToConversionWithAiueo(&session);
8843     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8844 
8845     commands::Command command;
8846     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8847     EXPECT_TRUE(command.output().consumed());
8848     command.Clear();
8849     // This behavior is the bug of b/5955618.
8850     // The result of TestSendKey and SendKey should be the same in terms of
8851     // |consumed()|.
8852     EXPECT_FALSE(command.output().consumed())
8853         << "Congrats! b/5955618 seems to be fixed";
8854     EXPECT_EQ("", GetComposition(command));
8855     EXPECT_EQ(0, command.output().candidates().candidate_size());
8856     EXPECT_FALSE(command.output().has_result());
8857     // Following behavior seems to be a bug.
8858     // This command should deactivate the IME.
8859     ASSERT_FALSE(command.output().has_status())
8860         << "Congrats! b/5955618 seems to be fixed.";
8861     // Ideally the following condition should be satisfied.
8862     // EXPECT_FALSE(command.output().status().activated());
8863   }
8864 
8865   {  // Cancel of Reverse conversion and deactivate IME in password field
8866     Session session(engine_.get());
8867     session.SetConfig(&config);
8868     InitSessionToPrecomposition(&session);
8869     SwitchInputFieldType(commands::Context::PASSWORD, &session);
8870 
8871     commands::Command command;
8872 
8873     // "[MO]" is a converted string like Kanji.
8874     // "MO" is an input string like Hiragana.
8875     SetupCommandForReverseConversion("[MO]", command.mutable_input());
8876     SetupMockForReverseConversion("[MO]", "MO");
8877     EXPECT_TRUE(session.SendCommand(&command));
8878 
8879     EXPECT_TRUE(TestSendKey("hankaku/zenkaku", &session, &command));
8880     EXPECT_TRUE(command.output().consumed());
8881     EXPECT_TRUE(SendKey("hankaku/zenkaku", &session, &command));
8882     // This behavior is the bug of b/5955618.
8883     // The result of TestSendKey and SendKey should be the same in terms of
8884     // |consumed()|.
8885     EXPECT_FALSE(command.output().consumed())
8886         << "Congrats! b/5955618 seems to be fixed";
8887     EXPECT_RESULT("[MO]", command);
8888     ASSERT_TRUE(command.output().has_status());
8889     // This behavior is the bug of b/5955618. IME should be deactivated.
8890     EXPECT_TRUE(command.output().status().activated())
8891         << "Congrats! b/5955618 seems to be fixed";
8892   }
8893 }
8894 
TEST_F(SessionTest,DoNothingOnCompositionKeepingSuggestWindow)8895 TEST_F(SessionTest, DoNothingOnCompositionKeepingSuggestWindow) {
8896   Session session(engine_.get());
8897   InitSessionToPrecomposition(&session);
8898 
8899   Segments segments_mo;
8900   {
8901     segments_mo.set_request_type(Segments::SUGGESTION);
8902     Segment *segment;
8903     segment = segments_mo.add_segment();
8904     segment->set_key("MO");
8905     segment->add_candidate()->value = "MOCHA";
8906     segment->add_candidate()->value = "MOZUKU";
8907   }
8908   GetConverterMock()->SetStartSuggestionForRequest(&segments_mo, true);
8909 
8910   commands::Command command;
8911   SendKey("M", &session, &command);
8912   EXPECT_TRUE(command.output().has_candidates());
8913 
8914   SendKey("Ctrl", &session, &command);
8915   EXPECT_TRUE(command.output().has_candidates());
8916 }
8917 
TEST_F(SessionTest,ModeChangeOfConvertAtPunctuations)8918 TEST_F(SessionTest, ModeChangeOfConvertAtPunctuations) {
8919   config::Config config;
8920   config.set_use_auto_conversion(true);
8921 
8922   Session session(engine_.get());
8923   session.SetConfig(&config);
8924   InitSessionToPrecomposition(&session);
8925 
8926   Segments segments_a_conv;
8927   {
8928     segments_a_conv.set_request_type(Segments::CONVERSION);
8929     Segment *segment;
8930     segment = segments_a_conv.add_segment();
8931     segment->set_key("あ");
8932     segment->add_candidate()->value = "あ";
8933   }
8934   GetConverterMock()->SetStartConversionForRequest(&segments_a_conv, true);
8935 
8936   commands::Command command;
8937   SendKey("a", &session, &command);     // "あ|" (composition)
8938   EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8939 
8940   SendKey(".", &session, &command);     // "あ。|" (conversion)
8941   EXPECT_EQ(ImeContext::CONVERSION, session.context().state());
8942 
8943   SendKey("ESC", &session, &command);   // "あ。|" (composition)
8944   EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8945 
8946   SendKey("Left", &session, &command);  // "あ|。" (composition)
8947   EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8948 
8949   SendKey("i", &session, &command);     // "あい|。" (should be composition)
8950   EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
8951 }
8952 
TEST_F(SessionTest,SuppressSuggestion)8953 TEST_F(SessionTest, SuppressSuggestion) {
8954   Session session(mock_data_engine_.get());
8955   InitSessionToPrecomposition(&session);
8956 
8957   commands::Command command;
8958   SendKey("a", &session, &command);
8959   EXPECT_TRUE(command.output().has_candidates());
8960 
8961   command.Clear();
8962   session.EditCancel(&command);
8963   EXPECT_FALSE(command.output().has_candidates());
8964 
8965   // Default behavior.
8966   SendKey("d", &session, &command);
8967   EXPECT_TRUE(command.output().has_candidates());
8968 
8969   // With an invalid identifer.  It should be the same with the
8970   // default behavior.
8971   SetSendKeyCommand("i", &command);
8972   command.mutable_input()->mutable_context()->add_experimental_features(
8973       "invalid_identifier");
8974   session.SendKey(&command);
8975   EXPECT_TRUE(command.output().has_candidates());
8976 
8977 }
8978 
TEST_F(SessionTest,DeleteHistory)8979 TEST_F(SessionTest, DeleteHistory) {
8980   std::unique_ptr<Session> session(new Session(engine_.get()));
8981   InitSessionToPrecomposition(session.get());
8982 
8983   Segments segments;
8984   Segment *segment = segments.add_segment();
8985   segment->set_key("delete");
8986   segment->add_candidate()->value = "DeleteHistory";
8987   ConversionRequest request;
8988   SetComposer(session.get(), &request);
8989   GetConverterMock()->SetStartPredictionForRequest(&segments, true);
8990 
8991   // Type "del". Preedit = "でl".
8992   commands::Command command;
8993   EXPECT_TRUE(SendKey("d", session.get(), &command));
8994   EXPECT_TRUE(SendKey("e", session.get(), &command));
8995   EXPECT_TRUE(SendKey("l", session.get(), &command));
8996   EXPECT_PREEDIT("でl", command);
8997 
8998   // Start prediction. Preedit = "DeleteHistory".
8999   command.Clear();
9000   EXPECT_TRUE(session->PredictAndConvert(&command));
9001   EXPECT_TRUE(command.output().has_candidates());
9002   EXPECT_EQ(ImeContext::CONVERSION, session->context().state());
9003   EXPECT_PREEDIT("DeleteHistory", command);
9004 
9005   // Do DeleteHistory command. After that, the session should be back in
9006   // composition state and preedit gets back to "でl" again.
9007   EXPECT_TRUE(SendKey("Ctrl Delete", session.get(), &command));
9008   EXPECT_EQ(ImeContext::COMPOSITION, session->context().state());
9009   EXPECT_PREEDIT("でl", command);
9010 }
9011 
TEST_F(SessionTest,SendKeyWithKeyString_Direct)9012 TEST_F(SessionTest, SendKeyWithKeyString_Direct) {
9013   Session session(engine_.get());
9014   InitSessionToDirect(&session);
9015 
9016   commands::Command command;
9017   const char kZa[] = "ざ";
9018   SetSendKeyCommandWithKeyString(kZa, &command);
9019   EXPECT_TRUE(session.TestSendKey(&command));
9020   EXPECT_FALSE(command.output().consumed());
9021   command.mutable_output()->Clear();
9022   EXPECT_TRUE(session.SendKey(&command));
9023   EXPECT_FALSE(command.output().consumed());
9024 }
9025 
TEST_F(SessionTest,SendKeyWithKeyString)9026 TEST_F(SessionTest, SendKeyWithKeyString) {
9027   Session session(engine_.get());
9028   InitSessionToPrecomposition(&session);
9029 
9030   commands::Command command;
9031 
9032   // Test for precomposition state.
9033   EXPECT_EQ(ImeContext::PRECOMPOSITION, session.context().state());
9034   const char kZa[] = "ざ";
9035   SetSendKeyCommandWithKeyString(kZa, &command);
9036   EXPECT_TRUE(session.TestSendKey(&command));
9037   EXPECT_TRUE(command.output().consumed());
9038   command.mutable_output()->Clear();
9039   EXPECT_TRUE(session.SendKey(&command));
9040   EXPECT_TRUE(command.output().consumed());
9041   EXPECT_PREEDIT(kZa, command);
9042 
9043   command.Clear();
9044 
9045   // Test for composition state.
9046   EXPECT_EQ(ImeContext::COMPOSITION, session.context().state());
9047   const char kOnsenManju[] = "♨饅頭";
9048   SetSendKeyCommandWithKeyString(kOnsenManju, &command);
9049   EXPECT_TRUE(session.TestSendKey(&command));
9050   EXPECT_TRUE(command.output().consumed());
9051   command.mutable_output()->Clear();
9052   EXPECT_TRUE(session.SendKey(&command));
9053   EXPECT_TRUE(command.output().consumed());
9054   EXPECT_PREEDIT(string(kZa) + kOnsenManju, command);
9055 }
9056 
TEST_F(SessionTest,IndirectImeOnOff)9057 TEST_F(SessionTest, IndirectImeOnOff) {
9058   std::unique_ptr<Session> session(new Session(engine_.get()));
9059   InitSessionToPrecomposition(session.get());
9060 
9061   {
9062     commands::Command command;
9063     // IMEOff
9064     SendSpecialKey(commands::KeyEvent::OFF, session.get(), &command);
9065   }
9066   {
9067     commands::Command command;
9068     // 'a'
9069     TestSendKeyWithModeAndActivated(
9070         "a", true, commands::HIRAGANA, session.get(), &command);
9071     EXPECT_TRUE(command.output().consumed());
9072   }
9073   {
9074     commands::Command command;
9075     // 'a'
9076     SendKeyWithModeAndActivated(
9077         "a", true, commands::HIRAGANA, session.get(), &command);
9078     EXPECT_TRUE(command.output().consumed());
9079     EXPECT_TRUE(command.output().has_status());
9080     EXPECT_TRUE(command.output().status().activated())
9081         << "Should be activated.";
9082   }
9083   {
9084     commands::Command command;
9085     // 'a'
9086     TestSendKeyWithModeAndActivated(
9087         "a", false, commands::HIRAGANA, session.get(), &command);
9088     EXPECT_FALSE(command.output().consumed());
9089   }
9090   {
9091     commands::Command command;
9092     // 'a'
9093     SendKeyWithModeAndActivated(
9094         "a", false, commands::HIRAGANA, session.get(), &command);
9095     EXPECT_FALSE(command.output().consumed());
9096     EXPECT_FALSE(command.output().has_result())
9097         << "Indirect IME off flushes ongoing composition";
9098     EXPECT_TRUE(command.output().has_status());
9099     EXPECT_FALSE(command.output().status().activated())
9100         << "Should be inactivated.";
9101   }
9102 }
9103 
TEST_F(SessionTest,MakeSureIMEOn)9104 TEST_F(SessionTest, MakeSureIMEOn) {
9105   std::unique_ptr<Session> session(new Session(engine_.get()));
9106   InitSessionToDirect(session.get());
9107 
9108   {
9109     commands::Command command;
9110     SetSendCommandCommand(commands::SessionCommand::TURN_ON_IME, &command);
9111 
9112     ASSERT_TRUE(session->SendCommand(&command));
9113     EXPECT_TRUE(command.output().consumed());
9114     ASSERT_TRUE(command.output().has_status());
9115     EXPECT_TRUE(command.output().status().activated());
9116   }
9117 
9118   {
9119     // Make sure we can change the input mode.
9120     commands::Command command;
9121     SetSendCommandCommand(commands::SessionCommand::TURN_ON_IME, &command);
9122     command.mutable_input()->mutable_command()->set_composition_mode(
9123         commands::FULL_KATAKANA);
9124 
9125     ASSERT_TRUE(session->SendCommand(&command));
9126     EXPECT_TRUE(command.output().consumed());
9127     ASSERT_TRUE(command.output().has_status());
9128     EXPECT_TRUE(command.output().status().activated());
9129     EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
9130   }
9131 
9132   {
9133     // Make sure we can change the input mode again.
9134     commands::Command command;
9135     SetSendCommandCommand(commands::SessionCommand::TURN_ON_IME, &command);
9136     command.mutable_input()->mutable_command()->set_composition_mode(
9137         commands::HIRAGANA);
9138 
9139     ASSERT_TRUE(session->SendCommand(&command));
9140     EXPECT_TRUE(command.output().consumed());
9141     ASSERT_TRUE(command.output().has_status());
9142     EXPECT_TRUE(command.output().status().activated());
9143     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
9144   }
9145 
9146   {
9147     // commands::DIRECT is not supported for the composition_mode.
9148     commands::Command command;
9149     SetSendCommandCommand(commands::SessionCommand::TURN_ON_IME, &command);
9150     command.mutable_input()->mutable_command()->set_composition_mode(
9151         commands::DIRECT);
9152     EXPECT_FALSE(session->SendCommand(&command));
9153   }
9154 }
9155 
TEST_F(SessionTest,MakeSureIMEOff)9156 TEST_F(SessionTest, MakeSureIMEOff) {
9157   std::unique_ptr<Session> session(new Session(engine_.get()));
9158   InitSessionToPrecomposition(session.get());
9159 
9160   {
9161     commands::Command command;
9162     SetSendCommandCommand(commands::SessionCommand::TURN_OFF_IME, &command);
9163 
9164     ASSERT_TRUE(session->SendCommand(&command));
9165     EXPECT_TRUE(command.output().consumed());
9166     ASSERT_TRUE(command.output().has_status());
9167     EXPECT_FALSE(command.output().status().activated());
9168   }
9169 
9170   {
9171     // Make sure we can change the input mode.
9172     commands::Command command;
9173     SetSendCommandCommand(commands::SessionCommand::TURN_OFF_IME, &command);
9174     command.mutable_input()->mutable_command()->set_composition_mode(
9175         commands::FULL_KATAKANA);
9176 
9177     ASSERT_TRUE(session->SendCommand(&command));
9178     EXPECT_TRUE(command.output().consumed());
9179     ASSERT_TRUE(command.output().has_status());
9180     EXPECT_FALSE(command.output().status().activated());
9181     EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
9182   }
9183 
9184   {
9185     // Make sure we can change the input mode again.
9186     commands::Command command;
9187     SetSendCommandCommand(commands::SessionCommand::TURN_OFF_IME, &command);
9188     command.mutable_input()->mutable_command()->set_composition_mode(
9189         commands::HIRAGANA);
9190 
9191     ASSERT_TRUE(session->SendCommand(&command));
9192     EXPECT_TRUE(command.output().consumed());
9193     ASSERT_TRUE(command.output().has_status());
9194     EXPECT_FALSE(command.output().status().activated());
9195     EXPECT_EQ(commands::HIRAGANA, command.output().status().mode());
9196   }
9197 
9198   {
9199     // commands::DIRECT is not supported for the composition_mode.
9200     commands::Command command;
9201     SetSendCommandCommand(commands::SessionCommand::TURN_OFF_IME, &command);
9202     command.mutable_input()->mutable_command()->set_composition_mode(
9203         commands::DIRECT);
9204     EXPECT_FALSE(session->SendCommand(&command));
9205   }
9206 
9207   {
9208     // Make sure SessionCommand::TURN_OFF_IME terminates the existing
9209     // composition.
9210 
9211     InitSessionToPrecomposition(session.get());
9212 
9213     // Set up converter.
9214     {
9215       commands::Command command;
9216 
9217       Segments segments;
9218       InsertCharacterChars("aiueo", session.get(), &command);
9219       ConversionRequest request;
9220       SetComposer(session.get(), &request);
9221       SetAiueo(&segments);
9222       FillT13Ns(request, &segments);
9223       GetConverterMock()->SetCommitSegmentValue(&segments, true);
9224     }
9225 
9226     // Send SessionCommand::TURN_OFF_IME to commit composition.
9227     {
9228       commands::Command command;
9229       SetSendCommandCommand(commands::SessionCommand::TURN_OFF_IME, &command);
9230       command.mutable_input()->mutable_command()->set_composition_mode(
9231           commands::FULL_KATAKANA);
9232       ASSERT_TRUE(session->SendCommand(&command));
9233       EXPECT_RESULT("あいうえお", command);
9234       EXPECT_TRUE(command.output().consumed());
9235       ASSERT_TRUE(command.output().has_status());
9236       EXPECT_FALSE(command.output().status().activated());
9237       EXPECT_EQ(commands::FULL_KATAKANA, command.output().status().mode());
9238     }
9239   }
9240 }
9241 
9242 }  // namespace session
9243 }  // namespace mozc
9244