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