1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/input_method/candidate_window_view.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/chromeos/input_method/candidate_view.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/views/test/views_test_base.h"
13 #include "ui/views/widget/widget.h"
16 namespace input_method
{
20 void ClearInputMethodLookupTable(size_t page_size
,
21 InputMethodLookupTable
* table
) {
22 table
->visible
= false;
23 table
->cursor_absolute_index
= 0;
24 table
->page_size
= page_size
;
25 table
->candidates
.clear();
26 table
->orientation
= InputMethodLookupTable::kVertical
;
27 table
->labels
.clear();
28 table
->annotations
.clear();
29 table
->mozc_candidates
.Clear();
32 void InitializeMozcCandidates(InputMethodLookupTable
* table
) {
33 table
->mozc_candidates
.Clear();
34 table
->mozc_candidates
.set_position(0);
35 table
->mozc_candidates
.set_size(0);
38 void AppendCandidateIntoLookupTable(InputMethodLookupTable
* table
,
39 const std::string
& value
) {
40 mozc::commands::Candidates::Candidate
*candidate
=
41 table
->mozc_candidates
.add_candidate();
43 int current_entry_count
= table
->mozc_candidates
.candidate_size();
44 table
->candidates
.push_back(value
);
45 candidate
->set_index(current_entry_count
);
46 candidate
->set_value(value
);
47 candidate
->set_id(current_entry_count
);
48 candidate
->set_information_id(current_entry_count
);
53 class CandidateWindowViewTest
: public views::ViewsTestBase
{
55 void ExpectLabels(const std::string shortcut
,
56 const std::string candidate
,
57 const std::string annotation
,
58 const CandidateView
* row
) {
59 EXPECT_EQ(shortcut
, UTF16ToUTF8(row
->shortcut_label_
->text()));
60 EXPECT_EQ(candidate
, UTF16ToUTF8(row
->candidate_label_
->text()));
61 EXPECT_EQ(annotation
, UTF16ToUTF8(row
->annotation_label_
->text()));
65 TEST_F(CandidateWindowViewTest
, ShouldUpdateCandidateViewsTest
) {
66 // This test verifies the process of judging update lookup-table or not.
67 // This judgement is handled by ShouldUpdateCandidateViews, which returns true
68 // if update is necessary and vice versa.
69 const char* kSampleCandidate1
= "Sample Candidate 1";
70 const char* kSampleCandidate2
= "Sample Candidate 2";
71 const char* kSampleCandidate3
= "Sample Candidate 3";
73 const char* kSampleAnnotation1
= "Sample Annotation 1";
74 const char* kSampleAnnotation2
= "Sample Annotation 2";
75 const char* kSampleAnnotation3
= "Sample Annotation 3";
77 const char* kSampleLabel1
= "Sample Label 1";
78 const char* kSampleLabel2
= "Sample Label 2";
79 const char* kSampleLabel3
= "Sample Label 3";
81 InputMethodLookupTable old_table
;
82 InputMethodLookupTable new_table
;
84 const size_t kPageSize
= 10;
86 ClearInputMethodLookupTable(kPageSize
, &old_table
);
87 ClearInputMethodLookupTable(kPageSize
, &new_table
);
89 old_table
.visible
= true;
90 old_table
.cursor_absolute_index
= 0;
91 old_table
.page_size
= 1;
92 old_table
.candidates
.clear();
93 old_table
.orientation
= InputMethodLookupTable::kVertical
;
94 old_table
.labels
.clear();
95 old_table
.annotations
.clear();
97 new_table
= old_table
;
99 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
102 new_table
.visible
= false;
103 // Visibility would be ignored.
104 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
106 new_table
= old_table
;
107 new_table
.candidates
.push_back(kSampleCandidate1
);
108 old_table
.candidates
.push_back(kSampleCandidate1
);
109 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
111 new_table
.labels
.push_back(kSampleLabel1
);
112 old_table
.labels
.push_back(kSampleLabel1
);
113 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
115 new_table
.annotations
.push_back(kSampleAnnotation1
);
116 old_table
.annotations
.push_back(kSampleAnnotation1
);
117 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
120 new_table
.cursor_absolute_index
= 1;
121 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
123 new_table
= old_table
;
125 new_table
.page_size
= 2;
126 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
128 new_table
= old_table
;
130 new_table
.orientation
= InputMethodLookupTable::kHorizontal
;
131 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
134 new_table
= old_table
;
135 new_table
.candidates
.push_back(kSampleCandidate2
);
136 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
138 old_table
.candidates
.push_back(kSampleCandidate3
);
139 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
141 new_table
.candidates
.clear();
142 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
144 new_table
.candidates
.push_back(kSampleCandidate2
);
145 old_table
.candidates
.clear();
146 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
149 new_table
= old_table
;
150 new_table
.labels
.push_back(kSampleLabel2
);
151 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
153 old_table
.labels
.push_back(kSampleLabel3
);
154 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
156 new_table
.labels
.clear();
157 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
159 new_table
.labels
.push_back(kSampleLabel2
);
160 old_table
.labels
.clear();
161 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
164 new_table
= old_table
;
165 new_table
.annotations
.push_back(kSampleAnnotation2
);
166 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
168 old_table
.annotations
.push_back(kSampleAnnotation3
);
169 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
171 new_table
.annotations
.clear();
172 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
174 new_table
.annotations
.push_back(kSampleAnnotation2
);
175 old_table
.annotations
.clear();
176 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
180 TEST_F(CandidateWindowViewTest
, MozcSuggestWindowShouldUpdateTest
) {
181 // ShouldUpdateCandidateViews method should also judge with consideration of
182 // the mozc specific candidate information. Following tests verify them.
183 const char* kSampleCandidate1
= "Sample Candidate 1";
184 const char* kSampleCandidate2
= "Sample Candidate 2";
186 const size_t kPageSize
= 10;
188 InputMethodLookupTable old_table
;
189 InputMethodLookupTable new_table
;
191 // State chagne from using non-mozc candidate to mozc candidate.
192 ClearInputMethodLookupTable(kPageSize
, &old_table
);
193 ClearInputMethodLookupTable(kPageSize
, &new_table
);
195 old_table
.candidates
.push_back(kSampleCandidate1
);
196 InitializeMozcCandidates(&new_table
);
197 AppendCandidateIntoLookupTable(&new_table
, kSampleCandidate2
);
199 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
202 // State change from using mozc candidate to non-mozc candidate
203 ClearInputMethodLookupTable(kPageSize
, &old_table
);
204 ClearInputMethodLookupTable(kPageSize
, &new_table
);
206 InitializeMozcCandidates(&old_table
);
207 AppendCandidateIntoLookupTable(&old_table
, kSampleCandidate1
);
209 new_table
.candidates
.push_back(kSampleCandidate2
);
211 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
214 // State change from using mozc candidate to mozc candidate
217 ClearInputMethodLookupTable(kPageSize
, &old_table
);
218 ClearInputMethodLookupTable(kPageSize
, &new_table
);
220 InitializeMozcCandidates(&old_table
);
221 AppendCandidateIntoLookupTable(&old_table
, kSampleCandidate1
);
223 InitializeMozcCandidates(&new_table
);
224 AppendCandidateIntoLookupTable(&new_table
, kSampleCandidate1
);
226 EXPECT_FALSE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
228 // Candidate contents
229 ClearInputMethodLookupTable(kPageSize
, &old_table
);
230 ClearInputMethodLookupTable(kPageSize
, &new_table
);
232 InitializeMozcCandidates(&old_table
);
233 AppendCandidateIntoLookupTable(&old_table
, kSampleCandidate1
);
235 InitializeMozcCandidates(&new_table
);
236 AppendCandidateIntoLookupTable(&new_table
, kSampleCandidate2
);
238 EXPECT_TRUE(CandidateWindowView::ShouldUpdateCandidateViews(old_table
,
242 TEST_F(CandidateWindowViewTest
, MozcUpdateCandidateTest
) {
243 // This test verifies whether UpdateCandidates function updates window mozc
244 // specific candidate position correctly on the correct condition.
246 // For testing, we have to prepare empty widget.
247 // We should NOT manually free widget by default, otherwise double free will
248 // be occurred. So, we should instantiate widget class with "new" operation.
249 views::Widget
* widget
= new views::Widget
;
250 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
251 widget
->Init(params
);
253 CandidateWindowView
candidate_window_view(widget
);
254 candidate_window_view
.Init();
256 const size_t kPageSize
= 10;
258 InputMethodLookupTable new_table
;
259 ClearInputMethodLookupTable(kPageSize
, &new_table
);
260 InitializeMozcCandidates(&new_table
);
262 // If candidate category is SUGGESTION, should not show at composition head.
263 new_table
.mozc_candidates
.set_category(mozc::commands::CONVERSION
);
264 candidate_window_view
.UpdateCandidates(new_table
);
265 EXPECT_FALSE(candidate_window_view
.should_show_at_composition_head_
);
267 // If candidate category is SUGGESTION, should show at composition head.
268 new_table
.mozc_candidates
.set_category(mozc::commands::SUGGESTION
);
269 candidate_window_view
.UpdateCandidates(new_table
);
270 EXPECT_TRUE(candidate_window_view
.should_show_at_composition_head_
);
272 // We should call CloseNow method, otherwise this test will leak memory.
276 TEST_F(CandidateWindowViewTest
, ShortcutSettingTest
) {
277 const char* kSampleCandidate
[] = {
278 "Sample Candidate 1",
279 "Sample Candidate 2",
282 const char* kSampleAnnotation
[] = {
283 "Sample Annotation 1",
284 "Sample Annotation 2",
285 "Sample Annotation 3"
287 const char* kEmptyLabel
= "";
288 const char* kDefaultVerticalLabel
[] = { "1", "2", "3" };
289 const char* kDefaultHorizontalLabel
[] = { "1.", "2.", "3." };
290 const char* kCustomizedLabel
[] = { "a", "s", "d" };
291 const char* kExpectedHorizontalCustomizedLabel
[] = { "a.", "s.", "d." };
293 views::Widget
* widget
= new views::Widget
;
294 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
295 widget
->Init(params
);
297 CandidateWindowView
candidate_window_view(widget
);
298 candidate_window_view
.Init();
301 SCOPED_TRACE("candidate_views allocation test");
302 const size_t kMaxPageSize
= 16;
303 for (size_t i
= 1; i
< kMaxPageSize
; ++i
) {
304 InputMethodLookupTable table
;
305 ClearInputMethodLookupTable(i
, &table
);
306 candidate_window_view
.UpdateCandidates(table
);
307 EXPECT_EQ(i
, candidate_window_view
.candidate_views_
.size());
311 SCOPED_TRACE("Empty labels expects default label(vertical)");
312 const size_t kPageSize
= 3;
313 InputMethodLookupTable table
;
314 ClearInputMethodLookupTable(kPageSize
, &table
);
316 table
.orientation
= InputMethodLookupTable::kVertical
;
317 for (size_t i
= 0; i
< kPageSize
; ++i
) {
318 table
.candidates
.push_back(kSampleCandidate
[i
]);
319 table
.annotations
.push_back(kSampleAnnotation
[i
]);
322 table
.labels
.clear();
324 candidate_window_view
.UpdateCandidates(table
);
326 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
327 for (size_t i
= 0; i
< kPageSize
; ++i
) {
328 ExpectLabels(kDefaultVerticalLabel
[i
],
330 kSampleAnnotation
[i
],
331 candidate_window_view
.candidate_views_
[i
]);
335 SCOPED_TRACE("Empty string for each labels expects empty labels(vertical)");
336 const size_t kPageSize
= 3;
337 InputMethodLookupTable table
;
338 ClearInputMethodLookupTable(kPageSize
, &table
);
340 table
.orientation
= InputMethodLookupTable::kVertical
;
341 for (size_t i
= 0; i
< kPageSize
; ++i
) {
342 table
.candidates
.push_back(kSampleCandidate
[i
]);
343 table
.annotations
.push_back(kSampleAnnotation
[i
]);
344 table
.labels
.push_back(kEmptyLabel
);
347 candidate_window_view
.UpdateCandidates(table
);
349 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
350 for (size_t i
= 0; i
< kPageSize
; ++i
) {
351 ExpectLabels(kEmptyLabel
, kSampleCandidate
[i
], kSampleAnnotation
[i
],
352 candidate_window_view
.candidate_views_
[i
]);
356 SCOPED_TRACE("Empty labels expects default label(horizontal)");
357 const size_t kPageSize
= 3;
358 InputMethodLookupTable table
;
359 ClearInputMethodLookupTable(kPageSize
, &table
);
361 table
.orientation
= InputMethodLookupTable::kHorizontal
;
362 for (size_t i
= 0; i
< kPageSize
; ++i
) {
363 table
.candidates
.push_back(kSampleCandidate
[i
]);
364 table
.annotations
.push_back(kSampleAnnotation
[i
]);
367 table
.labels
.clear();
369 candidate_window_view
.UpdateCandidates(table
);
371 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
372 for (size_t i
= 0; i
< kPageSize
; ++i
) {
373 ExpectLabels(kDefaultHorizontalLabel
[i
],
375 kSampleAnnotation
[i
],
376 candidate_window_view
.candidate_views_
[i
]);
381 "Empty string for each labels expect empty labels(horizontal)");
382 const size_t kPageSize
= 3;
383 InputMethodLookupTable table
;
384 ClearInputMethodLookupTable(kPageSize
, &table
);
386 table
.orientation
= InputMethodLookupTable::kHorizontal
;
387 for (size_t i
= 0; i
< kPageSize
; ++i
) {
388 table
.candidates
.push_back(kSampleCandidate
[i
]);
389 table
.annotations
.push_back(kSampleAnnotation
[i
]);
390 table
.labels
.push_back(kEmptyLabel
);
393 candidate_window_view
.UpdateCandidates(table
);
395 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
396 // Confirm actual labels not containing ".".
397 for (size_t i
= 0; i
< kPageSize
; ++i
) {
398 ExpectLabels(kEmptyLabel
, kSampleCandidate
[i
], kSampleAnnotation
[i
],
399 candidate_window_view
.candidate_views_
[i
]);
403 SCOPED_TRACE("Vertical customized label case");
404 const size_t kPageSize
= 3;
405 InputMethodLookupTable table
;
406 ClearInputMethodLookupTable(kPageSize
, &table
);
408 table
.orientation
= InputMethodLookupTable::kVertical
;
409 for (size_t i
= 0; i
< kPageSize
; ++i
) {
410 table
.candidates
.push_back(kSampleCandidate
[i
]);
411 table
.annotations
.push_back(kSampleAnnotation
[i
]);
412 table
.labels
.push_back(kCustomizedLabel
[i
]);
415 candidate_window_view
.UpdateCandidates(table
);
417 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
418 // Confirm actual labels not containing ".".
419 for (size_t i
= 0; i
< kPageSize
; ++i
) {
420 ExpectLabels(kCustomizedLabel
[i
],
422 kSampleAnnotation
[i
],
423 candidate_window_view
.candidate_views_
[i
]);
427 SCOPED_TRACE("Horizontal customized label case");
428 const size_t kPageSize
= 3;
429 InputMethodLookupTable table
;
430 ClearInputMethodLookupTable(kPageSize
, &table
);
432 table
.orientation
= InputMethodLookupTable::kHorizontal
;
433 for (size_t i
= 0; i
< kPageSize
; ++i
) {
434 table
.candidates
.push_back(kSampleCandidate
[i
]);
435 table
.annotations
.push_back(kSampleAnnotation
[i
]);
436 table
.labels
.push_back(kCustomizedLabel
[i
]);
439 candidate_window_view
.UpdateCandidates(table
);
441 ASSERT_EQ(kPageSize
, candidate_window_view
.candidate_views_
.size());
442 // Confirm actual labels not containing ".".
443 for (size_t i
= 0; i
< kPageSize
; ++i
) {
444 ExpectLabels(kExpectedHorizontalCustomizedLabel
[i
],
446 kSampleAnnotation
[i
],
447 candidate_window_view
.candidate_views_
[i
]);
451 // We should call CloseNow method, otherwise this test will leak memory.
455 TEST_F(CandidateWindowViewTest
, DoNotChangeRowHeightWithLabelSwitchTest
) {
456 const size_t kPageSize
= 10;
457 InputMethodLookupTable table
;
458 InputMethodLookupTable no_shortcut_table
;
460 const char kSampleCandidate1
[] = "Sample String 1";
461 const char kSampleCandidate2
[] = "\xE3\x81\x82"; // multi byte string.
462 const char kSampleCandidate3
[] = ".....";
464 const char kSampleShortcut1
[] = "1";
465 const char kSampleShortcut2
[] = "b";
466 const char kSampleShortcut3
[] = "C";
468 const char kSampleAnnotation1
[] = "Sample Annotation 1";
469 const char kSampleAnnotation2
[] = "\xE3\x81\x82"; // multi byte string.
470 const char kSampleAnnotation3
[] = "......";
472 // For testing, we have to prepare empty widget.
473 // We should NOT manually free widget by default, otherwise double free will
474 // be occurred. So, we should instantiate widget class with "new" operation.
475 views::Widget
* widget
= new views::Widget
;
476 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
477 widget
->Init(params
);
479 CandidateWindowView
candidate_window_view(widget
);
480 candidate_window_view
.Init();
482 // Create LookupTable object.
483 ClearInputMethodLookupTable(kPageSize
, &table
);
484 table
.visible
= true;
485 table
.cursor_absolute_index
= 0;
487 table
.candidates
.clear();
488 table
.orientation
= InputMethodLookupTable::kVertical
;
489 table
.labels
.clear();
490 table
.annotations
.clear();
492 table
.candidates
.push_back(kSampleCandidate1
);
493 table
.candidates
.push_back(kSampleCandidate2
);
494 table
.candidates
.push_back(kSampleCandidate3
);
496 table
.labels
.push_back(kSampleShortcut1
);
497 table
.labels
.push_back(kSampleShortcut2
);
498 table
.labels
.push_back(kSampleShortcut3
);
500 table
.annotations
.push_back(kSampleAnnotation1
);
501 table
.annotations
.push_back(kSampleAnnotation2
);
502 table
.annotations
.push_back(kSampleAnnotation3
);
504 no_shortcut_table
= table
;
505 no_shortcut_table
.labels
.clear();
507 int before_height
= 0;
509 // Test for shortcut mode to no-shortcut mode.
510 // Initialize with a shortcut mode lookup table.
511 candidate_window_view
.MaybeInitializeCandidateViews(table
);
512 ASSERT_EQ(3UL, candidate_window_view
.candidate_views_
.size());
514 candidate_window_view
.candidate_views_
[0]->GetContentsBounds().height();
515 // Checks all entry have same row height.
516 for (size_t i
= 1; i
< candidate_window_view
.candidate_views_
.size(); ++i
) {
517 const CandidateView
* view
= candidate_window_view
.candidate_views_
[i
];
518 EXPECT_EQ(before_height
, view
->GetContentsBounds().height());
521 // Initialize with a no shortcut mode lookup table.
522 candidate_window_view
.MaybeInitializeCandidateViews(no_shortcut_table
);
523 ASSERT_EQ(3UL, candidate_window_view
.candidate_views_
.size());
524 EXPECT_EQ(before_height
,
525 candidate_window_view
.candidate_views_
[0]->GetContentsBounds()
527 // Checks all entry have same row height.
528 for (size_t i
= 1; i
< candidate_window_view
.candidate_views_
.size(); ++i
) {
529 const CandidateView
* view
= candidate_window_view
.candidate_views_
[i
];
530 EXPECT_EQ(before_height
, view
->GetContentsBounds().height());
533 // Test for no-shortcut mode to shortcut mode.
534 // Initialize with a no shortcut mode lookup table.
535 candidate_window_view
.MaybeInitializeCandidateViews(no_shortcut_table
);
536 ASSERT_EQ(3UL, candidate_window_view
.candidate_views_
.size());
538 candidate_window_view
.candidate_views_
[0]->GetContentsBounds().height();
539 // Checks all entry have same row height.
540 for (size_t i
= 1; i
< candidate_window_view
.candidate_views_
.size(); ++i
) {
541 const CandidateView
* view
= candidate_window_view
.candidate_views_
[i
];
542 EXPECT_EQ(before_height
, view
->GetContentsBounds().height());
545 // Initialize with a shortcut mode lookup table.
546 candidate_window_view
.MaybeInitializeCandidateViews(table
);
547 ASSERT_EQ(3UL, candidate_window_view
.candidate_views_
.size());
548 EXPECT_EQ(before_height
,
549 candidate_window_view
.candidate_views_
[0]->GetContentsBounds()
551 // Checks all entry have same row height.
552 for (size_t i
= 1; i
< candidate_window_view
.candidate_views_
.size(); ++i
) {
553 const CandidateView
* view
= candidate_window_view
.candidate_views_
[i
];
554 EXPECT_EQ(before_height
, view
->GetContentsBounds().height());
557 // We should call CloseNow method, otherwise this test will leak memory.
560 } // namespace input_method
561 } // namespace chromeos