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