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 "composer/table.h"
31
32 #include "base/file_util.h"
33 #include "base/port.h"
34 #include "base/system_util.h"
35 #include "composer/internal/composition_input.h"
36 #include "config/config_handler.h"
37 #include "data_manager/testing/mock_data_manager.h"
38 #include "protocol/commands.pb.h"
39 #include "protocol/config.pb.h"
40 #include "testing/base/public/gunit.h"
41
42 DECLARE_string(test_tmpdir);
43
44 namespace mozc {
45 namespace composer {
46
47 using mozc::config::Config;
48 using mozc::commands::Request;
49
InitTable(Table * table)50 static void InitTable(Table* table) {
51 table->AddRule("a", "あ", "");
52 table->AddRule("i", "い", "");
53 table->AddRule("ka", "か", "");
54 table->AddRule("ki", "き", "");
55 table->AddRule("ku", "く", "");
56 table->AddRule("ke", "け", "");
57 table->AddRule("ko", "こ", "");
58 table->AddRule("kk", "っ", "k");
59 table->AddRule("na", "な", "");
60 table->AddRule("ni", "に", "");
61 table->AddRule("n", "ん", "");
62 table->AddRule("nn", "ん", "");
63 }
64
GetResult(const Table & table,const string & key)65 string GetResult(const Table &table, const string &key) {
66 const Entry *entry = table.LookUp(key);
67 if (entry == NULL) {
68 return "<NULL>";
69 }
70 return entry->result();
71 }
72
GetInput(const Table & table,const string & key)73 string GetInput(const Table &table, const string &key) {
74 const Entry *entry = table.LookUp(key);
75 if (entry == NULL) {
76 return "<NULL>";
77 }
78 return entry->input();
79 }
80
81 class TableTest : public ::testing::Test {
82 protected:
83 TableTest() = default;
84 ~TableTest() override = default;
85
SetUp()86 void SetUp() override {
87 config::ConfigHandler::GetDefaultConfig(&config_);
88 }
89
90 const testing::MockDataManager mock_data_manager_;
91 config::Config config_;
92
93 private:
94 DISALLOW_COPY_AND_ASSIGN(TableTest);
95 };
96
TEST_F(TableTest,LookUp)97 TEST_F(TableTest, LookUp) {
98 static const struct TestCase {
99 const char* input;
100 const bool expected_result;
101 const char* expected_output;
102 const char* expected_pending;
103 } test_cases[] = {
104 { "a", true, "あ", "" },
105 { "k", false, "", "" },
106 { "ka", true, "か", "" },
107 { "ki", true, "き", "" },
108 { "ku", true, "く", "" },
109 { "kk", true, "っ", "k" },
110 { "aka", false, "", "" },
111 { "na", true, "な", "" },
112 { "n", true, "ん", "" },
113 { "nn", true, "ん", "" },
114 };
115 static const int size = arraysize(test_cases);
116
117 Table table;
118 InitTable(&table);
119
120 for (int i = 0; i < size; ++i) {
121 const TestCase& test = test_cases[i];
122 string output;
123 string pending;
124 const Entry* entry;
125 entry = table.LookUp(test.input);
126
127 EXPECT_EQ(test.expected_result, (entry != NULL));
128 if (entry == NULL) {
129 continue;
130 }
131 EXPECT_EQ(test.expected_output, entry->result());
132 EXPECT_EQ(test.expected_pending, entry->pending());
133 }
134 }
135
TEST_F(TableTest,LookUpPredictiveAll)136 TEST_F(TableTest, LookUpPredictiveAll) {
137 Table table;
138 InitTable(&table);
139
140 std::vector<const Entry *> results;
141 table.LookUpPredictiveAll("k", &results);
142
143 EXPECT_EQ(6, results.size());
144 }
145
TEST_F(TableTest,Punctuations)146 TEST_F(TableTest, Punctuations) {
147 static const struct TestCase {
148 config::Config::PunctuationMethod method;
149 const char *input;
150 const char *expected;
151 } test_cases[] = {
152 { config::Config::KUTEN_TOUTEN, ",", "、" },
153 { config::Config::KUTEN_TOUTEN, ".", "。" },
154 { config::Config::COMMA_PERIOD, ",", "," },
155 { config::Config::COMMA_PERIOD, ".", "." },
156 { config::Config::KUTEN_PERIOD, ",", "、" },
157 { config::Config::KUTEN_PERIOD, ".", "." },
158 { config::Config::COMMA_TOUTEN, ",", "," },
159 { config::Config::COMMA_TOUTEN, ".", "。" },
160 };
161
162 commands::Request request;
163
164 for (int i = 0; i < arraysize(test_cases); ++i) {
165 config::Config config;
166 config.set_punctuation_method(test_cases[i].method);
167 Table table;
168 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config,
169 mock_data_manager_));
170 const Entry *entry = table.LookUp(test_cases[i].input);
171 EXPECT_TRUE(entry != NULL) << "Failed index = " << i;
172 if (entry) {
173 EXPECT_EQ(test_cases[i].expected, entry->result());
174 }
175 }
176 }
177
TEST_F(TableTest,Symbols)178 TEST_F(TableTest, Symbols) {
179 static const struct TestCase {
180 config::Config::SymbolMethod method;
181 const char *input;
182 const char *expected;
183 } test_cases[] = {
184 { config::Config::CORNER_BRACKET_MIDDLE_DOT, "[", "「" },
185 { config::Config::CORNER_BRACKET_MIDDLE_DOT, "]", "」" },
186 { config::Config::CORNER_BRACKET_MIDDLE_DOT, "/", "・" },
187 { config::Config::SQUARE_BRACKET_SLASH, "[", "[" },
188 { config::Config::SQUARE_BRACKET_SLASH, "]", "]" },
189 { config::Config::SQUARE_BRACKET_SLASH, "/", "/" },
190 { config::Config::CORNER_BRACKET_SLASH, "[", "「" },
191 { config::Config::CORNER_BRACKET_SLASH, "]", "」" },
192 { config::Config::CORNER_BRACKET_SLASH, "/", "/" },
193 { config::Config::SQUARE_BRACKET_MIDDLE_DOT, "[", "[" },
194 { config::Config::SQUARE_BRACKET_MIDDLE_DOT, "]", "]" },
195 { config::Config::SQUARE_BRACKET_MIDDLE_DOT, "/", "・" },
196 };
197
198 commands::Request request;
199
200 for (int i = 0; i < arraysize(test_cases); ++i) {
201 config::Config config;
202 config.set_symbol_method(test_cases[i].method);
203 Table table;
204 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config,
205 mock_data_manager_));
206 const Entry *entry = table.LookUp(test_cases[i].input);
207 EXPECT_TRUE(entry != NULL) << "Failed index = " << i;
208 if (entry) {
209 EXPECT_EQ(test_cases[i].expected, entry->result());
210 }
211 }
212 }
213
TEST_F(TableTest,KanaSuppressed)214 TEST_F(TableTest, KanaSuppressed) {
215 config_.set_preedit_method(config::Config::KANA);
216
217 commands::Request request;
218
219 Table table;
220 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config_,
221 mock_data_manager_));
222
223 const Entry *entry = table.LookUp("a");
224 ASSERT_TRUE(entry != NULL);
225 EXPECT_EQ("あ", entry->result());
226 EXPECT_TRUE(entry->pending().empty());
227 }
228
TEST_F(TableTest,KanaCombination)229 TEST_F(TableTest, KanaCombination) {
230 Table table;
231 commands::Request request;
232 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config_,
233 mock_data_manager_));
234 const Entry *entry = table.LookUp("か゛");
235 ASSERT_TRUE(entry != NULL);
236 EXPECT_EQ("が", entry->result());
237 EXPECT_TRUE(entry->pending().empty());
238 }
239
TEST_F(TableTest,InvalidEntryTest)240 TEST_F(TableTest, InvalidEntryTest) {
241 {
242 Table table;
243 EXPECT_FALSE(table.IsLoopingEntry("a", "b"));
244 table.AddRule("a", "aa", "b");
245
246 EXPECT_TRUE(table.IsLoopingEntry("b", "a"));
247 table.AddRule("b", "aa", "a"); // looping
248
249 EXPECT_TRUE(table.LookUp("a") != NULL);
250 EXPECT_TRUE(table.LookUp("b") == NULL);
251 }
252
253 {
254 Table table;
255 EXPECT_FALSE(table.IsLoopingEntry("a", "ba"));
256 table.AddRule("a", "aa", "ba");
257
258 EXPECT_TRUE(table.IsLoopingEntry("b", "a"));
259 table.AddRule("b", "aa", "a"); // looping
260
261 EXPECT_TRUE(table.LookUp("a") != NULL);
262 EXPECT_TRUE(table.LookUp("b") == NULL);
263 }
264
265 {
266 Table table;
267 EXPECT_FALSE(table.IsLoopingEntry("a", "b"));
268 table.AddRule("a", "aa", "b");
269
270 EXPECT_FALSE(table.IsLoopingEntry("b", "c"));
271 table.AddRule("b", "aa", "c");
272
273 EXPECT_FALSE(table.IsLoopingEntry("c", "d"));
274 table.AddRule("c", "aa", "d");
275
276 EXPECT_TRUE(table.IsLoopingEntry("d", "a"));
277 table.AddRule("d", "aa", "a"); // looping
278
279 EXPECT_TRUE(table.LookUp("a") != NULL);
280 EXPECT_TRUE(table.LookUp("b") != NULL);
281 EXPECT_TRUE(table.LookUp("c") != NULL);
282 EXPECT_TRUE(table.LookUp("d") == NULL);
283 }
284
285 {
286 Table table;
287 table.AddRule("wa", "WA", "");
288 table.AddRule("ww", "X", "w");
289
290 EXPECT_FALSE(table.IsLoopingEntry("www", "ww"));
291 table.AddRule("www", "W", "ww"); // not looping
292
293 EXPECT_TRUE(table.LookUp("wa") != NULL);
294 EXPECT_TRUE(table.LookUp("ww") != NULL);
295 EXPECT_TRUE(table.LookUp("www") != NULL);
296 }
297
298 {
299 Table table;
300 table.AddRule("wa", "WA", "");
301 table.AddRule("www", "W", "ww");
302
303 EXPECT_FALSE(table.IsLoopingEntry("ww", "w"));
304 table.AddRule("ww", "X", "w");
305
306 EXPECT_TRUE(table.LookUp("wa") != NULL);
307 EXPECT_TRUE(table.LookUp("ww") != NULL);
308 EXPECT_TRUE(table.LookUp("www") != NULL);
309 }
310
311 {
312 Table table;
313 EXPECT_TRUE(table.IsLoopingEntry("a", "a"));
314 table.AddRule("a", "aa", "a"); // looping
315
316 EXPECT_TRUE(table.LookUp("a") == NULL);
317 }
318
319 // Too long input
320 {
321 Table table;
322 string too_long;
323 // Maximum size is 300 now.
324 for (int i = 0; i < 1024; ++i) {
325 too_long += 'a';
326 }
327 table.AddRule(too_long, "test", "test");
328 EXPECT_TRUE(table.LookUp(too_long) == NULL);
329
330 table.AddRule("a", too_long, "test");
331 EXPECT_TRUE(table.LookUp("a") == NULL);
332
333 table.AddRule("a", "test", too_long);
334 EXPECT_TRUE(table.LookUp("a") == NULL);
335 }
336
337 // reasonably long
338 {
339 Table table;
340 string reasonably_long;
341 // Maximum size is 300 now.
342 for (int i = 0; i < 200; ++i) {
343 reasonably_long += 'a';
344 }
345 table.AddRule(reasonably_long, "test", "test");
346 EXPECT_TRUE(table.LookUp(reasonably_long) != NULL);
347
348 table.AddRule("a", reasonably_long, "test");
349 EXPECT_TRUE(table.LookUp("a") != NULL);
350
351 table.AddRule("a", "test", reasonably_long);
352 EXPECT_TRUE(table.LookUp("a") != NULL);
353 }
354 }
355
TEST_F(TableTest,CustomPunctuationsAndSymbols)356 TEST_F(TableTest, CustomPunctuationsAndSymbols) {
357 // Test against Issue2465801.
358 string custom_roman_table;
359 custom_roman_table.append("mozc\tMOZC\n");
360 custom_roman_table.append(",\tCOMMA\n");
361 custom_roman_table.append(".\tPERIOD\n");
362 custom_roman_table.append("/\tSLASH\n");
363 custom_roman_table.append("[\tOPEN\n");
364 custom_roman_table.append("]\tCLOSE\n");
365
366 config_.set_custom_roman_table(custom_roman_table);
367
368 Table table;
369 commands::Request request;
370 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
371
372 const Entry *entry = NULL;
373 entry = table.LookUp("mozc");
374 ASSERT_TRUE(entry != NULL);
375 EXPECT_EQ("MOZC", entry->result());
376
377 entry = table.LookUp(",");
378 ASSERT_TRUE(entry != NULL);
379 EXPECT_EQ("COMMA", entry->result());
380
381 entry = table.LookUp(".");
382 ASSERT_TRUE(entry != NULL);
383 EXPECT_EQ("PERIOD", entry->result());
384
385 entry = table.LookUp("/");
386 ASSERT_TRUE(entry != NULL);
387 EXPECT_EQ("SLASH", entry->result());
388
389 entry = table.LookUp("[");
390 ASSERT_TRUE(entry != NULL);
391 EXPECT_EQ("OPEN", entry->result());
392
393 entry = table.LookUp("]");
394 ASSERT_TRUE(entry != NULL);
395 EXPECT_EQ("CLOSE", entry->result());
396 }
397
TEST_F(TableTest,CaseSensitive)398 TEST_F(TableTest, CaseSensitive) {
399 Table table;
400 table.AddRule("a", "[a]", "");
401 table.AddRule("A", "[A]", "");
402 table.AddRule("ba", "[ba]", "");
403 table.AddRule("BA", "[BA]", "");
404 table.AddRule("Ba", "[Ba]", "");
405 // The rule of "bA" is intentionally dropped.
406 // table.AddRule("bA", "[bA]", "");
407 table.AddRule("za", "[za]", "");
408
409 // case insensitive
410 table.set_case_sensitive(false);
411 EXPECT_EQ("[a]", GetResult(table, "a"));
412 EXPECT_EQ("[a]", GetResult(table, "A"));
413 EXPECT_EQ("[ba]", GetResult(table, "ba"));
414 EXPECT_EQ("[ba]", GetResult(table, "BA"));
415 EXPECT_EQ("[ba]", GetResult(table, "Ba"));
416 EXPECT_EQ("[ba]", GetResult(table, "bA"));
417
418 EXPECT_EQ("a", GetInput(table, "a"));
419 EXPECT_EQ("a", GetInput(table, "A"));
420 EXPECT_EQ("ba", GetInput(table, "ba"));
421 EXPECT_EQ("ba", GetInput(table, "BA"));
422 EXPECT_EQ("ba", GetInput(table, "Ba"));
423 EXPECT_EQ("ba", GetInput(table, "bA"));
424
425 // Test for HasSubRules
426 EXPECT_TRUE(table.HasSubRules("Z"));
427
428 { // Test for LookUpPrefix
429 const Entry *entry = NULL;
430 size_t key_length = 0;
431 bool fixed = false;
432 entry = table.LookUpPrefix("bA", &key_length, &fixed);
433 EXPECT_TRUE(entry != NULL);
434 EXPECT_EQ("[ba]", entry->result());
435 EXPECT_EQ(2, key_length);
436 EXPECT_TRUE(fixed);
437 }
438
439 // case sensitive
440 table.set_case_sensitive(true);
441 EXPECT_TRUE(table.case_sensitive());
442 EXPECT_EQ("[a]", GetResult(table, "a"));
443 EXPECT_EQ("[A]", GetResult(table, "A"));
444 EXPECT_EQ("[ba]", GetResult(table, "ba"));
445 EXPECT_EQ("[BA]", GetResult(table, "BA"));
446 EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
447 EXPECT_EQ("<NULL>", GetResult(table, "bA"));
448
449 EXPECT_EQ("a", GetInput(table, "a"));
450 EXPECT_EQ("A", GetInput(table, "A"));
451 EXPECT_EQ("ba", GetInput(table, "ba"));
452 EXPECT_EQ("BA", GetInput(table, "BA"));
453 EXPECT_EQ("Ba", GetInput(table, "Ba"));
454 EXPECT_EQ("<NULL>", GetInput(table, "bA"));
455
456 // Test for HasSubRules
457 EXPECT_FALSE(table.HasSubRules("Z"));
458
459 { // Test for LookUpPrefix
460 const Entry *entry = NULL;
461 size_t key_length = 0;
462 bool fixed = false;
463 entry = table.LookUpPrefix("bA", &key_length, &fixed);
464 EXPECT_TRUE(entry == NULL);
465 EXPECT_EQ(1, key_length);
466 EXPECT_TRUE(fixed);
467 }
468 }
469
TEST_F(TableTest,CaseSensitivity)470 TEST_F(TableTest, CaseSensitivity) {
471 commands::Request request;
472 {
473 Table table;
474 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
475 EXPECT_FALSE(table.case_sensitive());
476 }
477 {
478 Table table;
479 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
480 table.AddRule("", "", "");
481 EXPECT_FALSE(table.case_sensitive());
482 }
483 {
484 Table table;
485 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
486 table.AddRule("a", "", "");
487 EXPECT_FALSE(table.case_sensitive());
488 }
489 {
490 Table table;
491 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
492 table.AddRule("A", "", "");
493 EXPECT_TRUE(table.case_sensitive());
494 }
495 {
496 Table table;
497 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
498 table.AddRule("a{A}a", "", "");
499 EXPECT_FALSE(table.case_sensitive());
500 }
501 {
502 Table table;
503 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
504 table.AddRule("A{A}A", "", "");
505 EXPECT_TRUE(table.case_sensitive());
506 }
507 }
508
509 // This test case was needed because the case sensitivity was configured
510 // by the configuration.
511 // Currently the case sensitivity is independent from the configuration.
TEST_F(TableTest,CaseSensitiveByConfiguration)512 TEST_F(TableTest, CaseSensitiveByConfiguration) {
513 commands::Request request;
514 Table table;
515
516 // config::Config::OFF
517 {
518 config_.set_shift_key_mode_switch(config::Config::OFF);
519 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
520
521 table.AddRule("a", "[a]", "");
522 table.AddRule("A", "[A]", "");
523 table.AddRule("ba", "[ba]", "");
524 table.AddRule("BA", "[BA]", "");
525 table.AddRule("Ba", "[Ba]", "");
526
527 EXPECT_TRUE(table.case_sensitive());
528 EXPECT_EQ("[a]", GetResult(table, "a"));
529 EXPECT_EQ("[A]", GetResult(table, "A"));
530 EXPECT_EQ("[ba]", GetResult(table, "ba"));
531 EXPECT_EQ("[BA]", GetResult(table, "BA"));
532 EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
533 EXPECT_EQ("<NULL>", GetResult(table, "bA"));
534
535 EXPECT_EQ("a", GetInput(table, "a"));
536 EXPECT_EQ("A", GetInput(table, "A"));
537 EXPECT_EQ("ba", GetInput(table, "ba"));
538 EXPECT_EQ("BA", GetInput(table, "BA"));
539 EXPECT_EQ("Ba", GetInput(table, "Ba"));
540 EXPECT_EQ("<NULL>", GetInput(table, "bA"));
541
542 // Test for HasSubRules
543 EXPECT_FALSE(table.HasSubRules("Z"));
544
545 { // Test for LookUpPrefix
546 const Entry *entry = NULL;
547 size_t key_length = 0;
548 bool fixed = false;
549 entry = table.LookUpPrefix("bA", &key_length, &fixed);
550 EXPECT_TRUE(entry == NULL);
551 EXPECT_EQ(1, key_length);
552 EXPECT_TRUE(fixed);
553 }
554 }
555
556 // config::Config::ASCII_INPUT_MODE
557 {
558 config_.set_shift_key_mode_switch(config::Config::ASCII_INPUT_MODE);
559 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
560
561 table.AddRule("a", "[a]", "");
562 table.AddRule("A", "[A]", "");
563 table.AddRule("ba", "[ba]", "");
564 table.AddRule("BA", "[BA]", "");
565 table.AddRule("Ba", "[Ba]", "");
566
567 EXPECT_TRUE(table.case_sensitive());
568 EXPECT_EQ("[a]", GetResult(table, "a"));
569 EXPECT_EQ("[A]", GetResult(table, "A"));
570 EXPECT_EQ("[ba]", GetResult(table, "ba"));
571 EXPECT_EQ("[BA]", GetResult(table, "BA"));
572 EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
573 EXPECT_EQ("<NULL>", GetResult(table, "bA"));
574
575 EXPECT_EQ("a", GetInput(table, "a"));
576 EXPECT_EQ("A", GetInput(table, "A"));
577 EXPECT_EQ("ba", GetInput(table, "ba"));
578 EXPECT_EQ("BA", GetInput(table, "BA"));
579 EXPECT_EQ("Ba", GetInput(table, "Ba"));
580 EXPECT_EQ("<NULL>", GetInput(table, "bA"));
581
582 // Test for HasSubRules
583 EXPECT_FALSE(table.HasSubRules("Z"));
584
585 { // Test for LookUpPrefix
586 const Entry *entry = NULL;
587 size_t key_length = 0;
588 bool fixed = false;
589 entry = table.LookUpPrefix("bA", &key_length, &fixed);
590 EXPECT_TRUE(entry == NULL);
591 EXPECT_EQ(1, key_length);
592 EXPECT_TRUE(fixed);
593 }
594 }
595
596 // config::Config::KATAKANA_INPUT_MODE
597 {
598 config_.set_shift_key_mode_switch(config::Config::KATAKANA_INPUT_MODE);
599 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
600
601 table.AddRule("a", "[a]", "");
602 table.AddRule("A", "[A]", "");
603 table.AddRule("ba", "[ba]", "");
604 table.AddRule("BA", "[BA]", "");
605 table.AddRule("Ba", "[Ba]", "");
606
607 EXPECT_TRUE(table.case_sensitive());
608 EXPECT_EQ("[a]", GetResult(table, "a"));
609 EXPECT_EQ("[A]", GetResult(table, "A"));
610 EXPECT_EQ("[ba]", GetResult(table, "ba"));
611 EXPECT_EQ("[BA]", GetResult(table, "BA"));
612 EXPECT_EQ("[Ba]", GetResult(table, "Ba"));
613 EXPECT_EQ("<NULL>", GetResult(table, "bA"));
614
615 EXPECT_EQ("a", GetInput(table, "a"));
616 EXPECT_EQ("A", GetInput(table, "A"));
617 EXPECT_EQ("ba", GetInput(table, "ba"));
618 EXPECT_EQ("BA", GetInput(table, "BA"));
619 EXPECT_EQ("Ba", GetInput(table, "Ba"));
620 EXPECT_EQ("<NULL>", GetInput(table, "bA"));
621
622 // Test for HasSubRules
623 EXPECT_FALSE(table.HasSubRules("Z"));
624
625 { // Test for LookUpPrefix
626 const Entry *entry = NULL;
627 size_t key_length = 0;
628 bool fixed = false;
629 entry = table.LookUpPrefix("bA", &key_length, &fixed);
630 EXPECT_TRUE(entry == NULL);
631 EXPECT_EQ(1, key_length);
632 EXPECT_TRUE(fixed);
633 }
634 }
635 }
636
637 // Table class automatically enables case-sensitive mode when the given roman
638 // table has any input rule which contains one or more upper case characters.
639 // e.g. "V" -> "5" or "YT" -> "You there"
640 // This feature was implemented as b/2910223 as per following request.
641 // http://www.google.com/support/forum/p/ime/thread?tid=4ea9aed4ac8a2ba6&hl=ja
642 //
643 // The following test checks if a case-sensitive and a case-inensitive roman
644 // table enables and disables this "case-sensitive mode", respectively.
TEST_F(TableTest,AutomaticCaseSensitiveDetection)645 TEST_F(TableTest, AutomaticCaseSensitiveDetection) {
646 static const char kCaseInsensitiveRomanTable[] = {
647 "m\tmozc\n" // m -> mozc
648 "n\tnamazu\n" // n -> namazu
649 };
650 static const char kCaseSensitiveRomanTable[] = {
651 "m\tmozc\n" // m -> mozc
652 "M\tMozc\n" // M -> Mozc
653 };
654
655 commands::Request request;
656
657 {
658 Table table;
659 config::Config config(config::ConfigHandler::DefaultConfig());
660 config.set_custom_roman_table(kCaseSensitiveRomanTable);
661 EXPECT_FALSE(table.case_sensitive())
662 << "case-sensitive mode should be desabled by default.";
663 // Load a custom config with case-sensitive custom roman table.
664 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config,
665 mock_data_manager_));
666 EXPECT_TRUE(table.case_sensitive())
667 << "Case sensitive roman table should enable case-sensitive mode.";
668 // Explicitly disable case-sensitive mode.
669 table.set_case_sensitive(false);
670 ASSERT_FALSE(table.case_sensitive());
671 }
672
673 {
674 Table table;
675 // Load a custom config with case-insensitive custom roman table.
676 config::Config config(config::ConfigHandler::DefaultConfig());
677 config.set_custom_roman_table(kCaseInsensitiveRomanTable);
678 ASSERT_TRUE(table.InitializeWithRequestAndConfig(request, config,
679 mock_data_manager_));
680 EXPECT_FALSE(table.case_sensitive())
681 << "Case insensitive roman table should disable case-sensitive mode.";
682 // Explicitly enable case-sensitive mode.
683 table.set_case_sensitive(true);
684 ASSERT_TRUE(table.case_sensitive());
685 }
686 }
687
TEST_F(TableTest,MobileMode)688 TEST_F(TableTest, MobileMode) {
689 mozc::commands::Request request;
690 request.set_zero_query_suggestion(true);
691 request.set_mixed_conversion(true);
692
693 {
694 // To 12keys -> Hiragana mode
695 request.set_special_romanji_table(
696 mozc::commands::Request::TWELVE_KEYS_TO_HIRAGANA);
697 mozc::composer::Table table;
698 table.InitializeWithRequestAndConfig(request, config_,
699 mock_data_manager_);
700 {
701 const mozc::composer::Entry *entry = NULL;
702 size_t key_length = 0;
703 bool fixed = false;
704 entry = table.LookUpPrefix("2", &key_length, &fixed);
705 EXPECT_EQ("2", entry->input());
706 EXPECT_EQ("", entry->result());
707 EXPECT_EQ("か", entry->pending());
708 EXPECT_EQ(1, key_length);
709 EXPECT_TRUE(fixed);
710 }
711 {
712 const mozc::composer::Entry *entry = NULL;
713 size_t key_length = 0;
714 bool fixed = false;
715 entry = table.LookUpPrefix("し*", &key_length, &fixed);
716 EXPECT_EQ("し*", entry->input());
717 EXPECT_EQ("", entry->result());
718 // 0F and 0E are shift in/out characters.
719 EXPECT_EQ("\x0F*\x0Eじ", entry->pending());
720 EXPECT_EQ(4, key_length);
721 EXPECT_TRUE(fixed);
722 }
723 }
724
725 {
726 // To 12keys -> Halfwidth Ascii mode
727 request.set_special_romanji_table(
728 mozc::commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII);
729 Table table;
730 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
731 const mozc::composer::Entry *entry = NULL;
732 size_t key_length = 0;
733 bool fixed = false;
734 entry = table.LookUpPrefix("2", &key_length, &fixed);
735 // "{?}" is to be replaced by "\x0F?\x0E".
736 EXPECT_EQ("\x0F?\x0E" "a", entry->pending());
737 }
738
739 {
740 // To Godan -> Hiragana mode
741 request.set_special_romanji_table(
742 mozc::commands::Request::GODAN_TO_HIRAGANA);
743 Table table;
744 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
745 {
746 const mozc::composer::Entry *entry = NULL;
747 size_t key_length = 0;
748 bool fixed = false;
749 entry = table.LookUpPrefix("しゃ*", &key_length, &fixed);
750 EXPECT_EQ("じゃ", entry->pending());
751 }
752 }
753
754 {
755 // To Flick -> Hiragana mode.
756 request.set_special_romanji_table(
757 mozc::commands::Request::FLICK_TO_HIRAGANA);
758 Table table;
759 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
760
761 size_t key_length = 0;
762 bool fixed = false;
763 const mozc::composer::Entry *entry = table.LookUpPrefix("a", &key_length,
764 &fixed);
765 EXPECT_EQ("き", entry->pending());
766 }
767
768 {
769 // To Notouch -> Hiragana mode.
770 request.set_special_romanji_table(
771 mozc::commands::Request::NOTOUCH_TO_HIRAGANA);
772 Table table;
773 table.InitializeWithRequestAndConfig(request, config_, mock_data_manager_);
774
775 size_t key_length = 0;
776 bool fixed = false;
777 const mozc::composer::Entry *entry = table.LookUpPrefix("a", &key_length,
778 &fixed);
779 EXPECT_EQ("き", entry->pending());
780 }
781 }
782
TEST_F(TableTest,OrderOfAddRule)783 TEST_F(TableTest, OrderOfAddRule) {
784 // The order of AddRule should not be sensitive.
785 {
786 Table table;
787 table.AddRule("www", "w", "ww");
788 table.AddRule("ww", "[X]", "w");
789 table.AddRule("we", "[WE]", "");
790 EXPECT_TRUE(table.HasSubRules("ww"));
791
792 const Entry *entry;
793 entry = table.LookUp("ww");
794 EXPECT_TRUE(NULL != entry);
795
796 size_t key_length;
797 bool fixed;
798 entry = table.LookUpPrefix("ww", &key_length, &fixed);
799 EXPECT_TRUE(NULL != entry);
800 EXPECT_EQ(2, key_length);
801 EXPECT_FALSE(fixed);
802 }
803 {
804 Table table;
805 table.AddRule("ww", "[X]", "w");
806 table.AddRule("we", "[WE]", "");
807 table.AddRule("www", "w", "ww");
808 EXPECT_TRUE(table.HasSubRules("ww"));
809
810 const Entry *entry = NULL;
811 entry = table.LookUp("ww");
812 EXPECT_TRUE(NULL != entry);
813
814 size_t key_length = 0;
815 bool fixed = false;
816 entry = table.LookUpPrefix("ww", &key_length, &fixed);
817 EXPECT_TRUE(NULL != entry);
818 EXPECT_EQ(2, key_length);
819 EXPECT_FALSE(fixed);
820 }
821 }
822
TEST_F(TableTest,AddRuleWithAttributes)823 TEST_F(TableTest, AddRuleWithAttributes) {
824 const string kInput = "1";
825 Table table;
826 table.AddRuleWithAttributes(kInput, "", "a", NEW_CHUNK);
827
828 EXPECT_TRUE(table.HasNewChunkEntry(kInput));
829
830 size_t key_length = 0;
831 bool fixed = false;
832 const Entry *entry = table.LookUpPrefix(kInput, &key_length, &fixed);
833 EXPECT_EQ(1, key_length);
834 EXPECT_TRUE(fixed);
835 ASSERT_TRUE(NULL != entry);
836 EXPECT_EQ(kInput, entry->input());
837 EXPECT_EQ("", entry->result());
838 EXPECT_EQ("a", entry->pending());
839 EXPECT_EQ(NEW_CHUNK, entry->attributes());
840
841 const string kInput2 = "22";
842 table.AddRuleWithAttributes(kInput2, "", "b", NEW_CHUNK | NO_TRANSLITERATION);
843
844 EXPECT_TRUE(table.HasNewChunkEntry(kInput2));
845
846 key_length = 0;
847 fixed = false;
848 entry = table.LookUpPrefix(kInput2, &key_length, &fixed);
849 EXPECT_EQ(2, key_length);
850 EXPECT_TRUE(fixed);
851 ASSERT_TRUE(NULL != entry);
852 EXPECT_EQ(kInput2, entry->input());
853 EXPECT_EQ("", entry->result());
854 EXPECT_EQ("b", entry->pending());
855 EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION), entry->attributes());
856 }
857
TEST_F(TableTest,LoadFromString)858 TEST_F(TableTest, LoadFromString) {
859 const string kRule =
860 "# This is a comment\n"
861 "\n" // Empty line to be ignored.
862 "a\t[A]\n" // 2 entry rule
863 "kk\t[X]\tk\n" // 3 entry rule
864 "ww\t[W]\tw\tNewChunk\n" // 3 entry rule + attribute rule
865 "xx\t[X]\tx\tNewChunk NoTransliteration\n" // multiple attribute rules
866 // all attributes
867 "yy\t[Y]\ty\tNewChunk NoTransliteration DirectInput EndChunk\n"
868 "#\t[#]\n"; // This line starts with '#' but should be a rule.
869 Table table;
870 table.LoadFromString(kRule);
871
872 const Entry *entry = NULL;
873 // Test for "a\t[A]\n" -- 2 entry rule
874 EXPECT_FALSE(table.HasNewChunkEntry("a"));
875 entry = table.LookUp("a");
876 ASSERT_TRUE(NULL != entry);
877 EXPECT_EQ("[A]", entry->result());
878 EXPECT_EQ("", entry->pending());
879
880 // Test for "kk\t[X]\tk\n" -- 3 entry rule
881 EXPECT_FALSE(table.HasNewChunkEntry("kk"));
882 entry = table.LookUp("kk");
883 ASSERT_TRUE(NULL != entry);
884 EXPECT_EQ("[X]", entry->result());
885 EXPECT_EQ("k", entry->pending());
886
887 // Test for "ww\t[W]\tw\tNewChunk\n" -- 3 entry rule + attribute rule
888 EXPECT_TRUE(table.HasNewChunkEntry("ww"));
889 entry = table.LookUp("ww");
890 ASSERT_TRUE(NULL != entry);
891 EXPECT_EQ("[W]", entry->result());
892 EXPECT_EQ("w", entry->pending());
893 EXPECT_EQ(NEW_CHUNK, entry->attributes());
894
895 // Test for "xx\t[X]\tx\tNewChunk NoTransliteration\n" -- multiple
896 // attribute rules
897 EXPECT_TRUE(table.HasNewChunkEntry("xx"));
898 entry = table.LookUp("xx");
899 ASSERT_TRUE(NULL != entry);
900 EXPECT_EQ("[X]", entry->result());
901 EXPECT_EQ("x", entry->pending());
902 EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION), entry->attributes());
903
904 // Test for "yy\t[Y]\ty\tNewChunk NoTransliteration DirectInput EndChunk\n"
905 // -- all attributes
906 EXPECT_TRUE(table.HasNewChunkEntry("yy"));
907 entry = table.LookUp("yy");
908 ASSERT_TRUE(NULL != entry);
909 EXPECT_EQ("[Y]", entry->result());
910 EXPECT_EQ("y", entry->pending());
911 EXPECT_EQ((NEW_CHUNK | NO_TRANSLITERATION | DIRECT_INPUT | END_CHUNK),
912 entry->attributes());
913
914 // Test for "#\t[#]\n" -- This line starts with '#' but should be a rule.
915 entry = table.LookUp("#");
916 ASSERT_TRUE(NULL != entry);
917 EXPECT_EQ("[#]", entry->result());
918 EXPECT_EQ("", entry->pending());
919 }
920
TEST_F(TableTest,SpecialKeys)921 TEST_F(TableTest, SpecialKeys) {
922 {
923 Table table;
924 table.AddRule("x{#1}y", "X1Y", "");
925 table.AddRule("x{#2}y", "X2Y", "");
926 table.AddRule("x{{}", "X{", "");
927 table.AddRule("xy", "XY", "");
928
929 const Entry *entry = NULL;
930 entry = table.LookUp("x{#1}y");
931 EXPECT_TRUE(NULL == entry);
932
933 string key;
934 key = Table::ParseSpecialKey("x{#1}y");
935 entry = table.LookUp(key);
936 ASSERT_TRUE(NULL != entry);
937 EXPECT_EQ(key, entry->input());
938 EXPECT_EQ("X1Y", entry->result());
939
940 key = Table::ParseSpecialKey("x{#2}y");
941 entry = table.LookUp(key);
942 ASSERT_TRUE(NULL != entry);
943 EXPECT_EQ(key, entry->input());
944 EXPECT_EQ("X2Y", entry->result());
945
946 key = "x{";
947 entry = table.LookUp(key);
948 ASSERT_TRUE(NULL != entry);
949 EXPECT_EQ(key, entry->input());
950 EXPECT_EQ("X{", entry->result());
951 }
952
953 {
954 // "{{}" is replaced with "{".
955 // "{*}" is replaced with "\x0F*\x0E".
956 Table table;
957 EXPECT_EQ("\x0F" "\x0E", table.AddRule("{}", "", "")->input());
958 EXPECT_EQ("{", table.AddRule("{", "", "")->input());
959 EXPECT_EQ("}", table.AddRule("}", "", "")->input());
960 EXPECT_EQ("{", table.AddRule("{{}", "", "")->input());
961 EXPECT_EQ("{}", table.AddRule("{{}}", "", "")->input());
962 EXPECT_EQ("a{", table.AddRule("a{", "", "")->input());
963 EXPECT_EQ("{a", table.AddRule("{a", "", "")->input());
964 EXPECT_EQ("a{a", table.AddRule("a{a", "", "")->input());
965 EXPECT_EQ("a}", table.AddRule("a}", "", "")->input());
966 EXPECT_EQ("}a", table.AddRule("}a", "", "")->input());
967 EXPECT_EQ("a}a", table.AddRule("a}a", "", "")->input());
968 EXPECT_EQ("a" "\x0F" "b" "\x0E" "c",
969 table.AddRule("a{b}c", "", "")->input());
970 EXPECT_EQ("a" "\x0F" "b" "\x0E" "c" "\x0F" "d" "\x0E" "\x0F" "e" "\x0E",
971 table.AddRule("a{b}c{d}{e}", "", "")->input());
972 EXPECT_EQ("}-{", table.AddRule("}-{", "", "")->input());
973 EXPECT_EQ("a{bc", table.AddRule("a{bc", "", "")->input());
974
975 // This is not a fixed specification, but a current behavior.
976 EXPECT_EQ("\x0F" "{-" "\x0E" "}",
977 table.AddRule("{{-}}", "", "")->input());
978 }
979 }
980
TEST_F(TableTest,TableManager)981 TEST_F(TableTest, TableManager) {
982 TableManager table_manager;
983 std::set<const Table*> table_set;
984 static const commands::Request::SpecialRomanjiTable
985 special_romanji_table[] = {
986 commands::Request::DEFAULT_TABLE,
987 commands::Request::TWELVE_KEYS_TO_HIRAGANA,
988 commands::Request::TWELVE_KEYS_TO_HALFWIDTHASCII,
989 commands::Request::FLICK_TO_HIRAGANA,
990 commands::Request::FLICK_TO_HALFWIDTHASCII,
991 commands::Request::TOGGLE_FLICK_TO_HIRAGANA,
992 commands::Request::TOGGLE_FLICK_TO_HALFWIDTHASCII,
993 commands::Request::GODAN_TO_HIRAGANA,
994 commands::Request::QWERTY_MOBILE_TO_HIRAGANA,
995 commands::Request::QWERTY_MOBILE_TO_HALFWIDTHASCII,
996 commands::Request::NOTOUCH_TO_HIRAGANA,
997 commands::Request::NOTOUCH_TO_HALFWIDTHASCII,
998 };
999 static const config::Config::PreeditMethod preedit_method[] = {
1000 config::Config::ROMAN,
1001 config::Config::KANA
1002 };
1003 static const config::Config::PunctuationMethod punctuation_method[] = {
1004 config::Config::KUTEN_TOUTEN,
1005 config::Config::COMMA_PERIOD,
1006 config::Config::KUTEN_PERIOD,
1007 config::Config::COMMA_TOUTEN
1008 };
1009 static const config::Config::SymbolMethod symbol_method[] = {
1010 config::Config::CORNER_BRACKET_MIDDLE_DOT,
1011 config::Config::SQUARE_BRACKET_SLASH,
1012 config::Config::CORNER_BRACKET_SLASH,
1013 config::Config::SQUARE_BRACKET_MIDDLE_DOT
1014 };
1015
1016 for (int romanji = 0; romanji < arraysize(special_romanji_table); ++romanji) {
1017 for (int preedit = 0; preedit < arraysize(preedit_method); ++preedit) {
1018 for (int punctuation = 0; punctuation < arraysize(punctuation_method);
1019 ++punctuation) {
1020 for (int symbol = 0; symbol < arraysize(symbol_method); ++symbol) {
1021 commands::Request request;
1022 request.set_special_romanji_table(special_romanji_table[romanji]);
1023 config::Config config;
1024 config.set_preedit_method(preedit_method[preedit]);
1025 config.set_punctuation_method(punctuation_method[punctuation]);
1026 config.set_symbol_method(symbol_method[symbol]);
1027 const Table *table = table_manager.GetTable(request, config,
1028 mock_data_manager_);
1029 EXPECT_TRUE(table != NULL);
1030 EXPECT_TRUE(table_manager.GetTable(request, config,
1031 mock_data_manager_) == table);
1032 EXPECT_TRUE(table_set.find(table) == table_set.end());
1033 table_set.insert(table);
1034 }
1035 }
1036 }
1037 }
1038
1039 {
1040 // b/6788850.
1041 const string kRule =
1042 "a\t[A]\n"; // 2 entry rule
1043
1044 commands::Request request;
1045 request.set_special_romanji_table(Request::DEFAULT_TABLE);
1046 config::Config config;
1047 config.set_preedit_method(Config::ROMAN);
1048 config.set_punctuation_method(Config::KUTEN_TOUTEN);
1049 config.set_symbol_method(Config::CORNER_BRACKET_MIDDLE_DOT);
1050 config.set_custom_roman_table(kRule);
1051 const Table *table = table_manager.GetTable(request, config,
1052 mock_data_manager_);
1053 EXPECT_TRUE(table != NULL);
1054 EXPECT_TRUE(table_manager.GetTable(request, config,
1055 mock_data_manager_) == table);
1056 EXPECT_TRUE(NULL != table->LookUp("a"));
1057 EXPECT_TRUE(NULL == table->LookUp("kk"));
1058
1059 const string kRule2 =
1060 "a\t[A]\n" // 2 entry rule
1061 "kk\t[X]\tk\n"; // 3 entry rule
1062 config.set_custom_roman_table(kRule2);
1063 const Table *table2 = table_manager.GetTable(request, config,
1064 mock_data_manager_);
1065 EXPECT_TRUE(table2 != NULL);
1066 EXPECT_TRUE(table_manager.GetTable(request, config,
1067 mock_data_manager_) == table2);
1068 EXPECT_TRUE(NULL != table2->LookUp("a"));
1069 EXPECT_TRUE(NULL != table2->LookUp("kk"));
1070 }
1071 }
1072
1073 } // namespace composer
1074 } // namespace mozc
1075