1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "session/internal/candidate_list.h"
31 
32 #include <memory>
33 #include <string>
34 
35 #include "testing/base/public/gunit.h"
36 
37 namespace mozc {
38 namespace session {
39 
40 class CandidateListTest : public testing::Test {
41  protected:
SetUp()42   virtual void SetUp() {
43     main_list_.reset(new CandidateList(true));
44     sub_list_2_.reset(new CandidateList(true));
45     sub_sub_list_2_1_.reset(new CandidateList(false));
46 
47     main_list_->AddCandidate(0, "0");  // main0
48     main_list_->AddCandidate(1, "1");  // main1
49     sub_list_1_ = main_list_->AllocateSubCandidateList(false);  // main2
50     main_list_->AddCandidate(2, "2");  // main3
51     main_list_->AddCandidate(3, "3");  // main4
52     main_list_->AddCandidate(4, "4");  // main5
53     main_list_->AddCandidate(5, "5");  // main6
54     main_list_->AddSubCandidateList(sub_list_2_.get());  // main7
55     main_list_->AddCandidate(6, "6");  // main8
56     main_list_->AddCandidate(7, "7");  // main9
57     main_list_->AddCandidate(8, "8");  // main10
58     main_list_->AddCandidate(9, "9");  // main11
59     main_list_->AddCandidate(10, "10");  // main12
60 
61     sub_list_1_->AddCandidate(-1, "-1");  // sub10
62     sub_list_1_->AddCandidate(-2, "-2");  // sub11
63     sub_list_1_->AddCandidate(-3, "-3");  // sub12
64     sub_list_1_->AddCandidate(-4, "-4");  // sub13
65     sub_list_1_->AddCandidate(-5, "-5");  // sub14
66 
67     sub_list_2_->AddSubCandidateList(sub_sub_list_2_1_.get());  // sub20
68     sub_list_2_->AddCandidate(21, "21");  // sub21
69     sub_list_2_->AddCandidate(22, "22");  // sub22
70     sub_list_2_->AddCandidate(23, "23");  // sub23
71     sub_list_2_->AddCandidate(24, "24");  // sub24
72     sub_list_2_->AddCandidate(25, "25");  // sub25
73 
74     sub_sub_list_2_1_->AddCandidate(210, "210");  // subsub210
75     sub_sub_list_2_1_->AddCandidate(211, "211");  // subsub211
76     sub_sub_list_2_1_->AddCandidate(212, "212");  // subsub212
77   }
78 
79   std::unique_ptr<CandidateList> main_list_;
80   // sub_list_1_ will be initialized on the fly.
81   CandidateList *sub_list_1_;
82   std::unique_ptr<CandidateList> sub_list_2_;
83   std::unique_ptr<CandidateList> sub_sub_list_2_1_;
84 };
85 
TEST_F(CandidateListTest,MoveToId)86 TEST_F(CandidateListTest, MoveToId) {
87   // main0
88   EXPECT_TRUE(main_list_->MoveToId(0));
89   EXPECT_EQ(0, main_list_->focused_id());
90   EXPECT_EQ(0, main_list_->focused_index());
91 
92   // main1
93   EXPECT_TRUE(main_list_->MoveToId(1));
94   EXPECT_EQ(1, main_list_->focused_id());
95   EXPECT_EQ(1, main_list_->focused_index());
96 
97   // (main2, sub13)
98   EXPECT_TRUE(main_list_->MoveToId(-4));
99   EXPECT_EQ(-4, main_list_->focused_id());
100   EXPECT_EQ(2, main_list_->focused_index());
101   EXPECT_EQ(3, sub_list_1_->focused_index());
102 
103   // (main7, sub20, subsub210)
104   EXPECT_TRUE(main_list_->MoveToId(210));
105   EXPECT_EQ(210, main_list_->focused_id());
106   EXPECT_EQ(7, main_list_->focused_index());
107   EXPECT_EQ(0, sub_list_2_->focused_index());
108   EXPECT_EQ(0, sub_sub_list_2_1_->focused_index());
109 
110   // Invalid IDs
111   EXPECT_FALSE(main_list_->MoveToId(13));
112   EXPECT_FALSE(main_list_->MoveToId(-6));
113   EXPECT_FALSE(main_list_->MoveToId(9999));
114 }
115 
116 
TEST_F(CandidateListTest,MoveNext)117 TEST_F(CandidateListTest, MoveNext) {
118   // main0 -> main1
119   EXPECT_TRUE(main_list_->MoveToId(0));
120   EXPECT_TRUE(main_list_->MoveNext());
121   EXPECT_EQ(1, main_list_->focused_id());
122   EXPECT_EQ(1, main_list_->focused_index());
123 
124   // main1 -> (main2, sub10)
125   EXPECT_TRUE(main_list_->MoveNext());
126   EXPECT_EQ(-1, main_list_->focused_id());
127   EXPECT_EQ(2, main_list_->focused_index());
128   EXPECT_EQ(0, sub_list_1_->focused_index());
129 
130   // (main2, sub10) -> (main2, sub11)
131   EXPECT_TRUE(main_list_->MoveNext());
132   EXPECT_EQ(-2, main_list_->focused_id());
133   EXPECT_EQ(2, main_list_->focused_index());
134   EXPECT_EQ(1, sub_list_1_->focused_index());
135 
136   // (main2, sub14) -> main3: no rotation
137   EXPECT_TRUE(main_list_->MoveToId(-5));
138   EXPECT_TRUE(main_list_->MoveNext());
139   EXPECT_EQ(2, main_list_->focused_id());
140   EXPECT_EQ(3, main_list_->focused_index());
141 
142   // (main7, sub25) -> (main7, sub20, subsub210): rotation
143   EXPECT_TRUE(main_list_->MoveToId(25));
144   EXPECT_TRUE(main_list_->MoveNext());
145   EXPECT_EQ(210, main_list_->focused_id());
146   EXPECT_EQ(7, main_list_->focused_index());
147   EXPECT_EQ(0, sub_list_2_->focused_index());
148   EXPECT_EQ(0, sub_sub_list_2_1_->focused_index());
149 
150   // (main7, sub20, subsub210) -> (main7, sub20, subsub211)
151   EXPECT_TRUE(main_list_->MoveNext());
152   EXPECT_EQ(211, main_list_->focused_id());
153   EXPECT_EQ(7, main_list_->focused_index());
154   EXPECT_EQ(0, sub_list_2_->focused_index());
155   EXPECT_EQ(1, sub_sub_list_2_1_->focused_index());
156 }
157 
TEST_F(CandidateListTest,MovePrev)158 TEST_F(CandidateListTest, MovePrev) {
159   // main1 -> main0
160   EXPECT_TRUE(main_list_->MoveToId(1));
161   EXPECT_TRUE(main_list_->MovePrev());
162   EXPECT_EQ(0, main_list_->focused_id());
163   EXPECT_EQ(0, main_list_->focused_index());
164 
165   // (main2, sub10) -> main1: no rotation
166   EXPECT_TRUE(main_list_->MoveToId(-1));
167   EXPECT_TRUE(main_list_->MovePrev());
168   EXPECT_EQ(1, main_list_->focused_id());
169 
170   // (main7, sub20, subsub210) -> (main7, sub25)
171   EXPECT_TRUE(main_list_->MoveToId(210));
172   EXPECT_TRUE(main_list_->MovePrev());
173   EXPECT_EQ(25, main_list_->focused_id());
174 }
175 
TEST_F(CandidateListTest,MoveNextPage)176 TEST_F(CandidateListTest, MoveNextPage) {
177   // main3 -> main9
178   EXPECT_TRUE(main_list_->MoveToId(2));
179   EXPECT_TRUE(main_list_->MoveNextPage());
180   EXPECT_EQ(7, main_list_->focused_id());
181 
182   // main9 -> main0
183   EXPECT_TRUE(main_list_->MoveNextPage());
184   EXPECT_EQ(0, main_list_->focused_id());
185 
186   // (main2, sub10) -> main9: no ratation
187   EXPECT_TRUE(main_list_->MoveToId(-1));
188   EXPECT_TRUE(main_list_->MoveNextPage());
189   EXPECT_EQ(7, main_list_->focused_id());
190 
191   // (main7, sub20, subsub210) -> (main7, sub20, subsub210)
192   EXPECT_TRUE(main_list_->MoveToId(210));
193   EXPECT_TRUE(main_list_->MoveNextPage());
194   EXPECT_EQ(210, main_list_->focused_id());
195 }
196 
TEST_F(CandidateListTest,MovePrevPage)197 TEST_F(CandidateListTest, MovePrevPage) {
198   // main3 -> main9
199   EXPECT_TRUE(main_list_->MoveToId(2));
200   EXPECT_TRUE(main_list_->MovePrevPage());
201   EXPECT_EQ(7, main_list_->focused_id());
202 
203   // main9 -> main0
204   EXPECT_TRUE(main_list_->MovePrevPage());
205   EXPECT_EQ(0, main_list_->focused_id());
206 
207   // (main2, sub10) -> main9: no ratation
208   EXPECT_TRUE(main_list_->MoveToId(-1));
209   EXPECT_TRUE(main_list_->MovePrevPage());
210   EXPECT_EQ(7, main_list_->focused_id());
211 
212   // (main7, sub20, subsub210) -> (main7, sub20, subsub210)
213   EXPECT_TRUE(main_list_->MoveToId(210));
214   EXPECT_TRUE(main_list_->MovePrevPage());
215   EXPECT_EQ(210, main_list_->focused_id());
216 }
217 
TEST_F(CandidateListTest,MoveToPageIndex)218 TEST_F(CandidateListTest, MoveToPageIndex) {
219   EXPECT_TRUE(main_list_->MoveToId(0));
220 
221   // main1
222   EXPECT_TRUE(main_list_->MoveToPageIndex(1));
223   EXPECT_EQ(1, main_list_->focused_id());
224 
225   // (main2, sub10)
226   EXPECT_TRUE(main_list_->MoveToPageIndex(2));
227   EXPECT_EQ(-1, main_list_->focused_id());
228 
229   // main12
230   EXPECT_TRUE(main_list_->MoveToId(10));
231 
232   // invalid index
233   EXPECT_FALSE(main_list_->MoveToPageIndex(7));
234 }
235 
TEST_F(CandidateListTest,Clear)236 TEST_F(CandidateListTest, Clear) {
237   EXPECT_TRUE(main_list_->MoveToId(0));
238 
239   main_list_->Clear();
240   EXPECT_FALSE(main_list_->MoveToId(0));
241   EXPECT_EQ(0, main_list_->size());
242 
243   main_list_->AddCandidate(500, "500");
244   main_list_->AddCandidate(501, "501");
245 
246   EXPECT_TRUE(main_list_->MoveNext());
247   EXPECT_EQ(501, main_list_->focused_id());
248   EXPECT_EQ(1, main_list_->focused_index());
249 }
250 
TEST_F(CandidateListTest,Duplication)251 TEST_F(CandidateListTest, Duplication) {
252   CandidateList main_list(true);
253   CandidateList sub_list(true);
254 
255   main_list.AddCandidate(0, "0");
256   main_list.AddCandidate(1, "1");
257   main_list.AddCandidate(2, "2");
258   main_list.AddCandidate(3, "0");  // dup
259   main_list.AddCandidate(4, "0");  // dup
260   main_list.AddCandidate(5, "1");  // dup
261   main_list.AddSubCandidateList(&sub_list);
262   sub_list.AddCandidate(6, "0");  // not dup
263   sub_list.AddCandidate(7, "7");
264   sub_list.AddCandidate(8, "7");  // dup
265 
266   EXPECT_EQ(4, main_list.size());
267   EXPECT_EQ(2, sub_list.size());
268 
269   main_list.MoveToId(3);
270   EXPECT_EQ(0, main_list.focused_id());
271 
272   main_list.MoveToId(4);
273   EXPECT_EQ(0, main_list.focused_id());
274 
275   main_list.MoveToId(5);
276   EXPECT_EQ(1, main_list.focused_id());
277 
278   main_list.MoveToId(6);
279   EXPECT_EQ(6, main_list.focused_id());
280 
281   main_list.MoveToId(8);
282   EXPECT_EQ(7, main_list.focused_id());
283 }
284 
TEST_F(CandidateListTest,FocusedId)285 TEST_F(CandidateListTest, FocusedId) {
286   CandidateList empty_list(true);
287   EXPECT_EQ(0, empty_list.focused_id());
288 }
289 
TEST_F(CandidateListTest,set_page_size)290 TEST_F(CandidateListTest, set_page_size) {
291   EXPECT_EQ(9, main_list_->page_size());
292   // Move to the 10th item.
293   EXPECT_TRUE(main_list_->MoveToId(7));
294 
295   // Make sure the default values.
296   EXPECT_EQ(7, main_list_->focused_id());
297   EXPECT_EQ(9, main_list_->focused_index());
298   size_t begin, end;
299   main_list_->GetPageRange(main_list_->focused_index(), &begin, &end);
300   EXPECT_EQ(9, begin);
301   EXPECT_EQ(12, end);  // The last index.
302 
303   // Change the page size.
304   main_list_->set_page_size(11);
305   // The id and index should not be changed.
306   EXPECT_EQ(7, main_list_->focused_id());
307   EXPECT_EQ(9, main_list_->focused_index());
308 
309   // The begin and end should be changed.
310   main_list_->GetPageRange(main_list_->focused_index(), &begin, &end);
311   EXPECT_EQ(0, begin);
312   EXPECT_EQ(10, end);
313 }
314 
TEST_F(CandidateListTest,Attributes)315 TEST_F(CandidateListTest, Attributes) {
316   CandidateList main_list(true);
317 
318   main_list.AddCandidateWithAttributes(0, "hiragana", HIRAGANA);
319   main_list.AddCandidateWithAttributes(1, "f_katakana", FULL_WIDTH | KATAKANA);
320   main_list.AddCandidateWithAttributes(2, "h_ascii",
321                                        (HALF_WIDTH | ASCII | LOWER));
322   main_list.AddCandidateWithAttributes(3, "H_ASCII",
323                                        (HALF_WIDTH | ASCII | UPPER));
324   // dup entry
325   main_list.AddCandidateWithAttributes(4, "h_ascii",
326                                        (HALF_WIDTH | ASCII | LOWER));
327   main_list.AddCandidateWithAttributes(5, "H_Ascii", (HALF_WIDTH | ASCII));
328   main_list.AddCandidateWithAttributes(6, "f_ascii",
329                                        (FULL_WIDTH | ASCII | LOWER));
330 
331   EXPECT_EQ(6, main_list.size());
332 
333   EXPECT_FALSE(main_list.MoveNextAttributes(HALF_WIDTH | KATAKANA));
334 
335   // h_ascii
336   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
337   EXPECT_EQ(2, main_list.focused_id());
338   EXPECT_EQ((HALF_WIDTH | ASCII | LOWER),
339             main_list.focused_candidate().attributes());
340 
341   // H_ASCII
342   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
343   EXPECT_EQ(3, main_list.focused_id());
344   EXPECT_EQ((HALF_WIDTH | ASCII | UPPER),
345             main_list.focused_candidate().attributes());
346 
347   // ID:4 (h_ascii) should be skipped because it's a dup.
348 
349   // H_Ascii
350   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
351   EXPECT_EQ(5, main_list.focused_id());
352   EXPECT_EQ((HALF_WIDTH | ASCII), main_list.focused_candidate().attributes());
353 
354   // h_ascii / Looped
355   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
356   EXPECT_EQ(2, main_list.focused_id());
357 }
358 
TEST_F(CandidateListTest,Attributes2)359 TEST_F(CandidateListTest, Attributes2) {
360   CandidateList main_list(true);
361 
362   main_list.AddCandidate(0, "dvd");
363   main_list.AddCandidate(1, "DVD");
364   main_list.AddCandidateWithAttributes(2, "f_dvd", HIRAGANA);
365   // id#3 is dup
366   main_list.AddCandidateWithAttributes(3, "f_dvd", FULL_WIDTH | KATAKANA);
367   main_list.AddCandidateWithAttributes(4, "h_dvd",
368                                        (HALF_WIDTH | ASCII));
369   // id#5 is dup
370   main_list.AddCandidateWithAttributes(5, "h_dvd",
371                                        (HALF_WIDTH | ASCII | LOWER));
372   main_list.AddCandidateWithAttributes(6, "h_DVD",
373                                        (HALF_WIDTH | ASCII | UPPER));
374   main_list.AddCandidateWithAttributes(7, "h_Dvd",
375                                        (HALF_WIDTH | ASCII | CAPITALIZED));
376   // id#8 is dup
377   main_list.AddCandidateWithAttributes(8, "f_dvd",
378                                        (FULL_WIDTH | ASCII));
379   // id#9 is dup
380   main_list.AddCandidateWithAttributes(9, "f_dvd",
381                                        (FULL_WIDTH | ASCII | LOWER));
382   main_list.AddCandidateWithAttributes(10, "f_DVD",
383                                        (FULL_WIDTH | ASCII | UPPER));
384   main_list.AddCandidateWithAttributes(11, "f_Dvd",
385                                        (FULL_WIDTH | ASCII | CAPITALIZED));
386   // id#12 is dup
387   main_list.AddCandidateWithAttributes(12, "h_dvd", HALF_WIDTH | KATAKANA);
388 
389   EXPECT_EQ(8, main_list.size());
390 
391   EXPECT_TRUE(main_list.MoveNextAttributes(FULL_WIDTH | ASCII));
392 
393   EXPECT_EQ(2, main_list.focused_id());
394   EXPECT_EQ((HIRAGANA | KATAKANA | FULL_WIDTH | ASCII | LOWER),
395             main_list.focused_candidate().attributes());
396 
397   EXPECT_TRUE(main_list.MoveNextAttributes(FULL_WIDTH | ASCII));
398   EXPECT_EQ(10, main_list.focused_id());
399   EXPECT_EQ((FULL_WIDTH | ASCII | UPPER),
400             main_list.focused_candidate().attributes());
401 
402   EXPECT_TRUE(main_list.MoveNextAttributes(FULL_WIDTH | ASCII));
403   EXPECT_EQ(11, main_list.focused_id());
404   EXPECT_EQ((FULL_WIDTH | ASCII | CAPITALIZED),
405             main_list.focused_candidate().attributes());
406 
407   EXPECT_TRUE(main_list.MoveNextAttributes(FULL_WIDTH | ASCII));
408   EXPECT_EQ(2, main_list.focused_id());
409 }
410 
TEST_F(CandidateListTest,AttributesWithSubList)411 TEST_F(CandidateListTest, AttributesWithSubList) {
412   CandidateList main_list(true);
413   main_list.AddCandidate(0, "kanji");
414 
415   CandidateList *sub_list = main_list.AllocateSubCandidateList(false);
416   sub_list->AddCandidateWithAttributes(1, "hiragana", HIRAGANA);
417   sub_list->AddCandidateWithAttributes(2, "f_katakana", FULL_WIDTH | KATAKANA);
418   sub_list->AddCandidateWithAttributes(3, "h_ascii",
419                                        (HALF_WIDTH | ASCII | LOWER));
420   sub_list->AddCandidateWithAttributes(4, "H_ASCII",
421                                        (HALF_WIDTH | ASCII | UPPER));
422   sub_list->AddCandidateWithAttributes(5, "H_Ascii",
423                                        (HALF_WIDTH | ASCII | CAPITALIZED));
424   EXPECT_EQ(2, main_list.size());
425   EXPECT_EQ(5, sub_list->size());
426 
427   // h_ascii
428   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
429   EXPECT_EQ(3, main_list.focused_id());
430   EXPECT_EQ((HALF_WIDTH | ASCII | LOWER),
431             sub_list->focused_candidate().attributes());
432 
433   // H_ASCII
434   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
435   EXPECT_EQ(4, main_list.focused_id());
436   EXPECT_EQ((HALF_WIDTH | ASCII | UPPER),
437             sub_list->focused_candidate().attributes());
438 
439   // H_Ascii
440   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
441   EXPECT_EQ(5, main_list.focused_id());
442   EXPECT_EQ((HALF_WIDTH | ASCII | CAPITALIZED),
443             sub_list->focused_candidate().attributes());
444 
445   // h_ascii / Looped
446   EXPECT_TRUE(main_list.MoveNextAttributes(HALF_WIDTH | ASCII));
447   EXPECT_EQ(3, main_list.focused_id());
448   EXPECT_EQ((HALF_WIDTH | ASCII | LOWER),
449             sub_list->focused_candidate().attributes());
450 }
451 
TEST_F(CandidateListTest,GetDeepestFocusedCandidate)452 TEST_F(CandidateListTest, GetDeepestFocusedCandidate) {
453   EXPECT_TRUE(main_list_->MoveToPageIndex(2));
454   EXPECT_EQ(0, main_list_->focused_candidate().id());
455   EXPECT_TRUE(main_list_->focused_candidate().IsSubcandidateList());
456 
457   const Candidate &deepest_candidate = main_list_->GetDeepestFocusedCandidate();
458   EXPECT_EQ(-1, deepest_candidate.id());
459   EXPECT_FALSE(deepest_candidate.IsSubcandidateList());
460 }
461 
TEST_F(CandidateListTest,NextAvailableId)462 TEST_F(CandidateListTest, NextAvailableId) {
463   EXPECT_EQ(213, main_list_->next_available_id());
464   EXPECT_EQ(0, sub_list_1_->next_available_id());
465   EXPECT_EQ(213, sub_list_2_->next_available_id());
466   EXPECT_EQ(213, sub_sub_list_2_1_->next_available_id());
467 
468   // Append duplicate candidate.
469   // next_available_id should be incremented.
470   sub_sub_list_2_1_->AddCandidate(213, "212");
471   EXPECT_EQ(214, main_list_->next_available_id());
472   EXPECT_EQ(214, sub_sub_list_2_1_->next_available_id());
473 }
474 
475 }  // namespace session
476 }  // namespace mozc
477