Home | History | Annotate | Download | only in ime
      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 
     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 
     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:
     67   CandidateWindowViewTest() {}
     68   virtual ~CandidateWindowViewTest() {}
     69 
     70  protected:
     71   virtual void SetUp() {
     72     views::ViewsTestBase::SetUp();
     73     candidate_window_view_ = new CandidateWindowView(GetContext());
     74     candidate_window_view_->InitWidget();
     75   }
     76 
     77   CandidateWindowView* candidate_window_view() {
     78     return candidate_window_view_;
     79   }
     80 
     81   int selected_candidate_index_in_page() {
     82     return candidate_window_view_->selected_candidate_index_in_page_;
     83   }
     84 
     85   size_t GetCandidatesSize() const {
     86     return candidate_window_view_->candidate_views_.size();
     87   }
     88 
     89   CandidateView* GetCandidateAt(size_t i) {
     90     return candidate_window_view_->candidate_views_[i];
     91   }
     92 
     93   void SelectCandidateAt(int index_in_page) {
     94     candidate_window_view_->SelectCandidateAt(index_in_page);
     95   }
     96 
     97   void MaybeInitializeCandidateViews(
     98       const ui::CandidateWindow& candidate_window) {
     99     candidate_window_view_->MaybeInitializeCandidateViews(candidate_window);
    100   }
    101 
    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 
    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 
    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 
    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 
    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