1 // Copyright 2014 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 "ash/ime/candidate_window_view.h"
9 #include "ash/ime/candidate_view.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/views/test/views_test_base.h"
14 #include "ui/views/widget/widget.h"
20 const char* kSampleCandidate
[] = {
25 const char* kSampleAnnotation
[] = {
26 "Sample Annotation 1",
27 "Sample Annotation 2",
30 const char* kSampleDescriptionTitle
[] = {
31 "Sample Description Title 1",
32 "Sample Description Title 2",
33 "Sample Description Title 3",
35 const char* kSampleDescriptionBody
[] = {
36 "Sample Description Body 1",
37 "Sample Description Body 2",
38 "Sample Description Body 3",
41 void InitCandidateWindow(size_t page_size
,
42 ui::CandidateWindow
* candidate_window
) {
43 candidate_window
->set_cursor_position(0);
44 candidate_window
->set_page_size(page_size
);
45 candidate_window
->mutable_candidates()->clear();
46 candidate_window
->set_orientation(ui::CandidateWindow::VERTICAL
);
49 void InitCandidateWindowWithCandidatesFilled(
51 ui::CandidateWindow
* candidate_window
) {
52 InitCandidateWindow(page_size
, candidate_window
);
53 for (size_t i
= 0; i
< page_size
; ++i
) {
54 ui::CandidateWindow::Entry entry
;
55 entry
.value
= base::UTF8ToUTF16(base::StringPrintf(
56 "value %lld", static_cast<unsigned long long>(i
)));
57 entry
.label
= base::UTF8ToUTF16(base::StringPrintf(
58 "%lld", static_cast<unsigned long long>(i
)));
59 candidate_window
->mutable_candidates()->push_back(entry
);
65 class CandidateWindowViewTest
: public views::ViewsTestBase
{
67 CandidateWindowViewTest() {}
68 virtual ~CandidateWindowViewTest() {}
71 virtual void SetUp() {
72 views::ViewsTestBase::SetUp();
73 candidate_window_view_
= new CandidateWindowView(GetContext());
74 candidate_window_view_
->InitWidget();
77 CandidateWindowView
* candidate_window_view() {
78 return candidate_window_view_
;
81 int selected_candidate_index_in_page() {
82 return candidate_window_view_
->selected_candidate_index_in_page_
;
85 size_t GetCandidatesSize() const {
86 return candidate_window_view_
->candidate_views_
.size();
89 CandidateView
* GetCandidateAt(size_t i
) {
90 return candidate_window_view_
->candidate_views_
[i
];
93 void SelectCandidateAt(int index_in_page
) {
94 candidate_window_view_
->SelectCandidateAt(index_in_page
);
97 void MaybeInitializeCandidateViews(
98 const ui::CandidateWindow
& candidate_window
) {
99 candidate_window_view_
->MaybeInitializeCandidateViews(candidate_window
);
102 void ExpectLabels(const std::string
& shortcut
,
103 const std::string
& candidate
,
104 const std::string
& annotation
,
105 const CandidateView
* row
) {
106 EXPECT_EQ(shortcut
, base::UTF16ToUTF8(row
->shortcut_label_
->text()));
107 EXPECT_EQ(candidate
, base::UTF16ToUTF8(row
->candidate_label_
->text()));
108 EXPECT_EQ(annotation
, base::UTF16ToUTF8(row
->annotation_label_
->text()));
112 // owned by |parent_|.
113 CandidateWindowView
* candidate_window_view_
;
115 DISALLOW_COPY_AND_ASSIGN(CandidateWindowViewTest
);
118 TEST_F(CandidateWindowViewTest
, UpdateCandidatesTest_CursorVisibility
) {
119 // Visible (by default) cursor.
120 ui::CandidateWindow candidate_window
;
121 const int candidate_window_size
= 9;
122 InitCandidateWindowWithCandidatesFilled(candidate_window_size
,
124 candidate_window_view()->UpdateCandidates(candidate_window
);
125 EXPECT_EQ(0, selected_candidate_index_in_page());
128 candidate_window
.set_is_cursor_visible(false);
129 candidate_window_view()->UpdateCandidates(candidate_window
);
130 EXPECT_EQ(-1, selected_candidate_index_in_page());
132 // Move the cursor to the end.
133 candidate_window
.set_cursor_position(candidate_window_size
- 1);
134 candidate_window_view()->UpdateCandidates(candidate_window
);
135 EXPECT_EQ(-1, selected_candidate_index_in_page());
137 // Change the cursor to visible. The cursor must be at the end.
138 candidate_window
.set_is_cursor_visible(true);
139 candidate_window_view()->UpdateCandidates(candidate_window
);
140 EXPECT_EQ(candidate_window_size
- 1, selected_candidate_index_in_page());
143 TEST_F(CandidateWindowViewTest
, SelectCandidateAtTest
) {
145 ui::CandidateWindow candidate_window_large
;
146 const int candidate_window_large_size
= 9;
147 InitCandidateWindowWithCandidatesFilled(candidate_window_large_size
,
148 &candidate_window_large
);
149 candidate_window_large
.set_cursor_position(candidate_window_large_size
- 1);
150 candidate_window_view()->UpdateCandidates(candidate_window_large
);
152 // Select the last candidate.
153 SelectCandidateAt(candidate_window_large_size
- 1);
155 // Reduce the number of candidates to 3.
156 ui::CandidateWindow candidate_window_small
;
157 const int candidate_window_small_size
= 3;
158 InitCandidateWindowWithCandidatesFilled(candidate_window_small_size
,
159 &candidate_window_small
);
160 candidate_window_small
.set_cursor_position(candidate_window_small_size
- 1);
161 // Make sure the test doesn't crash if the candidate window reduced
162 // its size. (crbug.com/174163)
163 candidate_window_view()->UpdateCandidates(candidate_window_small
);
164 SelectCandidateAt(candidate_window_small_size
- 1);
167 TEST_F(CandidateWindowViewTest
, ShortcutSettingTest
) {
168 const char* kEmptyLabel
= "";
169 const char* kCustomizedLabel
[] = { "a", "s", "d" };
170 const char* kExpectedHorizontalCustomizedLabel
[] = { "a.", "s.", "d." };
173 SCOPED_TRACE("candidate_views allocation test");
174 const size_t kMaxPageSize
= 16;
175 for (size_t i
= 1; i
< kMaxPageSize
; ++i
) {
176 ui::CandidateWindow candidate_window
;
177 InitCandidateWindow(i
, &candidate_window
);
178 candidate_window_view()->UpdateCandidates(candidate_window
);
179 EXPECT_EQ(i
, GetCandidatesSize());
183 SCOPED_TRACE("Empty string for each labels expects empty labels(vertical)");
184 const size_t kPageSize
= 3;
185 ui::CandidateWindow candidate_window
;
186 InitCandidateWindow(kPageSize
, &candidate_window
);
188 candidate_window
.set_orientation(ui::CandidateWindow::VERTICAL
);
189 for (size_t i
= 0; i
< kPageSize
; ++i
) {
190 ui::CandidateWindow::Entry entry
;
191 entry
.value
= base::UTF8ToUTF16(kSampleCandidate
[i
]);
192 entry
.annotation
= base::UTF8ToUTF16(kSampleAnnotation
[i
]);
193 entry
.description_title
= base::UTF8ToUTF16(kSampleDescriptionTitle
[i
]);
194 entry
.description_body
= base::UTF8ToUTF16(kSampleDescriptionBody
[i
]);
195 entry
.label
= base::UTF8ToUTF16(kEmptyLabel
);
196 candidate_window
.mutable_candidates()->push_back(entry
);
199 candidate_window_view()->UpdateCandidates(candidate_window
);
201 ASSERT_EQ(kPageSize
, GetCandidatesSize());
202 for (size_t i
= 0; i
< kPageSize
; ++i
) {
203 ExpectLabels(kEmptyLabel
, kSampleCandidate
[i
], kSampleAnnotation
[i
],
209 "Empty string for each labels expect empty labels(horizontal)");
210 const size_t kPageSize
= 3;
211 ui::CandidateWindow candidate_window
;
212 InitCandidateWindow(kPageSize
, &candidate_window
);
214 candidate_window
.set_orientation(ui::CandidateWindow::HORIZONTAL
);
215 for (size_t i
= 0; i
< kPageSize
; ++i
) {
216 ui::CandidateWindow::Entry entry
;
217 entry
.value
= base::UTF8ToUTF16(kSampleCandidate
[i
]);
218 entry
.annotation
= base::UTF8ToUTF16(kSampleAnnotation
[i
]);
219 entry
.description_title
= base::UTF8ToUTF16(kSampleDescriptionTitle
[i
]);
220 entry
.description_body
= base::UTF8ToUTF16(kSampleDescriptionBody
[i
]);
221 entry
.label
= base::UTF8ToUTF16(kEmptyLabel
);
222 candidate_window
.mutable_candidates()->push_back(entry
);
225 candidate_window_view()->UpdateCandidates(candidate_window
);
227 ASSERT_EQ(kPageSize
, GetCandidatesSize());
228 // Confirm actual labels not containing ".".
229 for (size_t i
= 0; i
< kPageSize
; ++i
) {
230 ExpectLabels(kEmptyLabel
, kSampleCandidate
[i
], kSampleAnnotation
[i
],
235 SCOPED_TRACE("Vertical customized label case");
236 const size_t kPageSize
= 3;
237 ui::CandidateWindow candidate_window
;
238 InitCandidateWindow(kPageSize
, &candidate_window
);
240 candidate_window
.set_orientation(ui::CandidateWindow::VERTICAL
);
241 for (size_t i
= 0; i
< kPageSize
; ++i
) {
242 ui::CandidateWindow::Entry entry
;
243 entry
.value
= base::UTF8ToUTF16(kSampleCandidate
[i
]);
244 entry
.annotation
= base::UTF8ToUTF16(kSampleAnnotation
[i
]);
245 entry
.description_title
= base::UTF8ToUTF16(kSampleDescriptionTitle
[i
]);
246 entry
.description_body
= base::UTF8ToUTF16(kSampleDescriptionBody
[i
]);
247 entry
.label
= base::UTF8ToUTF16(kCustomizedLabel
[i
]);
248 candidate_window
.mutable_candidates()->push_back(entry
);
251 candidate_window_view()->UpdateCandidates(candidate_window
);
253 ASSERT_EQ(kPageSize
, GetCandidatesSize());
254 // Confirm actual labels not containing ".".
255 for (size_t i
= 0; i
< kPageSize
; ++i
) {
256 ExpectLabels(kCustomizedLabel
[i
],
258 kSampleAnnotation
[i
],
263 SCOPED_TRACE("Horizontal customized label case");
264 const size_t kPageSize
= 3;
265 ui::CandidateWindow candidate_window
;
266 InitCandidateWindow(kPageSize
, &candidate_window
);
268 candidate_window
.set_orientation(ui::CandidateWindow::HORIZONTAL
);
269 for (size_t i
= 0; i
< kPageSize
; ++i
) {
270 ui::CandidateWindow::Entry entry
;
271 entry
.value
= base::UTF8ToUTF16(kSampleCandidate
[i
]);
272 entry
.annotation
= base::UTF8ToUTF16(kSampleAnnotation
[i
]);
273 entry
.description_title
= base::UTF8ToUTF16(kSampleDescriptionTitle
[i
]);
274 entry
.description_body
= base::UTF8ToUTF16(kSampleDescriptionBody
[i
]);
275 entry
.label
= base::UTF8ToUTF16(kCustomizedLabel
[i
]);
276 candidate_window
.mutable_candidates()->push_back(entry
);
279 candidate_window_view()->UpdateCandidates(candidate_window
);
281 ASSERT_EQ(kPageSize
, GetCandidatesSize());
282 // Confirm actual labels not containing ".".
283 for (size_t i
= 0; i
< kPageSize
; ++i
) {
284 ExpectLabels(kExpectedHorizontalCustomizedLabel
[i
],
286 kSampleAnnotation
[i
],
292 TEST_F(CandidateWindowViewTest
, DoNotChangeRowHeightWithLabelSwitchTest
) {
293 const size_t kPageSize
= 10;
294 ui::CandidateWindow candidate_window
;
295 ui::CandidateWindow no_shortcut_candidate_window
;
297 const base::string16 kSampleCandidate1
= base::UTF8ToUTF16(
299 const base::string16 kSampleCandidate2
= base::UTF8ToUTF16(
300 "\xE3\x81\x82"); // multi byte string.
301 const base::string16 kSampleCandidate3
= base::UTF8ToUTF16(".....");
303 const base::string16 kSampleShortcut1
= base::UTF8ToUTF16("1");
304 const base::string16 kSampleShortcut2
= base::UTF8ToUTF16("b");
305 const base::string16 kSampleShortcut3
= base::UTF8ToUTF16("C");
307 const base::string16 kSampleAnnotation1
= base::UTF8ToUTF16(
308 "Sample Annotation 1");
309 const base::string16 kSampleAnnotation2
= base::UTF8ToUTF16(
310 "\xE3\x81\x82"); // multi byte string.
311 const base::string16 kSampleAnnotation3
= base::UTF8ToUTF16("......");
313 // Create CandidateWindow object.
314 InitCandidateWindow(kPageSize
, &candidate_window
);
316 candidate_window
.set_cursor_position(0);
317 candidate_window
.set_page_size(3);
318 candidate_window
.mutable_candidates()->clear();
319 candidate_window
.set_orientation(ui::CandidateWindow::VERTICAL
);
320 no_shortcut_candidate_window
.CopyFrom(candidate_window
);
322 ui::CandidateWindow::Entry entry
;
323 entry
.value
= kSampleCandidate1
;
324 entry
.annotation
= kSampleAnnotation1
;
325 candidate_window
.mutable_candidates()->push_back(entry
);
326 entry
.label
= kSampleShortcut1
;
327 no_shortcut_candidate_window
.mutable_candidates()->push_back(entry
);
329 entry
.value
= kSampleCandidate2
;
330 entry
.annotation
= kSampleAnnotation2
;
331 candidate_window
.mutable_candidates()->push_back(entry
);
332 entry
.label
= kSampleShortcut2
;
333 no_shortcut_candidate_window
.mutable_candidates()->push_back(entry
);
335 entry
.value
= kSampleCandidate3
;
336 entry
.annotation
= kSampleAnnotation3
;
337 candidate_window
.mutable_candidates()->push_back(entry
);
338 entry
.label
= kSampleShortcut3
;
339 no_shortcut_candidate_window
.mutable_candidates()->push_back(entry
);
341 int before_height
= 0;
343 // Test for shortcut mode to no-shortcut mode.
344 // Initialize with a shortcut mode candidate window.
345 MaybeInitializeCandidateViews(candidate_window
);
346 ASSERT_EQ(3UL, GetCandidatesSize());
347 // Check the selected index is invalidated.
348 EXPECT_EQ(-1, selected_candidate_index_in_page());
350 GetCandidateAt(0)->GetContentsBounds().height();
351 // Checks all entry have same row height.
352 for (size_t i
= 1; i
< GetCandidatesSize(); ++i
)
353 EXPECT_EQ(before_height
, GetCandidateAt(i
)->GetContentsBounds().height());
355 // Initialize with a no shortcut mode candidate window.
356 MaybeInitializeCandidateViews(no_shortcut_candidate_window
);
357 ASSERT_EQ(3UL, GetCandidatesSize());
358 // Check the selected index is invalidated.
359 EXPECT_EQ(-1, selected_candidate_index_in_page());
360 EXPECT_EQ(before_height
, GetCandidateAt(0)->GetContentsBounds().height());
361 // Checks all entry have same row height.
362 for (size_t i
= 1; i
< GetCandidatesSize(); ++i
)
363 EXPECT_EQ(before_height
, GetCandidateAt(i
)->GetContentsBounds().height());
365 // Test for no-shortcut mode to shortcut mode.
366 // Initialize with a no shortcut mode candidate window.
367 MaybeInitializeCandidateViews(no_shortcut_candidate_window
);
368 ASSERT_EQ(3UL, GetCandidatesSize());
369 // Check the selected index is invalidated.
370 EXPECT_EQ(-1, selected_candidate_index_in_page());
371 before_height
= GetCandidateAt(0)->GetContentsBounds().height();
372 // Checks all entry have same row height.
373 for (size_t i
= 1; i
< GetCandidatesSize(); ++i
)
374 EXPECT_EQ(before_height
, GetCandidateAt(i
)->GetContentsBounds().height());
376 // Initialize with a shortcut mode candidate window.
377 MaybeInitializeCandidateViews(candidate_window
);
378 ASSERT_EQ(3UL, GetCandidatesSize());
379 // Check the selected index is invalidated.
380 EXPECT_EQ(-1, selected_candidate_index_in_page());
381 EXPECT_EQ(before_height
, GetCandidateAt(0)->GetContentsBounds().height());
382 // Checks all entry have same row height.
383 for (size_t i
= 1; i
< GetCandidatesSize(); ++i
)
384 EXPECT_EQ(before_height
, GetCandidateAt(i
)->GetContentsBounds().height());