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