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/internal/composition.h"
31 
32 #include <algorithm>
33 #include <memory>
34 #include <set>
35 #include <string>
36 
37 #include "composer/internal/char_chunk.h"
38 #include "composer/internal/composition_input.h"
39 #include "composer/internal/transliterators.h"
40 #include "composer/table.h"
41 #include "testing/base/public/gunit.h"
42 
43 namespace mozc {
44 namespace composer {
45 namespace {
46 
GetString(const Composition & composition)47 string GetString(const Composition &composition) {
48   string output;
49   composition.GetString(&output);
50   return output;
51 }
52 
GetRawString(const Composition & composition)53 string GetRawString(const Composition &composition) {
54   string output;
55   composition.GetStringWithTransliterator(Transliterators::RAW_STRING, &output);
56   return output;
57 }
58 
SetInput(const string & raw,const string & conversion,const bool is_new_input,CompositionInput * input)59 void SetInput(const string &raw,
60               const string &conversion,
61               const bool is_new_input,
62               CompositionInput *input) {
63   input->Clear();
64   input->set_raw(raw);
65   if (!conversion.empty()) {
66     input->set_conversion(conversion);
67   }
68   input->set_is_new_input(is_new_input);
69 }
70 
InsertCharacters(const string & input,size_t pos,Composition * composition)71 size_t InsertCharacters(const string &input,
72                         size_t pos,
73                         Composition *composition) {
74   for (size_t i = 0; i < input.size(); ++i) {
75     pos = composition->InsertAt(pos, input.substr(i, 1));
76   }
77   return pos;
78 }
79 
80 }  // namespace
81 
82 class CompositionTest : public testing::Test {
83  protected:
SetUp()84   virtual void SetUp() {
85     table_.reset(new Table);
86     composition_.reset(new Composition(table_.get()));
87     composition_->SetInputMode(Transliterators::CONVERSION_STRING);
88   }
89 
90   std::unique_ptr<Table> table_;
91   std::unique_ptr<Composition> composition_;
92 };
93 
InitComposition(Composition * comp)94 static int InitComposition(Composition* comp) {
95   static const struct TestCharChunk {
96     const char* conversion;
97     const char* pending;
98     const char* raw;
99   } test_chunks[] = {
100     // "あ ky き った っty"  (9 chars)
101     // a ky ki tta tty
102     { "あ", "", "a" },
103     { "", "ky", "ky" },
104     { "き", "", "ki" },
105     { "った", "", "tta" },
106     { "っ", "ty", "tty" },
107   };
108   static const int test_chunks_size = arraysize(test_chunks);
109   CharChunkList::iterator it;
110   comp->MaybeSplitChunkAt(0, &it);
111   for (int i = 0; i < test_chunks_size; ++i) {
112     const TestCharChunk& data = test_chunks[i];
113     CharChunk* chunk = *comp->InsertChunk(&it);
114     chunk->set_conversion(data.conversion);
115     chunk->set_pending(data.pending);
116     chunk->set_raw(data.raw);
117   }
118   return test_chunks_size;
119 }
120 
AppendChunk(const char * conversion,const char * pending,const char * raw,Composition * comp)121 static CharChunk *AppendChunk(const char *conversion,
122                               const char *pending,
123                               const char *raw,
124                               Composition *comp) {
125   CharChunkList::iterator it;
126   comp->MaybeSplitChunkAt(comp->GetLength(), &it);
127 
128   CharChunk* chunk = *comp->InsertChunk(&it);
129   chunk->set_conversion(conversion);
130   chunk->set_pending(pending);
131   chunk->set_raw(raw);
132   return chunk;
133 }
134 
TEST_F(CompositionTest,GetChunkLength)135 TEST_F(CompositionTest, GetChunkLength) {
136   static const struct TestCase {
137     const char* conversion;
138     const char* pending;
139     const char* raw;
140     const int expected_conv_length;
141     const int expected_raw_length;
142   } test_cases[] = {
143     { "あ", "", "a", 1, 1 },
144     { "", "ky", "ky", 2, 2 },
145     { "き", "", "ki", 1, 2 },
146     { "った", "", "tta", 2, 3 },
147     { "っ", "ty", "tty", 3, 3 },
148   };
149   CharChunk *chunk = AppendChunk("", "", "", composition_.get());
150 
151   for (int i = 0; i < arraysize(test_cases); ++i) {
152     const TestCase& test = test_cases[i];
153     chunk->set_conversion(test.conversion);
154     chunk->set_pending(test.pending);
155     chunk->set_raw(test.raw);
156 
157     const int conv_length =
158         chunk->GetLength(Transliterators::CONVERSION_STRING);
159     EXPECT_EQ(test.expected_conv_length, conv_length);
160 
161     const int raw_length = chunk->GetLength(Transliterators::RAW_STRING);
162     EXPECT_EQ(test.expected_raw_length, raw_length);
163   }
164 }
165 
166 namespace {
TestGetChunkAt(Composition * comp,Transliterators::Transliterator transliterator,const size_t index,const size_t expected_index,const size_t expected_inner_position)167 bool TestGetChunkAt(Composition *comp,
168                     Transliterators::Transliterator transliterator,
169                     const size_t index,
170                     const size_t expected_index,
171                     const size_t expected_inner_position) {
172   CharChunkList::iterator it;
173   CharChunkList::const_iterator expected_it;
174   size_t inner_position;
175 
176   expected_it = comp->GetCharChunkList().begin();
177   for (int j = 0; j < expected_index; ++j, ++expected_it) {}
178 
179   comp->GetChunkAt(index, transliterator, &it, &inner_position);
180   if (it == comp->GetCharChunkList().end()) {
181     EXPECT_TRUE(expected_it == it);
182     EXPECT_EQ(expected_inner_position, inner_position);
183   } else {
184     string result, expected_result;
185     EXPECT_EQ((*expected_it)->conversion(), (*it)->conversion());
186     EXPECT_EQ((*expected_it)->pending(), (*it)->pending());
187     EXPECT_EQ((*expected_it)->raw(), (*it)->raw());
188     EXPECT_EQ(expected_inner_position, inner_position);
189   }
190   return true;
191 }
192 }  // namespace
193 
TEST_F(CompositionTest,GetChunkAt)194 TEST_F(CompositionTest, GetChunkAt) {
195   InitComposition(composition_.get());
196 
197   TestGetChunkAt(composition_.get(),
198                  Transliterators::CONVERSION_STRING, 0, 0, 0);
199   TestGetChunkAt(composition_.get(),
200                  Transliterators::CONVERSION_STRING, 1, 0, 1);
201   TestGetChunkAt(composition_.get(),
202                  Transliterators::CONVERSION_STRING, 2, 1, 1);
203   TestGetChunkAt(composition_.get(),
204                  Transliterators::CONVERSION_STRING, 3, 1, 2);
205   TestGetChunkAt(composition_.get(),
206                  Transliterators::CONVERSION_STRING, 4, 2, 1);
207   TestGetChunkAt(composition_.get(),
208                  Transliterators::CONVERSION_STRING, 5, 3, 1);
209   TestGetChunkAt(composition_.get(),
210                  Transliterators::CONVERSION_STRING, 6, 3, 2);
211   TestGetChunkAt(composition_.get(),
212                  Transliterators::CONVERSION_STRING, 7, 4, 1);
213   TestGetChunkAt(composition_.get(),
214                  Transliterators::CONVERSION_STRING, 8, 4, 2);
215   TestGetChunkAt(composition_.get(),
216                  Transliterators::CONVERSION_STRING, 9, 4, 3);
217   // end
218   TestGetChunkAt(composition_.get(),
219                  Transliterators::CONVERSION_STRING, 10, 4, 3);
220   // end
221   TestGetChunkAt(composition_.get(),
222                  Transliterators::CONVERSION_STRING, 11, 4, 3);
223 
224   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 0, 0, 0);
225   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 1, 0, 1);
226   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 2, 1, 1);
227   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 3, 1, 2);
228   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 4, 2, 1);
229   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 5, 2, 2);
230   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 6, 3, 1);
231   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 7, 3, 2);
232   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 8, 3, 3);
233   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 9, 4, 1);
234   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 10, 4, 2);
235   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 11, 4, 3);
236   // end
237   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 12, 4, 3);
238   // end
239   TestGetChunkAt(composition_.get(), Transliterators::RAW_STRING, 13, 4, 3);
240 }
241 
TEST_F(CompositionTest,GetString)242 TEST_F(CompositionTest, GetString) {
243   InitComposition(composition_.get());
244   string composition;
245 
246   const size_t dummy_position = 0;
247 
248   // Test RAW mode
249   composition_->SetDisplayMode(dummy_position, Transliterators::RAW_STRING);
250   composition_->GetString(&composition);
251   EXPECT_EQ("akykittatty", composition);
252 
253   // Test CONVERSION mode
254   composition_->SetDisplayMode(dummy_position,
255                                Transliterators::CONVERSION_STRING);
256   composition_->GetString(&composition);
257   EXPECT_EQ("あkyきったっty", composition);
258 }
259 
TEST_F(CompositionTest,GetStringWithDisplayMode)260 TEST_F(CompositionTest, GetStringWithDisplayMode) {
261   AppendChunk("も", "", "mo", composition_.get());
262   AppendChunk("ず", "", "z", composition_.get());
263   AppendChunk("く", "", "c", composition_.get());
264 
265   string composition;
266   composition_->GetStringWithTransliterator(Transliterators::CONVERSION_STRING,
267                                             &composition);
268   EXPECT_EQ("もずく", composition);
269 
270   composition_->GetStringWithTransliterator(Transliterators::RAW_STRING,
271                                             &composition);
272   EXPECT_EQ("mozc", composition);
273 }
274 
TEST_F(CompositionTest,SplitRawChunk)275 TEST_F(CompositionTest, SplitRawChunk) {
276   static const struct TestCase {
277     const char* conversion;
278     const char* pending;
279     const char* raw;
280     const int position;
281     const char* expected_left_conversion;
282     const char* expected_left_pending;
283     const char* expected_left_raw;
284     const char* expected_right_conversion;
285     const char* expected_right_pending;
286     const char* expected_right_raw;
287   } test_cases[] = {
288     { "あ", "", "a", 0, "", "", "", "あ", "", "a" },
289     { "", "ky", "ky", 1, "", "k", "k", "", "y", "y" },
290     { "き", "", "ki", 1, "k", "", "k", "i", "", "i" },
291     { "った", "", "tta", 1, "t", "", "t", "ta", "", "ta" },
292     { "った", "", "tta", 2, "tt", "", "tt", "a", "", "a" },
293     { "っ", "ty", "tty", 1, "っ", "", "t", "", "ty", "ty" },
294     { "っ", "ty", "tty", 2, "っ", "t", "tt", "", "y", "y" },
295     { "っ", "ty", "tty", 3, "", "", "", "っ", "ty", "tty" },
296   };
297   for (int i = 0; i < arraysize(test_cases); ++i) {
298     const TestCase& test = test_cases[i];
299     CharChunk right_orig_chunk(Transliterators::CONVERSION_STRING, NULL);
300     right_orig_chunk.set_conversion(test.conversion);
301     right_orig_chunk.set_pending(test.pending);
302     right_orig_chunk.set_raw(test.raw);
303     CharChunk *left_new_chunk_ptr = NULL;
304     right_orig_chunk.SplitChunk(Transliterators::RAW_STRING,
305                                 test.position, &left_new_chunk_ptr);
306     std::unique_ptr<CharChunk> left_new_chunk(left_new_chunk_ptr);
307 
308     if (left_new_chunk.get() != NULL) {
309     EXPECT_EQ(test.expected_left_conversion, left_new_chunk->conversion());
310     EXPECT_EQ(test.expected_left_pending, left_new_chunk->pending());
311     EXPECT_EQ(test.expected_left_raw, left_new_chunk->raw());
312     }
313 
314     EXPECT_EQ(test.expected_right_conversion, right_orig_chunk.conversion());
315     EXPECT_EQ(test.expected_right_pending, right_orig_chunk.pending());
316     EXPECT_EQ(test.expected_right_raw, right_orig_chunk.raw());
317   }
318 }
319 
TEST_F(CompositionTest,SplitConversionChunk)320 TEST_F(CompositionTest, SplitConversionChunk) {
321   static const struct TestCase {
322     const char* conversion;
323     const char* pending;
324     const char* raw;
325     const int position;
326     const char* expected_left_conversion;
327     const char* expected_left_pending;
328     const char* expected_left_raw;
329     const char* expected_right_conversion;
330     const char* expected_right_pending;
331     const char* expected_right_raw;
332   } test_cases[] = {
333     { "あ", "", "a", 0, "", "", "", "あ", "", "a" },
334     { "", "ky", "ky", 1, "", "k", "k", "", "y", "y" },
335     { "きょ", "", "kyo", 1, "き", "", "き", "ょ", "", "ょ" },
336     { "っ", "t", "tt", 1, "っ", "", "t", "", "t", "t" },
337     { "った", "", "tta", 1, "っ", "", "っ", "た", "", "た" },
338     { "っ", "ty", "tty", 1, "っ", "", "t", "", "ty", "ty" },
339     { "っ", "ty", "tty", 2, "っ", "t", "tt", "", "y", "y" },
340     { "っ", "ty", "tty", 3, "", "", "", "っ", "ty", "tty" },
341   };
342   for (int i = 0; i < arraysize(test_cases); ++i) {
343     const TestCase& test = test_cases[i];
344     CharChunk right_orig_chunk(Transliterators::CONVERSION_STRING, NULL);
345     right_orig_chunk.set_conversion(test.conversion);
346     right_orig_chunk.set_pending(test.pending);
347     right_orig_chunk.set_raw(test.raw);
348     CharChunk *left_new_chunk_ptr = NULL;
349     right_orig_chunk.SplitChunk(Transliterators::CONVERSION_STRING,
350                                 test.position, &left_new_chunk_ptr);
351     std::unique_ptr<CharChunk> left_new_chunk(left_new_chunk_ptr);
352 
353     if (left_new_chunk.get() != NULL) {
354       EXPECT_EQ(test.expected_left_conversion, left_new_chunk->conversion());
355       EXPECT_EQ(test.expected_left_pending, left_new_chunk->pending());
356       EXPECT_EQ(test.expected_left_raw, left_new_chunk->raw());
357     }
358 
359     EXPECT_EQ(test.expected_right_conversion, right_orig_chunk.conversion());
360     EXPECT_EQ(test.expected_right_pending, right_orig_chunk.pending());
361     EXPECT_EQ(test.expected_right_raw, right_orig_chunk.raw());
362   }
363 }
364 
TEST_F(CompositionTest,GetLength)365 TEST_F(CompositionTest, GetLength) {
366   table_->AddRule("a", "A", "");
367   table_->AddRule("ka", "K", "");
368 
369   EXPECT_EQ(0, composition_->GetLength());
370 
371   InsertCharacters("aka", 0, composition_.get());
372   EXPECT_EQ(2, composition_->GetLength());
373 }
374 
TEST_F(CompositionTest,MaybeSplitChunkAt)375 TEST_F(CompositionTest, MaybeSplitChunkAt) {
376   static const struct TestCase {
377     const int position;
378     const int expected_raw_chunks_size;
379     const int expected_conv_chunks_size;
380   } test_cases[] = {
381     // "あ ky き った っty"  (9 chars)
382     // a ky ki tta tty (11 chars)
383     {  0, 5, 5 },
384     {  1, 5, 5 },
385     {  2, 6, 6 },
386     {  3, 5, 5 },
387     {  4, 6, 5 },
388     {  5, 5, 6 },
389     {  6, 6, 5 },
390     {  7, 6, 6 },
391     {  8, 5, 6 },
392     {  9, 6, 5 },
393     { 10, 6, 5 },
394     { 11, 5, 5 },
395     { 12, 5, 5 },
396   };
397   const size_t dummy_position = 0;
398   for (int i = 0; i < arraysize(test_cases); ++i) {
399     const TestCase& test = test_cases[i];
400 
401     {  // Test RAW mode
402       Composition raw_comp(table_.get());
403       InitComposition(&raw_comp);
404       raw_comp.SetDisplayMode(dummy_position, Transliterators::RAW_STRING);
405       CharChunkList::iterator raw_it;
406 
407       raw_comp.MaybeSplitChunkAt(test.position, &raw_it);
408       const size_t raw_chunks_size = raw_comp.GetCharChunkList().size();
409       EXPECT_EQ(test.expected_raw_chunks_size, raw_chunks_size);
410     }
411 
412     {  // Test CONVERSION mode
413       Composition conv_comp(table_.get());
414       InitComposition(&conv_comp);
415       conv_comp.SetDisplayMode(dummy_position,
416                                Transliterators::CONVERSION_STRING);
417       CharChunkList::iterator conv_it;
418 
419       conv_comp.MaybeSplitChunkAt(test.position, &conv_it);
420       const size_t conv_chunks_size = conv_comp.GetCharChunkList().size();
421       EXPECT_EQ(test.expected_conv_chunks_size, conv_chunks_size);
422     }
423   }
424 }
425 
426 namespace {
GetDeletedString(Transliterators::Transliterator t12r,const int position)427 string GetDeletedString(Transliterators::Transliterator t12r,
428                         const int position) {
429   std::unique_ptr<Table> table(new Table);
430   std::unique_ptr<Composition> comp(new Composition(table.get()));
431 
432   InitComposition(comp.get());
433   comp->SetDisplayMode(0, t12r);
434   comp->DeleteAt(position);
435   string composition;
436   comp->GetString(&composition);
437 
438   comp.reset();
439   table.reset();
440   return composition;
441 }
442 }  // namespace
443 
TEST_F(CompositionTest,DeleteAt)444 TEST_F(CompositionTest, DeleteAt) {
445   // "あkyきったっty" is the original string
446   EXPECT_EQ("kyきったっty",
447             GetDeletedString(Transliterators::CONVERSION_STRING, 0));
448   EXPECT_EQ("あyきったっty",
449             GetDeletedString(Transliterators::CONVERSION_STRING, 1));
450   EXPECT_EQ("あkきったっty",
451             GetDeletedString(Transliterators::CONVERSION_STRING, 2));
452   EXPECT_EQ("あkyったっty",
453             GetDeletedString(Transliterators::CONVERSION_STRING, 3));
454   EXPECT_EQ("あkyきたっty",
455             GetDeletedString(Transliterators::CONVERSION_STRING, 4));
456   EXPECT_EQ("あkyきっっty",
457             GetDeletedString(Transliterators::CONVERSION_STRING, 5));
458   EXPECT_EQ("あkyきったty",
459             GetDeletedString(Transliterators::CONVERSION_STRING, 6));
460   EXPECT_EQ("あkyきったっy",
461             GetDeletedString(Transliterators::CONVERSION_STRING, 7));
462   EXPECT_EQ("あkyきったっt",
463             GetDeletedString(Transliterators::CONVERSION_STRING, 8));
464   // end
465   EXPECT_EQ("あkyきったっty",
466             GetDeletedString(Transliterators::CONVERSION_STRING, 9));
467   EXPECT_EQ("あkyきったっty",
468             GetDeletedString(Transliterators::CONVERSION_STRING, -1));
469 
470   // "akykittatty" is the original string
471   EXPECT_EQ("kykittatty", GetDeletedString(Transliterators::RAW_STRING, 0));
472   EXPECT_EQ("aykittatty", GetDeletedString(Transliterators::RAW_STRING, 1));
473   EXPECT_EQ("akkittatty", GetDeletedString(Transliterators::RAW_STRING, 2));
474   EXPECT_EQ("akyittatty", GetDeletedString(Transliterators::RAW_STRING, 3));
475   EXPECT_EQ("akykttatty", GetDeletedString(Transliterators::RAW_STRING, 4));
476   EXPECT_EQ("akykitatty", GetDeletedString(Transliterators::RAW_STRING, 5));
477   EXPECT_EQ("akykitatty", GetDeletedString(Transliterators::RAW_STRING, 6));
478   EXPECT_EQ("akykitttty", GetDeletedString(Transliterators::RAW_STRING, 7));
479   EXPECT_EQ("akykittaty", GetDeletedString(Transliterators::RAW_STRING, 8));
480   EXPECT_EQ("akykittaty", GetDeletedString(Transliterators::RAW_STRING, 9));
481   EXPECT_EQ("akykittatt", GetDeletedString(Transliterators::RAW_STRING, 10));
482   // end
483   EXPECT_EQ("akykittatty", GetDeletedString(Transliterators::RAW_STRING, 11));
484   EXPECT_EQ("akykittatty", GetDeletedString(Transliterators::RAW_STRING, -1));
485 }
486 
TEST_F(CompositionTest,DeleteAt_InvisibleCharacter)487 TEST_F(CompositionTest, DeleteAt_InvisibleCharacter) {
488   CharChunkList::iterator it;
489   CharChunk *chunk;
490 
491   composition_->MaybeSplitChunkAt(0, &it);
492 
493   chunk = *composition_->InsertChunk(&it);
494   chunk->set_raw("1");
495   chunk->set_pending(Table::ParseSpecialKey("{1}"));
496 
497   chunk = *composition_->InsertChunk(&it);
498   chunk->set_raw("2");
499   chunk->set_pending(Table::ParseSpecialKey("{2}2"));
500 
501   chunk = *composition_->InsertChunk(&it);
502   chunk->set_raw("3");
503   chunk->set_pending("3");
504 
505   // Now the CharChunks in the comp are expected to be following;
506   // (raw, pending) = [ ("1", "{1}"), ("2", "{2}2"), ("3", "3") ]
507   // {} means invisible characters.
508 
509   composition_->DeleteAt(0);
510   string composition;
511   composition_->GetString(&composition);
512   EXPECT_EQ("3", composition);
513 }
514 
515 namespace {
516 
InitTable(Table * table)517 void InitTable(Table* table) {
518   table->AddRule("i",  "い", "");
519   table->AddRule("ki", "き", "");
520   table->AddRule("kyi", "きぃ", "");
521   table->AddRule("ti", "ち", "");
522   table->AddRule("tya", "ちゃ", "");
523   table->AddRule("tyi", "ちぃ", "");
524   table->AddRule("ya", "や", "");
525   table->AddRule("yy", "っ", "y");
526 }
527 
GetInsertedString(Transliterators::Transliterator t12r,const size_t position,const string & input)528 string GetInsertedString(Transliterators::Transliterator t12r,
529                          const size_t position,
530                          const string &input) {
531   std::unique_ptr<Table> table(new Table);
532   InitTable(table.get());
533   std::unique_ptr<Composition> comp(new Composition(table.get()));
534   InitComposition(comp.get());
535 
536   comp->SetTable(table.get());
537   comp->SetDisplayMode(0, t12r);
538   comp->InsertAt(position, input);
539 
540   string composition;
541   comp->GetString(&composition);
542 
543   comp.reset();
544   table.reset();
545 
546   return composition;
547 }
548 }  // namespace
549 
TEST_F(CompositionTest,InsertAt)550 TEST_F(CompositionTest, InsertAt) {
551   // "あkyきったっty" is the original string
552   EXPECT_EQ("いあkyきったっty",
553             GetInsertedString(Transliterators::CONVERSION_STRING, 0, "i"));
554   EXPECT_EQ("あいkyきったっty",
555             GetInsertedString(Transliterators::CONVERSION_STRING, 1, "i"));
556   EXPECT_EQ("あきyきったっty",
557             GetInsertedString(Transliterators::CONVERSION_STRING, 2, "i"));
558   EXPECT_EQ("あきぃきったっty",
559             GetInsertedString(Transliterators::CONVERSION_STRING, 3, "i"));
560   EXPECT_EQ("あkyきいったっty",
561             GetInsertedString(Transliterators::CONVERSION_STRING, 4, "i"));
562   EXPECT_EQ("あkyきっいたっty",
563             GetInsertedString(Transliterators::CONVERSION_STRING, 5, "i"));
564   EXPECT_EQ("あkyきったっちぃ",
565             GetInsertedString(Transliterators::CONVERSION_STRING, 9, "i"));
566   EXPECT_EQ("yあkyきったっty",
567             GetInsertedString(Transliterators::CONVERSION_STRING, 0, "y"));
568   EXPECT_EQ("あykyきったっty",
569             GetInsertedString(Transliterators::CONVERSION_STRING, 1, "y"));
570   EXPECT_EQ("あkyyきったっty",
571             GetInsertedString(Transliterators::CONVERSION_STRING, 2, "y"));
572   EXPECT_EQ("あkyyきったっty",
573             GetInsertedString(Transliterators::CONVERSION_STRING, 3, "y"));
574   EXPECT_EQ("あkyきyったっty",
575             GetInsertedString(Transliterators::CONVERSION_STRING, 4, "y"));
576   EXPECT_EQ("あkyきっyたっty",
577             GetInsertedString(Transliterators::CONVERSION_STRING, 5, "y"));
578   // end
579   EXPECT_EQ("あkyきったっちぃ",
580             GetInsertedString(Transliterators::CONVERSION_STRING, 9, "i"));
581   // end
582   EXPECT_EQ("あkyきったっtyy",
583             GetInsertedString(Transliterators::CONVERSION_STRING, 9, "y"));
584 
585   // "akykittatty" is the original string
586   EXPECT_EQ("iakykittatty",
587             GetInsertedString(Transliterators::RAW_STRING, 0, "i"));
588 
589   EXPECT_EQ("aikykittatty",
590             GetInsertedString(Transliterators::RAW_STRING, 1, "i"));
591 
592   EXPECT_EQ("akiykittatty",
593             GetInsertedString(Transliterators::RAW_STRING, 2, "i"));
594 
595   EXPECT_EQ("akyikittatty",
596             GetInsertedString(Transliterators::RAW_STRING, 3, "i"));
597 
598   EXPECT_EQ("akykiittatty",
599             GetInsertedString(Transliterators::RAW_STRING, 4, "i"));
600 
601   EXPECT_EQ("akykiittatty",
602             GetInsertedString(Transliterators::RAW_STRING, 5, "i"));
603 
604   EXPECT_EQ("akykittattyi",
605             GetInsertedString(Transliterators::RAW_STRING, 11, "i"));  // end
606 }
607 
TEST_F(CompositionTest,GetExpandedStrings)608 TEST_F(CompositionTest, GetExpandedStrings) {
609   Table table;
610   InitTable(&table);
611   composition_->SetTable(&table);
612   InitComposition(composition_.get());
613 
614   // a ky ki tta tty
615   string base;
616   std::set<string> expanded;
617   composition_->GetExpandedStrings(&base, &expanded);
618   EXPECT_EQ("あkyきったっ", base);
619   EXPECT_EQ(2, expanded.size());
620   // You cannot use EXPECT_NE here because it causes compile error in gtest
621   // when the compiler is Visual C++. b/5655673
622   EXPECT_TRUE(expanded.find("ちぃ") != expanded.end());
623   // You cannot use EXPECT_NE here because it causes compile error in gtest
624   // when the compiler is Visual C++. b/5655673
625   EXPECT_TRUE(expanded.find("ちゃ") != expanded.end());
626 }
627 
TEST_F(CompositionTest,ConvertPosition)628 TEST_F(CompositionTest, ConvertPosition) {
629   // Test against http://b/1550597
630 
631   // Invalid positions.
632   EXPECT_EQ(0,
633             composition_->ConvertPosition(
634                 static_cast<size_t>(-1),
635                 Transliterators::CONVERSION_STRING,
636                 Transliterators::RAW_STRING));
637   EXPECT_EQ(0,
638             composition_->ConvertPosition(
639                 0,
640                 Transliterators::CONVERSION_STRING,
641                 Transliterators::RAW_STRING));
642   EXPECT_EQ(0,
643             composition_->ConvertPosition(
644                 1,
645                 Transliterators::CONVERSION_STRING,
646                 Transliterators::RAW_STRING));
647   EXPECT_EQ(0,
648             composition_->ConvertPosition(
649                 0,
650                 Transliterators::RAW_STRING,
651                 Transliterators::CONVERSION_STRING));
652   EXPECT_EQ(0,
653             composition_->ConvertPosition(
654                 static_cast<size_t>(-1),
655                 Transliterators::RAW_STRING,
656                 Transliterators::CONVERSION_STRING));
657   EXPECT_EQ(0,
658             composition_->ConvertPosition(
659                 1,
660                 Transliterators::RAW_STRING,
661                 Transliterators::CONVERSION_STRING));
662 
663   AppendChunk("ね", "", "ne", composition_.get());
664   AppendChunk("っと", "", "tto", composition_.get());
665 
666   // "|ねっと" -> "|netto"
667   EXPECT_EQ(0,
668             composition_->ConvertPosition(
669                 0,
670                 Transliterators::CONVERSION_STRING,
671                 Transliterators::RAW_STRING));
672   // "ね|っと" -> "ne|tto"
673   EXPECT_EQ(2,
674             composition_->ConvertPosition(
675                 1,
676                 Transliterators::CONVERSION_STRING,
677                 Transliterators::RAW_STRING));
678   // "ねっ|と" -> "net|to"
679   EXPECT_EQ(3,
680             composition_->ConvertPosition(
681                 2,
682                 Transliterators::CONVERSION_STRING,
683                 Transliterators::RAW_STRING));
684   // "ねっと|" -> "netto|"
685   EXPECT_EQ(5,
686             composition_->ConvertPosition(
687                 3,
688                 Transliterators::CONVERSION_STRING,
689                 Transliterators::RAW_STRING));
690 
691   // Invalid positions.
692   EXPECT_EQ(5,
693             composition_->ConvertPosition(
694                 static_cast<size_t>(-1),
695                 Transliterators::CONVERSION_STRING,
696                 Transliterators::RAW_STRING));
697   EXPECT_EQ(5,
698             composition_->ConvertPosition(
699                 4,
700                 Transliterators::CONVERSION_STRING,
701                 Transliterators::RAW_STRING));
702 
703   // "|netto" -> "|ねっと"
704   EXPECT_EQ(0,
705             composition_->ConvertPosition(
706                 0,
707                 Transliterators::RAW_STRING,
708                 Transliterators::CONVERSION_STRING));
709   // "n|etto" -> "ね|っと"
710   EXPECT_EQ(1,
711             composition_->ConvertPosition(
712                 1,
713                 Transliterators::RAW_STRING,
714                 Transliterators::CONVERSION_STRING));
715   // "ne|tto" -> "ね|っと"
716   EXPECT_EQ(1,
717             composition_->ConvertPosition(
718                 2,
719                 Transliterators::RAW_STRING,
720                 Transliterators::CONVERSION_STRING));
721   // "net|to" -> "ねっ|と"
722   EXPECT_EQ(2,
723             composition_->ConvertPosition(
724                 3,
725                 Transliterators::RAW_STRING,
726                 Transliterators::CONVERSION_STRING));
727   // "nett|o" -> "ねっと|"
728   EXPECT_EQ(3,
729             composition_->ConvertPosition(
730                 4,
731                 Transliterators::RAW_STRING,
732                 Transliterators::CONVERSION_STRING));
733   // "netto|" -> "ねっと|"
734   EXPECT_EQ(3,
735             composition_->ConvertPosition(
736                 5,
737                 Transliterators::RAW_STRING,
738                 Transliterators::CONVERSION_STRING));
739   // Invalid positions.
740   EXPECT_EQ(3,
741             composition_->ConvertPosition(
742                 static_cast<size_t>(-1),
743                 Transliterators::RAW_STRING,
744                 Transliterators::CONVERSION_STRING));
745   EXPECT_EQ(3,
746             composition_->ConvertPosition(
747                 6,
748                 Transliterators::RAW_STRING,
749                 Transliterators::CONVERSION_STRING));
750 
751   CharChunkList::iterator chunk_it;
752   size_t inner_position;
753   composition_->GetChunkAt(5, Transliterators::RAW_STRING,
754                            &chunk_it, &inner_position);
755 
756   EXPECT_EQ("tto", (*chunk_it)->raw());
757   EXPECT_EQ(3, inner_position);
758 }
759 
TEST_F(CompositionTest,SetDisplayMode)760 TEST_F(CompositionTest, SetDisplayMode) {
761   AppendChunk("も", "", "mo", composition_.get());
762   AppendChunk("ず", "", "zu", composition_.get());
763   AppendChunk("く", "", "ku", composition_.get());
764 
765   CharChunkList::iterator chunk_it;
766   size_t inner_position;
767   composition_->GetChunkAt(0, Transliterators::CONVERSION_STRING,
768                            &chunk_it, &inner_position);
769   EXPECT_EQ("mo", (*chunk_it)->raw());
770   EXPECT_EQ(0, inner_position);
771   composition_->GetChunkAt(1, Transliterators::CONVERSION_STRING,
772                            &chunk_it, &inner_position);
773   EXPECT_EQ("mo", (*chunk_it)->raw());
774   EXPECT_EQ(1, inner_position);
775   composition_->GetChunkAt(2, Transliterators::CONVERSION_STRING,
776                            &chunk_it, &inner_position);
777   EXPECT_EQ("zu", (*chunk_it)->raw());
778   EXPECT_EQ(1, inner_position);
779   composition_->GetChunkAt(3, Transliterators::CONVERSION_STRING,
780                            &chunk_it, &inner_position);
781   EXPECT_EQ("ku", (*chunk_it)->raw());
782   EXPECT_EQ(1, inner_position);
783 
784   EXPECT_EQ(6,
785             composition_->SetDisplayMode(1,
786                                          Transliterators::RAW_STRING));
787   EXPECT_EQ(3,
788             composition_->SetDisplayMode(2,
789                                          Transliterators::CONVERSION_STRING));
790   EXPECT_EQ(6,
791             composition_->SetDisplayMode(2,
792                                          Transliterators::RAW_STRING));
793 }
794 
TEST_F(CompositionTest,GetStringWithTrimMode)795 TEST_F(CompositionTest, GetStringWithTrimMode) {
796   Table table;
797   table.AddRule("ka", "か", "");
798   table.AddRule("n", "ん", "");
799   // This makes the above rule ambiguous.
800   table.AddRule("na", "な", "");
801   composition_->SetTable(&table);
802 
803   string output_empty;
804   composition_->GetStringWithTrimMode(TRIM, &output_empty);
805   EXPECT_TRUE(output_empty.empty());
806 
807   size_t pos = 0;
808   pos = composition_->InsertAt(pos, "k");
809   pos = composition_->InsertAt(pos, "a");
810   pos = composition_->InsertAt(pos, "n");
811 
812   string output_trim;
813   composition_->GetStringWithTrimMode(TRIM, &output_trim);
814   EXPECT_EQ("か", output_trim);
815 
816   string output_asis;
817   composition_->GetStringWithTrimMode(ASIS, &output_asis);
818   EXPECT_EQ("かn", output_asis);
819 
820   string output_fix;
821   composition_->GetStringWithTrimMode(FIX, &output_asis);
822   EXPECT_EQ("かん", output_asis);
823 }
824 
TEST_F(CompositionTest,InsertKeyAndPreeditAt)825 TEST_F(CompositionTest, InsertKeyAndPreeditAt) {
826   Table table;
827   table.AddRule("す゛", "ず", "");
828   table.AddRule("く゛", "ぐ", "");
829   composition_->SetTable(&table);
830 
831   size_t pos = 0;
832   pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
833   pos = composition_->InsertKeyAndPreeditAt(pos, "r", "す");
834   pos = composition_->InsertKeyAndPreeditAt(pos, "@", "゛");
835   pos = composition_->InsertKeyAndPreeditAt(pos, "h", "く");
836   pos = composition_->InsertKeyAndPreeditAt(pos, "!", "!");
837 
838   string comp_str;
839   composition_->GetString(&comp_str);
840   EXPECT_EQ("もずく!", comp_str);
841 
842   string comp_ascii_str;
843   composition_->GetStringWithTransliterator(
844       Transliterators::RAW_STRING, &comp_ascii_str);
845   EXPECT_EQ("mr@h!", comp_ascii_str);
846 }
847 
TEST_F(CompositionTest,InsertKey_ForN)848 TEST_F(CompositionTest, InsertKey_ForN) {
849   Table table;
850   table.AddRule("a", "[A]", "");
851   table.AddRule("n", "[N]", "");
852   table.AddRule("nn", "[N]", "");
853   table.AddRule("na", "[NA]", "");
854   table.AddRule("nya", "[NYA]", "");
855   table.AddRule("ya", "[YA]", "");
856   table.AddRule("ka", "[KA]", "");
857   composition_->SetTable(&table);
858 
859   size_t pos = 0;
860   pos = composition_->InsertAt(pos, "n");
861   pos = composition_->InsertAt(pos, "y");
862   pos = composition_->InsertAt(pos, "n");
863   pos = composition_->InsertAt(pos, "y");
864   pos = composition_->InsertAt(pos, "a");
865 
866   string comp_str;
867   composition_->GetString(&comp_str);
868   EXPECT_EQ("ny[NYA]", comp_str);
869 }
870 
TEST_F(CompositionTest,GetStringWithDisplayMode_ForKana)871 TEST_F(CompositionTest, GetStringWithDisplayMode_ForKana) {
872   Table table;
873   // Empty table is OK.
874   composition_->SetTable(&table);
875 
876   size_t pos = 0;
877   pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
878 
879   string comp_str;
880   composition_->GetStringWithTransliterator(
881       Transliterators::RAW_STRING, &comp_str);
882   EXPECT_EQ("m", comp_str);
883 }
884 
TEST_F(CompositionTest,InputMode)885 TEST_F(CompositionTest, InputMode) {
886   Table table;
887   table.AddRule("a", "あ", "");
888   table.AddRule("ka", "か", "");
889   composition_->SetTable(&table);
890 
891   size_t pos = 0;
892   pos = composition_->InsertAt(pos, "k");
893 
894   string result;
895   composition_->GetString(&result);
896   EXPECT_EQ("k", result);
897 
898   composition_->SetInputMode(Transliterators::FULL_KATAKANA);
899   pos = composition_->InsertAt(pos, "a");
900   composition_->GetString(&result);
901   // If a vowel and a consonant were typed with different
902   // transliterators, these characters should not be combined.
903   EXPECT_EQ("kア", result);
904 
905   composition_->SetInputMode(Transliterators::HALF_ASCII);
906   pos = composition_->InsertAt(pos, "k");
907   composition_->GetString(&result);
908   EXPECT_EQ("kアk", result);
909 
910   composition_->SetInputMode(Transliterators::HIRAGANA);
911   pos = composition_->InsertAt(pos, "a");
912   composition_->GetString(&result);
913   EXPECT_EQ("kアkあ", result);
914 
915   EXPECT_EQ(Transliterators::CONVERSION_STRING,
916             composition_->GetTransliterator(0));
917   EXPECT_EQ(Transliterators::CONVERSION_STRING,
918             composition_->GetTransliterator(1));
919   EXPECT_EQ(Transliterators::FULL_KATAKANA, composition_->GetTransliterator(2));
920   EXPECT_EQ(Transliterators::HALF_ASCII, composition_->GetTransliterator(3));
921   EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(4));
922   EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(5));
923   EXPECT_EQ(Transliterators::HIRAGANA, composition_->GetTransliterator(10));
924 }
925 
TEST_F(CompositionTest,SetTable)926 TEST_F(CompositionTest, SetTable) {
927   Table table;
928   table.AddRule("a", "あ", "");
929   table.AddRule("ka", "か", "");
930 
931   Table table2;
932   table2.AddRule("a", "あ", "");
933   table2.AddRule("ka", "か", "");
934 
935   composition_->SetTable(&table);
936   composition_->SetInputMode(Transliterators::HIRAGANA);
937 
938   size_t pos = 0;
939   pos = composition_->InsertAt(pos, "k");
940 
941   string result;
942   composition_->GetString(&result);
943   EXPECT_EQ("k", result);
944 
945   composition_->SetTable(&table2);
946 
947   pos = composition_->InsertAt(pos, "a");
948   composition_->GetString(&result);
949   EXPECT_EQ("kあ", result);
950 }
951 
TEST_F(CompositionTest,Transliterator)952 TEST_F(CompositionTest, Transliterator) {
953   Table table;
954   table.AddRule("a", "あ", "");
955   composition_->SetTable(&table);
956 
957   // Insert "a" which is converted to "あ".
958   size_t pos = 0;
959   pos = composition_->InsertAt(pos, "a");
960   EXPECT_EQ(1, pos);
961   string result;
962   composition_->GetString(&result);
963   EXPECT_EQ("あ", result);
964 
965   // Set transliterator for Half Ascii.
966   composition_->SetTransliterator(0, pos, Transliterators::HALF_ASCII);
967   composition_->GetString(&result);
968   EXPECT_EQ("a", result);
969 
970   // Insert "a" again.
971   pos = composition_->InsertAt(pos, "a");
972   EXPECT_EQ(2, pos);
973   result.clear();
974   composition_->GetString(&result);
975   EXPECT_EQ("aあ", result);
976 
977   // Set transliterator for Full Katakana.
978   composition_->SetTransliterator(0, pos, Transliterators::FULL_KATAKANA);
979   composition_->GetString(&result);
980   EXPECT_EQ("アア", result);
981 }
982 
TEST_F(CompositionTest,HalfAsciiTransliterator)983 TEST_F(CompositionTest, HalfAsciiTransliterator) {
984   Table table;
985   table.AddRule("-", "ー", "");
986   composition_->SetTable(&table);
987   composition_->SetInputMode(Transliterators::HALF_ASCII);
988 
989   size_t pos = 0;
990   pos = composition_->InsertKeyAndPreeditAt(pos, "-", "-");
991   EXPECT_EQ(1, pos);
992   EXPECT_EQ("-", GetString(*composition_));
993 
994   pos = composition_->InsertKeyAndPreeditAt(pos, "-", "-");
995   EXPECT_EQ(2, pos);
996   EXPECT_EQ("--", GetString(*composition_));
997 }
998 
TEST_F(CompositionTest,ShouldCommit)999 TEST_F(CompositionTest, ShouldCommit) {
1000   table_->AddRuleWithAttributes("ka", "[KA]", "", DIRECT_INPUT);
1001   table_->AddRuleWithAttributes("tt", "[X]", "t", DIRECT_INPUT);
1002   table_->AddRuleWithAttributes("ta", "[TA]", "", NO_TABLE_ATTRIBUTE);
1003 
1004   size_t pos = 0;
1005 
1006   pos = composition_->InsertAt(pos, "k");
1007   EXPECT_FALSE(composition_->ShouldCommit());
1008 
1009   pos = composition_->InsertAt(pos, "a");
1010   EXPECT_TRUE(composition_->ShouldCommit());
1011 
1012   pos = composition_->InsertAt(pos, "t");
1013   EXPECT_FALSE(composition_->ShouldCommit());
1014 
1015   pos = composition_->InsertAt(pos, "t");
1016   EXPECT_FALSE(composition_->ShouldCommit());
1017 
1018   pos = composition_->InsertAt(pos, "a");
1019   EXPECT_TRUE(composition_->ShouldCommit());
1020 
1021   pos = composition_->InsertAt(pos, "t");
1022   EXPECT_FALSE(composition_->ShouldCommit());
1023 
1024   pos = composition_->InsertAt(pos, "a");
1025   EXPECT_FALSE(composition_->ShouldCommit());
1026   EXPECT_EQ("[KA][X][TA][TA]", GetString(*composition_));
1027 }
1028 
TEST_F(CompositionTest,Issue2190364)1029 TEST_F(CompositionTest, Issue2190364) {
1030   // This is a unittest against http://b/2190364
1031   Table table;
1032   composition_->SetTable(&table);
1033 
1034   size_t pos = 0;
1035 
1036   composition_->SetInputMode(Transliterators::FULL_ASCII);
1037   pos = composition_->InsertKeyAndPreeditAt(pos, "a", "ち");
1038   EXPECT_EQ("a", GetString(*composition_));
1039 
1040   pos = composition_->InsertAt(pos, " ");
1041   EXPECT_EQ("a ", GetString(*composition_));
1042 }
1043 
1044 
TEST_F(CompositionTest,Issue1817410)1045 TEST_F(CompositionTest, Issue1817410) {
1046   // This is a unittest against http://b/2190364
1047   Table table;
1048   table.AddRule("ss", "っ", "s");
1049   composition_->SetTable(&table);
1050 
1051   size_t pos = 0;
1052   pos = composition_->InsertAt(pos, "s");
1053   pos = composition_->InsertAt(pos, "s");
1054 
1055   string preedit;
1056   composition_->GetString(&preedit);
1057   EXPECT_EQ("っs", preedit);
1058 
1059   EXPECT_EQ(0, composition_->ConvertPosition(0, Transliterators::LOCAL,
1060                                              Transliterators::HALF_ASCII));
1061   EXPECT_EQ(1, composition_->ConvertPosition(1, Transliterators::LOCAL,
1062                                              Transliterators::HALF_ASCII));
1063   EXPECT_EQ(2, composition_->ConvertPosition(2, Transliterators::LOCAL,
1064                                              Transliterators::HALF_ASCII));
1065 
1066   {  // "s|s"
1067     CharChunkList::iterator chunk_it;
1068     size_t inner_position;
1069     composition_->GetChunkAt(1, Transliterators::LOCAL,
1070                              &chunk_it, &inner_position);
1071     EXPECT_EQ(1, inner_position);
1072     EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::LOCAL));
1073 
1074     EXPECT_EQ(0, composition_->GetPosition(Transliterators::HALF_ASCII,
1075                                            chunk_it));
1076     EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::HALF_ASCII));
1077   }
1078 
1079   {  // "ss|"
1080     CharChunkList::iterator chunk_it;
1081     size_t inner_position;
1082     composition_->GetChunkAt(2, Transliterators::LOCAL,
1083                              &chunk_it, &inner_position);
1084     EXPECT_EQ(2, inner_position);
1085     EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::LOCAL));
1086 
1087     EXPECT_EQ(0, composition_->GetPosition(Transliterators::HALF_ASCII,
1088                                            chunk_it));
1089     EXPECT_EQ(2, (*chunk_it)->GetLength(Transliterators::HALF_ASCII));
1090   }
1091 }
1092 
TEST_F(CompositionTest,Issue2209634)1093 TEST_F(CompositionTest, Issue2209634) {
1094   // This is a unittest against http://b/2209634
1095   // "q@" becomes "qた@".
1096   Table table;
1097   table.AddRule("q", "", "た");
1098   table.AddRule("た@", "だ", "");
1099   composition_->SetTable(&table);
1100 
1101   composition_->SetInputMode(Transliterators::HALF_ASCII);
1102 
1103   size_t pos = 0;
1104   pos = composition_->InsertAt(pos, "q");
1105   pos = composition_->InsertAt(pos, "@");
1106 
1107   string preedit;
1108   composition_->GetString(&preedit);
1109   EXPECT_EQ("q@", preedit);
1110 }
1111 
TEST_F(CompositionTest,Issue2330530)1112 TEST_F(CompositionTest, Issue2330530) {
1113   // This is a unittest against http://b/2330530
1114   // "Win" + Numpad7 becomes "Win77" instead of "Win7".
1115   Table table;
1116   table.AddRule("wi", "うぃ", "");
1117   table.AddRule("i", "い", "");
1118   table.AddRule("n", "ん", "");
1119   table.AddRule("na", "な", "");
1120   composition_->SetTable(&table);
1121 
1122   composition_->SetInputMode(Transliterators::HALF_ASCII);
1123 
1124   size_t pos = 0;
1125   pos = composition_->InsertAt(pos, "W");
1126   pos = composition_->InsertAt(pos, "i");
1127   pos = composition_->InsertAt(pos, "n");
1128 
1129   string preedit;
1130   composition_->GetString(&preedit);
1131   EXPECT_EQ("Win", preedit);
1132 
1133   pos = composition_->InsertKeyAndPreeditAt(pos, "7", "7");
1134   composition_->GetString(&preedit);
1135   EXPECT_EQ("Win7", preedit);
1136 }
1137 
TEST_F(CompositionTest,Issue2819580)1138 TEST_F(CompositionTest, Issue2819580) {
1139   // This is a unittest against http://b/2819580.
1140   // 'y' after 'n' disappears.
1141   Table table;
1142   table.AddRule("po", "ぽ", "");
1143   table.AddRule("n", "ん", "");
1144   table.AddRule("na", "な", "");
1145   table.AddRule("ya", "や", "");
1146   table.AddRule("nya", "にゃ", "");
1147   table.AddRule("byo", "びょ", "");
1148 
1149   composition_->SetTable(&table);
1150 
1151   composition_->SetInputMode(Transliterators::HIRAGANA);
1152 
1153   size_t pos = 0;
1154   pos = composition_->InsertAt(pos, "n");
1155   pos = composition_->InsertAt(pos, "y");
1156   {
1157     string output;
1158     composition_->GetStringWithTrimMode(FIX, &output);
1159     EXPECT_EQ("んy", output);
1160 
1161     composition_->GetStringWithTrimMode(ASIS, &output);
1162     EXPECT_EQ("ny", output);
1163 
1164     composition_->GetStringWithTrimMode(TRIM, &output);
1165     EXPECT_EQ("", output);
1166   }
1167 }
1168 
TEST_F(CompositionTest,Issue2990253)1169 TEST_F(CompositionTest, Issue2990253) {
1170   // SplitChunk fails.
1171   // Ambiguous text is left in rhs CharChunk invalidly.
1172   Table table;
1173   table.AddRule("po", "ぽ", "");
1174   table.AddRule("n", "ん", "");
1175   table.AddRule("na", "な", "");
1176   table.AddRule("ya", "や", "");
1177   table.AddRule("nya", "にゃ", "");
1178   table.AddRule("byo", "びょ", "");
1179 
1180   composition_->SetTable(&table);
1181 
1182   composition_->SetInputMode(Transliterators::HIRAGANA);
1183 
1184   size_t pos = 0;
1185   pos = composition_->InsertAt(pos, "n");
1186   pos = composition_->InsertAt(pos, "y");
1187   pos = 1;
1188   pos = composition_->InsertAt(pos, "b");
1189   {
1190     string output;
1191     composition_->GetStringWithTrimMode(FIX, &output);
1192     EXPECT_EQ("んby", output);
1193 
1194     composition_->GetStringWithTrimMode(ASIS, &output);
1195     EXPECT_EQ("んby", output);
1196 
1197     composition_->GetStringWithTrimMode(TRIM, &output);
1198     // doubtful result. should be "ん"
1199     // May relate to http://b/2990358
1200     EXPECT_EQ("んb", output);
1201   }
1202 }
1203 
TEST_F(CompositionTest,InsertionIntoPreeditMakesInvalidText_1)1204 TEST_F(CompositionTest, InsertionIntoPreeditMakesInvalidText_1) {
1205   // http://b/2990358
1206   // Test for mainly Composition::InsertAt()
1207 
1208   Table table;
1209   table.AddRule("po", "ぽ", "");
1210   table.AddRule("n", "ん", "");
1211   table.AddRule("na", "な", "");
1212   table.AddRule("ya", "や", "");
1213   table.AddRule("nya", "にゃ", "");
1214   table.AddRule("byo", "びょ", "");
1215 
1216   composition_->SetTable(&table);
1217 
1218   composition_->SetInputMode(Transliterators::HIRAGANA);
1219 
1220   size_t pos = 0;
1221   pos = composition_->InsertAt(pos, "n");
1222   pos = composition_->InsertAt(pos, "y");
1223   pos = 1;
1224   pos = composition_->InsertAt(pos, "b");
1225   pos = 3;
1226   pos = composition_->InsertAt(pos, "o");
1227   {
1228     string output;
1229     composition_->GetStringWithTrimMode(FIX, &output);
1230     EXPECT_EQ("んびょ", output);
1231 
1232     composition_->GetStringWithTrimMode(ASIS, &output);
1233     EXPECT_EQ("んびょ", output);
1234 
1235     composition_->GetStringWithTrimMode(TRIM, &output);
1236     EXPECT_EQ("んびょ", output);
1237   }
1238 }
1239 
TEST_F(CompositionTest,InsertionIntoPreeditMakesInvalidText_2)1240 TEST_F(CompositionTest, InsertionIntoPreeditMakesInvalidText_2) {
1241   // http://b/2990358
1242   // Test for mainly Composition::InsertKeyAndPreeditAt()
1243 
1244   Table table;
1245   table.AddRule("す゛", "ず", "");
1246   table.AddRule("く゛", "ぐ", "");
1247   composition_->SetTable(&table);
1248 
1249   size_t pos = 0;
1250   pos = composition_->InsertKeyAndPreeditAt(pos, "m", "も");
1251   pos = composition_->InsertKeyAndPreeditAt(pos, "r", "す");
1252   pos = composition_->InsertKeyAndPreeditAt(pos, "h", "く");
1253   pos = composition_->InsertKeyAndPreeditAt(2, "@", "゛");
1254   pos = composition_->InsertKeyAndPreeditAt(5, "!", "!");
1255 
1256   string comp_str;
1257   composition_->GetString(&comp_str);
1258   EXPECT_EQ("もずく!", comp_str);
1259 
1260   string comp_ascii_str;
1261   composition_->GetStringWithTransliterator(
1262       Transliterators::RAW_STRING, &comp_ascii_str);
1263   EXPECT_EQ("mr@h!", comp_ascii_str);
1264 }
1265 
TEST_F(CompositionTest,CombinePendingChunks)1266 TEST_F(CompositionTest, CombinePendingChunks) {
1267   Table table;
1268   table.AddRule("po", "ぽ", "");
1269   table.AddRule("n", "ん", "");
1270   table.AddRule("na", "な", "");
1271   table.AddRule("ya", "や", "");
1272   table.AddRule("nya", "にゃ", "");
1273   table.AddRule("byo", "びょ", "");
1274 
1275   {
1276     // empty chunks + "n" -> empty chunks + "n"
1277     Composition comp(&table);
1278     comp.SetInputMode(Transliterators::HIRAGANA);
1279 
1280     size_t pos = 0;
1281 
1282     CharChunkList::iterator it;
1283     comp.MaybeSplitChunkAt(pos, &it);
1284     CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
1285 
1286     CompositionInput input;
1287     SetInput("n", "", false, &input);
1288     comp.CombinePendingChunks(chunk_it, input);
1289     EXPECT_EQ("", (*chunk_it)->pending());
1290     EXPECT_EQ("", (*chunk_it)->conversion());
1291     EXPECT_EQ("", (*chunk_it)->raw());
1292     EXPECT_EQ("", (*chunk_it)->ambiguous());
1293   }
1294   {
1295     // [x] + "n" -> [x] + "n"
1296     // No combination performed.
1297     Composition comp(&table);
1298     comp.SetInputMode(Transliterators::HIRAGANA);
1299 
1300     size_t pos = 0;
1301     pos = comp.InsertAt(pos, "x");
1302 
1303     CharChunkList::iterator it;
1304     comp.MaybeSplitChunkAt(pos, &it);
1305     CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
1306     CompositionInput input;
1307     SetInput("n", "", false, &input);
1308 
1309     comp.CombinePendingChunks(chunk_it, input);
1310     EXPECT_EQ("", (*chunk_it)->pending());
1311     EXPECT_EQ("", (*chunk_it)->conversion());
1312     EXPECT_EQ("", (*chunk_it)->raw());
1313     EXPECT_EQ("", (*chunk_it)->ambiguous());
1314   }
1315   {
1316     // Append "a" to [n][y] -> [ny] + "a"
1317     // Combination performed.
1318     Composition comp(&table);
1319     comp.SetInputMode(Transliterators::HIRAGANA);
1320 
1321     size_t pos = 0;
1322     pos = comp.InsertAt(pos, "y");
1323     pos = comp.InsertAt(0, "n");
1324 
1325     CharChunkList::iterator it;
1326     comp.MaybeSplitChunkAt(2, &it);
1327     CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
1328     CompositionInput input;
1329     SetInput("a", "", false, &input);
1330 
1331     comp.CombinePendingChunks(chunk_it, input);
1332     EXPECT_EQ("ny", (*chunk_it)->pending());
1333     EXPECT_EQ("", (*chunk_it)->conversion());
1334     EXPECT_EQ("ny", (*chunk_it)->raw());
1335     EXPECT_EQ("んy", (*chunk_it)->ambiguous());
1336   }
1337   {
1338     // Append "a" to [x][n][y] -> [x][ny] + "a"
1339     // Combination performed.
1340     Composition comp(&table);
1341     comp.SetTable(&table);
1342     comp.SetInputMode(Transliterators::HIRAGANA);
1343 
1344     size_t pos = 0;
1345     pos = comp.InsertAt(pos, "x");
1346     pos = comp.InsertAt(pos, "y");
1347     pos = comp.InsertAt(1, "n");
1348 
1349     CharChunkList::iterator it;
1350     comp.MaybeSplitChunkAt(3, &it);
1351     CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
1352     CompositionInput input;
1353     SetInput("a", "", false, &input);
1354 
1355     comp.CombinePendingChunks(chunk_it, input);
1356     EXPECT_EQ("ny", (*chunk_it)->pending());
1357     EXPECT_EQ("", (*chunk_it)->conversion());
1358     EXPECT_EQ("ny", (*chunk_it)->raw());
1359     EXPECT_EQ("んy", (*chunk_it)->ambiguous());
1360   }
1361 
1362   {
1363     // Append "a" of conversion value to [x][n][y] -> [x][ny] + "a"
1364     // Combination performed.  If composition input contains a
1365     // conversion, the conversion is used rather than a raw value.
1366     Composition comp(&table);
1367     comp.SetInputMode(Transliterators::HIRAGANA);
1368 
1369     size_t pos = 0;
1370     pos = comp.InsertAt(pos, "x");
1371     pos = comp.InsertAt(pos, "y");
1372     pos = comp.InsertAt(1, "n");
1373 
1374     CharChunkList::iterator it;
1375     comp.MaybeSplitChunkAt(3, &it);
1376     CharChunkList::iterator chunk_it = comp.GetInsertionChunk(&it);
1377     CompositionInput input;
1378     SetInput("x", "a", false, &input);
1379 
1380     comp.CombinePendingChunks(chunk_it, input);
1381     EXPECT_EQ("ny", (*chunk_it)->pending());
1382     EXPECT_EQ("", (*chunk_it)->conversion());
1383     EXPECT_EQ("ny", (*chunk_it)->raw());
1384   }
1385 }
1386 
TEST_F(CompositionTest,NewChunkBehaviors)1387 TEST_F(CompositionTest, NewChunkBehaviors) {
1388   Table table;
1389   table.AddRule("n", "", "ん");
1390   table.AddRule("na", "", "な");
1391   table.AddRuleWithAttributes("a", "", "あ", NEW_CHUNK);
1392   table.AddRule("ん*", "", "猫");
1393   table.AddRule("*", "", "");
1394   table.AddRule("ん#", "", "猫");
1395 
1396   composition_->SetTable(&table);
1397 
1398   CompositionInput input;
1399   {
1400     size_t pos = 0;
1401     composition_->Erase();
1402     SetInput("n", "", true, &input);
1403     pos = composition_->InsertInput(pos, input);
1404     SetInput("a", "", true, &input);
1405     pos = composition_->InsertInput(pos, input);
1406     EXPECT_EQ("nあ", GetString(*composition_));
1407   }
1408   {
1409     size_t pos = 0;
1410     composition_->Erase();
1411     SetInput("n", "", false, &input);
1412     pos = composition_->InsertInput(pos, input);
1413     SetInput("a", "", false, &input);
1414     pos = composition_->InsertInput(pos, input);
1415     EXPECT_EQ("な", GetString(*composition_));
1416   }
1417 
1418   {
1419     size_t pos = 0;
1420     composition_->Erase();
1421     SetInput("n", "", true, &input);
1422     pos = composition_->InsertInput(pos, input);
1423     SetInput("*", "", true, &input);
1424     pos = composition_->InsertInput(pos, input);
1425     EXPECT_EQ("猫", GetString(*composition_));
1426   }
1427   {
1428     size_t pos = 0;
1429     composition_->Erase();
1430     SetInput("n", "", false, &input);
1431     pos = composition_->InsertInput(pos, input);
1432     SetInput("*", "", false, &input);
1433     pos = composition_->InsertInput(pos, input);
1434     EXPECT_EQ("猫", GetString(*composition_));
1435   }
1436 
1437   {
1438     size_t pos = 0;
1439     composition_->Erase();
1440     SetInput("n", "", true, &input);
1441     pos = composition_->InsertInput(pos, input);
1442     SetInput("#", "", true, &input);
1443     pos = composition_->InsertInput(pos, input);
1444     EXPECT_EQ("n#", GetString(*composition_));
1445   }
1446   {
1447     size_t pos = 0;
1448     composition_->Erase();
1449     SetInput("n", "", false, &input);
1450     pos = composition_->InsertInput(pos, input);
1451     SetInput("#", "", false, &input);
1452     pos = composition_->InsertInput(pos, input);
1453     EXPECT_EQ("猫", GetString(*composition_));
1454   }
1455 
1456   {
1457     size_t pos = 0;
1458     composition_->Erase();
1459     SetInput("n", "", true, &input);
1460     pos = composition_->InsertInput(pos, input);
1461     SetInput("1", "", true, &input);
1462     pos = composition_->InsertInput(pos, input);
1463     EXPECT_EQ("n1", GetString(*composition_));
1464   }
1465   {
1466     size_t pos = 0;
1467     composition_->Erase();
1468     SetInput("n", "", false, &input);
1469     pos = composition_->InsertInput(pos, input);
1470     SetInput("1", "", false, &input);
1471     pos = composition_->InsertInput(pos, input);
1472     EXPECT_EQ("ん""1", GetString(*composition_));
1473   }
1474 }
1475 
TEST_F(CompositionTest,TwelveKeysInput)1476 TEST_F(CompositionTest, TwelveKeysInput) {
1477   // Simulates flick + toggle input mode.
1478 
1479   Table table;
1480   table.AddRule("n", "", "ん");
1481   table.AddRule("na", "", "な");
1482   table.AddRule("a", "", "あ");
1483   table.AddRule("*", "", "");
1484   table.AddRule("ほ*", "", "ぼ");
1485   table.AddRuleWithAttributes("7", "", "は", NEW_CHUNK);
1486   table.AddRule("は""7", "", "ひ");
1487   table.AddRule("ひ*", "", "び");
1488 
1489   composition_->SetTable(&table);
1490 
1491   CompositionInput input;
1492   size_t pos = 0;
1493 
1494   SetInput("n", "", true, &input);
1495   pos = composition_->InsertInput(pos, input);
1496 
1497   SetInput("a", "", false, &input);
1498   pos = composition_->InsertInput(pos, input);
1499 
1500   SetInput("7", "ほ", false, &input);
1501   pos = composition_->InsertInput(pos, input);
1502 
1503   SetInput("*", "", true, &input);
1504   pos = composition_->InsertInput(pos, input);
1505 
1506   SetInput("7", "ひ", false, &input);
1507   pos = composition_->InsertInput(pos, input);
1508 
1509   SetInput("7", "", true, &input);
1510   pos = composition_->InsertInput(pos, input);
1511 
1512   SetInput("7", "は", false, &input);
1513   pos = composition_->InsertInput(pos, input);
1514 
1515   SetInput("7", "", true, &input);
1516   pos = composition_->InsertInput(pos, input);
1517 
1518   SetInput("7", "は", false, &input);
1519   pos = composition_->InsertInput(pos, input);
1520 
1521   EXPECT_EQ("なぼひはははは", GetString(*composition_));
1522 }
1523 
TEST_F(CompositionTest,DifferentRulesForSamePendingWithSpecialKeys)1524 TEST_F(CompositionTest, DifferentRulesForSamePendingWithSpecialKeys) {
1525   table_->AddRule("4", "", "[ta]");
1526   table_->AddRule("[to]4", "", "[x]{#1}");
1527   table_->AddRule("[x]{#1}4", "", "[ta]");
1528 
1529   table_->AddRule("*", "", "");
1530   table_->AddRule("[tu]*", "", "[x]{#2}");
1531   table_->AddRule("[x]{#2}*", "", "[tu]");
1532 
1533   {
1534     composition_->Erase();
1535     size_t pos = 0;
1536     pos = composition_->InsertAt(pos, "[to]4");
1537     EXPECT_EQ(3, pos);
1538     EXPECT_EQ("[x]", GetString(*composition_));
1539     EXPECT_EQ("[to]4", GetRawString(*composition_));
1540 
1541     pos = composition_->InsertAt(pos, "4");
1542     EXPECT_EQ(4, pos);
1543     EXPECT_EQ("[ta]", GetString(*composition_));
1544     EXPECT_EQ("[to]44", GetRawString(*composition_));
1545   }
1546 
1547   {
1548     composition_->Erase();
1549     size_t pos = 0;
1550     pos = composition_->InsertAt(pos, "[to]4");
1551     EXPECT_EQ(3, pos);
1552     EXPECT_EQ("[x]", GetString(*composition_));
1553     EXPECT_EQ("[to]4", GetRawString(*composition_));
1554 
1555     pos = composition_->InsertAt(pos, "*");
1556     EXPECT_EQ(3, pos);
1557     EXPECT_EQ("[x]", GetString(*composition_));
1558     EXPECT_EQ("[to]4*", GetRawString(*composition_));
1559   }
1560 
1561   {
1562     composition_->Erase();
1563     size_t pos = 0;
1564     pos = composition_->InsertAt(pos, "[tu]*");
1565     EXPECT_EQ(3, pos);
1566     EXPECT_EQ("[x]", GetString(*composition_));
1567     EXPECT_EQ("[tu]*", GetRawString(*composition_));
1568 
1569     pos = composition_->InsertAt(pos, "*");
1570     EXPECT_EQ(4, pos);
1571     EXPECT_EQ("[tu]", GetString(*composition_));
1572     EXPECT_EQ("[tu]**", GetRawString(*composition_));
1573   }
1574 
1575   {
1576     composition_->Erase();
1577     size_t pos = 0;
1578     pos = composition_->InsertAt(pos, "[tu]*");
1579     EXPECT_EQ(3, pos);
1580     EXPECT_EQ("[x]", GetString(*composition_));
1581     EXPECT_EQ("[tu]*", GetRawString(*composition_));
1582 
1583     pos = composition_->InsertAt(pos, "*");
1584     EXPECT_EQ(4, pos);
1585     EXPECT_EQ("[tu]", GetString(*composition_));
1586     EXPECT_EQ("[tu]**", GetRawString(*composition_));
1587   }
1588 }
1589 
TEST_F(CompositionTest,LoopingRuleFor12KeysWithSpecialKeys)1590 TEST_F(CompositionTest, LoopingRuleFor12KeysWithSpecialKeys) {
1591   table_->AddRule("2", "", "a");
1592   table_->AddRule("a2", "", "b");
1593   table_->AddRule("b2", "", "c");
1594   table_->AddRule("c2", "", "{2}2");
1595   table_->AddRule("{2}22", "", "a");
1596 
1597   size_t pos = 0;
1598   pos = composition_->InsertAt(pos, "2");
1599   EXPECT_EQ("a", GetString(*composition_));
1600   EXPECT_EQ("2", GetRawString(*composition_));
1601 
1602   pos = composition_->InsertAt(pos, "2");
1603   EXPECT_EQ("b", GetString(*composition_));
1604   EXPECT_EQ("22", GetRawString(*composition_));
1605 
1606   pos = composition_->InsertAt(pos, "2");
1607   EXPECT_EQ("c", GetString(*composition_));
1608   EXPECT_EQ("222", GetRawString(*composition_));
1609 
1610   pos = composition_->InsertAt(pos, "2");
1611   EXPECT_EQ("2", GetString(*composition_));
1612   EXPECT_EQ("2222", GetRawString(*composition_));
1613 
1614   pos = composition_->InsertAt(pos, "2");
1615   EXPECT_EQ("a", GetString(*composition_));
1616   EXPECT_EQ("22222", GetRawString(*composition_));
1617 
1618   pos = composition_->InsertAt(pos, "2");
1619   EXPECT_EQ("b", GetString(*composition_));
1620   EXPECT_EQ("222222", GetRawString(*composition_));
1621 
1622   pos = composition_->InsertAt(pos, "2");
1623   EXPECT_EQ("c", GetString(*composition_));
1624   EXPECT_EQ("2222222", GetRawString(*composition_));
1625 }
1626 
TEST_F(CompositionTest,AlphanumericOfSSH)1627 TEST_F(CompositionTest, AlphanumericOfSSH) {
1628   // This is a unittest against http://b/3199626
1629   // 'ssh' (っsh) + F10 should be 'ssh'.
1630   Table table;
1631   table.AddRule("ss", "っ", "s");
1632   table.AddRule("shi", "し", "");
1633 
1634   composition_->SetTable(&table);
1635   composition_->SetInputMode(Transliterators::HIRAGANA);
1636   size_t pos = 0;
1637   pos = composition_->InsertAt(pos, "s");
1638   pos = composition_->InsertAt(pos, "s");
1639   pos = composition_->InsertAt(pos, "h");
1640   EXPECT_EQ(3, pos);
1641 
1642   string output;
1643   composition_->GetStringWithTrimMode(FIX, &output);
1644   EXPECT_EQ("っsh", output);
1645 }
1646 
TEST_F(CompositionTest,GrassHack)1647 TEST_F(CompositionTest, GrassHack) {
1648   Table table;
1649   table.AddRule("ww", "っ", "w");
1650   table.AddRule("we", "うぇ", "");
1651   table.AddRule("www", "w", "ww");
1652 
1653   composition_->SetTable(&table);
1654   composition_->SetInputMode(Transliterators::HIRAGANA);
1655   size_t pos = 0;
1656   pos = composition_->InsertAt(pos, "w");
1657   pos = composition_->InsertAt(pos, "w");
1658   pos = composition_->InsertAt(pos, "w");
1659 
1660   EXPECT_EQ("www", GetString(*composition_));
1661 
1662   pos = composition_->InsertAt(pos, "e");
1663   EXPECT_EQ("wっうぇ", GetString(*composition_));
1664 }
1665 
TEST_F(CompositionTest,RulesForFirstKeyEvents)1666 TEST_F(CompositionTest, RulesForFirstKeyEvents) {
1667   table_->AddRuleWithAttributes("a", "[A]", "", NEW_CHUNK);
1668   table_->AddRule("n", "[N]", "");
1669   table_->AddRule("nn", "[N]", "");
1670   table_->AddRule("na", "[NA]", "");
1671   table_->AddRuleWithAttributes("ni", "[NI]", "", NEW_CHUNK);
1672 
1673   CompositionInput input;
1674 
1675   {
1676     SetInput("a", "", true, &input);
1677     composition_->InsertInput(0, input);
1678     EXPECT_EQ("[A]", GetString(*composition_));
1679   }
1680 
1681   {
1682     composition_->Erase();
1683     CompositionInput input;
1684     SetInput("anaa", "", true, &input);
1685     composition_->InsertInput(0, input);
1686     EXPECT_EQ("[A][NA][A]", GetString(*composition_));
1687   }
1688 
1689   {
1690     composition_->Erase();
1691 
1692     CompositionInput input;
1693     SetInput("an", "", true, &input);
1694     const size_t position_an = composition_->InsertInput(0, input);
1695 
1696     SetInput("a", "", true, &input);
1697     composition_->InsertInput(position_an, input);
1698     EXPECT_EQ("[A]n[A]", GetString(*composition_));
1699 
1700     // This input should be treated as a part of "NA".
1701     SetInput("a", "", false, &input);
1702     composition_->InsertInput(position_an, input);
1703     EXPECT_EQ("[A][NA][A]", GetString(*composition_));
1704 
1705     string raw_t13n;
1706     composition_->GetStringWithTransliterator(
1707         Transliterators::RAW_STRING, &raw_t13n);
1708     EXPECT_EQ("anaa", raw_t13n);
1709   }
1710 
1711   {
1712     composition_->Erase();
1713 
1714     CompositionInput input;
1715     SetInput("an", "", true, &input);
1716     const size_t position_an = composition_->InsertInput(0, input);
1717 
1718     SetInput("ni", "", true, &input);
1719     composition_->InsertInput(position_an, input);
1720     EXPECT_EQ("[A]n[NI]", GetString(*composition_));
1721 
1722     string raw_t13n;
1723     composition_->GetStringWithTransliterator(
1724         Transliterators::RAW_STRING, &raw_t13n);
1725     EXPECT_EQ("anni", raw_t13n);
1726   }
1727 }
1728 
TEST_F(CompositionTest,NoTransliteration)1729 TEST_F(CompositionTest, NoTransliteration) {
1730   table_->AddRuleWithAttributes("0", "0", "", NO_TABLE_ATTRIBUTE);
1731   table_->AddRuleWithAttributes("1", "1", "", NO_TRANSLITERATION);
1732   table_->AddRuleWithAttributes("kk", "っ", "k", NO_TABLE_ATTRIBUTE);
1733   table_->AddRuleWithAttributes("ka", "か", "", NO_TRANSLITERATION);
1734   table_->AddRuleWithAttributes("ss", "っ", "s", NO_TRANSLITERATION);
1735   table_->AddRuleWithAttributes("sa", "さ", "", NO_TABLE_ATTRIBUTE);
1736   table_->AddRuleWithAttributes("tt", "っ", "t", NO_TRANSLITERATION);
1737   table_->AddRuleWithAttributes("ta", "た", "", NO_TRANSLITERATION);
1738 
1739   composition_->SetInputMode(Transliterators::FULL_KATAKANA);
1740 
1741   InsertCharacters("01kkassatta", 0, composition_.get());
1742   EXPECT_EQ("01ッカっさった", GetString(*composition_));
1743 }
1744 
TEST_F(CompositionTest,NoTransliteration_Issue3497962)1745 TEST_F(CompositionTest, NoTransliteration_Issue3497962) {
1746   table_->AddRuleWithAttributes("2", "", "a", NEW_CHUNK | NO_TRANSLITERATION);
1747   table_->AddRuleWithAttributes("a2", "", "b", NO_TABLE_ATTRIBUTE);
1748   table_->AddRuleWithAttributes("b2", "", "c", NO_TABLE_ATTRIBUTE);
1749   table_->AddRuleWithAttributes("c2", "", "{2}2", NO_TABLE_ATTRIBUTE);
1750   table_->AddRuleWithAttributes("{2}22", "", "a", NO_TABLE_ATTRIBUTE);
1751 
1752   composition_->SetInputMode(Transliterators::HIRAGANA);
1753 
1754   int pos = 0;
1755   pos = composition_->InsertAt(pos, "2");
1756   EXPECT_EQ("a", GetString(*composition_));
1757 
1758   pos = composition_->InsertAt(pos, "2");
1759   EXPECT_EQ("b", GetString(*composition_));
1760 }
1761 
TEST_F(CompositionTest,SetTransliteratorOnEmpty)1762 TEST_F(CompositionTest, SetTransliteratorOnEmpty) {
1763   composition_->SetTransliterator(0, 0, Transliterators::HIRAGANA);
1764   CompositionInput input;
1765   SetInput("a", "", true, &input);
1766   composition_->InsertInput(0, input);
1767   EXPECT_EQ(1, composition_->GetLength());
1768 }
1769 
TEST_F(CompositionTest,Clone)1770 TEST_F(CompositionTest, Clone) {
1771   Composition src(table_.get());
1772   src.SetInputMode(Transliterators::FULL_KATAKANA);
1773 
1774   AppendChunk("も", "", "mo", &src);
1775   AppendChunk("ず", "", "z", &src);
1776   AppendChunk("く", "", "c", &src);
1777 
1778   EXPECT_EQ(table_.get(), src.table());
1779   EXPECT_EQ(Transliterators::FULL_KATAKANA, src.input_t12r());
1780   EXPECT_EQ(3, src.chunks().size());
1781 
1782   std::unique_ptr<Composition> dest(src.CloneImpl());
1783   ASSERT_NE(nullptr, dest.get());
1784   EXPECT_EQ(src.table(), dest->table());
1785   EXPECT_EQ(src.input_t12r(), dest->input_t12r());
1786   ASSERT_EQ(src.chunks().size(), dest->chunks().size());
1787 
1788   CharChunkList::const_iterator src_it = src.chunks().begin();
1789   CharChunkList::const_iterator dest_it = dest->chunks().begin();
1790   for (; src_it != src.chunks().end(); ++src_it, ++dest_it) {
1791     EXPECT_EQ((*src_it)->raw(), (*dest_it)->raw());
1792   }
1793 }
1794 
1795 }  // namespace composer
1796 }  // namespace mozc
1797