• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/ime/candidate_window_view.h"
6 
7 #include <string>
8 
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"
15 
16 namespace ash {
17 namespace ime {
18 
19 namespace {
20 const char* kSampleCandidate[] = {
21   "Sample Candidate 1",
22   "Sample Candidate 2",
23   "Sample Candidate 3"
24 };
25 const char* kSampleAnnotation[] = {
26   "Sample Annotation 1",
27   "Sample Annotation 2",
28   "Sample Annotation 3"
29 };
30 const char* kSampleDescriptionTitle[] = {
31   "Sample Description Title 1",
32   "Sample Description Title 2",
33   "Sample Description Title 3",
34 };
35 const char* kSampleDescriptionBody[] = {
36   "Sample Description Body 1",
37   "Sample Description Body 2",
38   "Sample Description Body 3",
39 };
40 
InitCandidateWindow(size_t page_size,ui::CandidateWindow * candidate_window)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);
47 }
48 
InitCandidateWindowWithCandidatesFilled(size_t page_size,ui::CandidateWindow * candidate_window)49 void InitCandidateWindowWithCandidatesFilled(
50     size_t page_size,
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);
60   }
61 }
62 
63 }  // namespace
64 
65 class CandidateWindowViewTest : public views::ViewsTestBase {
66  public:
CandidateWindowViewTest()67   CandidateWindowViewTest() {}
~CandidateWindowViewTest()68   virtual ~CandidateWindowViewTest() {}
69 
70  protected:
SetUp()71   virtual void SetUp() {
72     views::ViewsTestBase::SetUp();
73     candidate_window_view_ = new CandidateWindowView(GetContext());
74     candidate_window_view_->InitWidget();
75   }
76 
candidate_window_view()77   CandidateWindowView* candidate_window_view() {
78     return candidate_window_view_;
79   }
80 
selected_candidate_index_in_page()81   int selected_candidate_index_in_page() {
82     return candidate_window_view_->selected_candidate_index_in_page_;
83   }
84 
GetCandidatesSize() const85   size_t GetCandidatesSize() const {
86     return candidate_window_view_->candidate_views_.size();
87   }
88 
GetCandidateAt(size_t i)89   CandidateView* GetCandidateAt(size_t i) {
90     return candidate_window_view_->candidate_views_[i];
91   }
92 
SelectCandidateAt(int index_in_page)93   void SelectCandidateAt(int index_in_page) {
94     candidate_window_view_->SelectCandidateAt(index_in_page);
95   }
96 
MaybeInitializeCandidateViews(const ui::CandidateWindow & candidate_window)97   void MaybeInitializeCandidateViews(
98       const ui::CandidateWindow& candidate_window) {
99     candidate_window_view_->MaybeInitializeCandidateViews(candidate_window);
100   }
101 
ExpectLabels(const std::string & shortcut,const std::string & candidate,const std::string & annotation,const CandidateView * row)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()));
109   }
110 
111  private:
112   // owned by |parent_|.
113   CandidateWindowView* candidate_window_view_;
114 
115   DISALLOW_COPY_AND_ASSIGN(CandidateWindowViewTest);
116 };
117 
TEST_F(CandidateWindowViewTest,UpdateCandidatesTest_CursorVisibility)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,
123                                           &candidate_window);
124   candidate_window_view()->UpdateCandidates(candidate_window);
125   EXPECT_EQ(0, selected_candidate_index_in_page());
126 
127   // Invisible cursor.
128   candidate_window.set_is_cursor_visible(false);
129   candidate_window_view()->UpdateCandidates(candidate_window);
130   EXPECT_EQ(-1, selected_candidate_index_in_page());
131 
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());
136 
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());
141 }
142 
TEST_F(CandidateWindowViewTest,SelectCandidateAtTest)143 TEST_F(CandidateWindowViewTest, SelectCandidateAtTest) {
144   // Set 9 candidates.
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);
151 
152   // Select the last candidate.
153   SelectCandidateAt(candidate_window_large_size - 1);
154 
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);
165 }
166 
TEST_F(CandidateWindowViewTest,ShortcutSettingTest)167 TEST_F(CandidateWindowViewTest, ShortcutSettingTest) {
168   const char* kEmptyLabel = "";
169   const char* kCustomizedLabel[] = { "a", "s", "d" };
170   const char* kExpectedHorizontalCustomizedLabel[] = { "a.", "s.", "d." };
171 
172   {
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());
180     }
181   }
182   {
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);
187 
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);
197     }
198 
199     candidate_window_view()->UpdateCandidates(candidate_window);
200 
201     ASSERT_EQ(kPageSize, GetCandidatesSize());
202     for (size_t i = 0; i < kPageSize; ++i) {
203       ExpectLabels(kEmptyLabel, kSampleCandidate[i], kSampleAnnotation[i],
204                    GetCandidateAt(i));
205     }
206   }
207   {
208     SCOPED_TRACE(
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);
213 
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);
223     }
224 
225     candidate_window_view()->UpdateCandidates(candidate_window);
226 
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],
231                    GetCandidateAt(i));
232     }
233   }
234   {
235     SCOPED_TRACE("Vertical customized label case");
236     const size_t kPageSize = 3;
237     ui::CandidateWindow candidate_window;
238     InitCandidateWindow(kPageSize, &candidate_window);
239 
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);
249     }
250 
251     candidate_window_view()->UpdateCandidates(candidate_window);
252 
253     ASSERT_EQ(kPageSize, GetCandidatesSize());
254     // Confirm actual labels not containing ".".
255     for (size_t i = 0; i < kPageSize; ++i) {
256       ExpectLabels(kCustomizedLabel[i],
257                    kSampleCandidate[i],
258                    kSampleAnnotation[i],
259                    GetCandidateAt(i));
260     }
261   }
262   {
263     SCOPED_TRACE("Horizontal customized label case");
264     const size_t kPageSize = 3;
265     ui::CandidateWindow candidate_window;
266     InitCandidateWindow(kPageSize, &candidate_window);
267 
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);
277     }
278 
279     candidate_window_view()->UpdateCandidates(candidate_window);
280 
281     ASSERT_EQ(kPageSize, GetCandidatesSize());
282     // Confirm actual labels not containing ".".
283     for (size_t i = 0; i < kPageSize; ++i) {
284       ExpectLabels(kExpectedHorizontalCustomizedLabel[i],
285                    kSampleCandidate[i],
286                    kSampleAnnotation[i],
287                    GetCandidateAt(i));
288     }
289   }
290 }
291 
TEST_F(CandidateWindowViewTest,DoNotChangeRowHeightWithLabelSwitchTest)292 TEST_F(CandidateWindowViewTest, DoNotChangeRowHeightWithLabelSwitchTest) {
293   const size_t kPageSize = 10;
294   ui::CandidateWindow candidate_window;
295   ui::CandidateWindow no_shortcut_candidate_window;
296 
297   const base::string16 kSampleCandidate1 = base::UTF8ToUTF16(
298       "Sample String 1");
299   const base::string16 kSampleCandidate2 = base::UTF8ToUTF16(
300       "\xE3\x81\x82");  // multi byte string.
301   const base::string16 kSampleCandidate3 = base::UTF8ToUTF16(".....");
302 
303   const base::string16 kSampleShortcut1 = base::UTF8ToUTF16("1");
304   const base::string16 kSampleShortcut2 = base::UTF8ToUTF16("b");
305   const base::string16 kSampleShortcut3 = base::UTF8ToUTF16("C");
306 
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("......");
312 
313   // Create CandidateWindow object.
314   InitCandidateWindow(kPageSize, &candidate_window);
315 
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);
321 
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);
328 
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);
334 
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);
340 
341   int before_height = 0;
342 
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());
349   before_height =
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());
354 
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());
364 
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());
375 
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());
385 }
386 
387 }  // namespace ime
388 }  // namespace ash
389