Home | History | Annotate | Download | only in input_method
      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.
      4 
      5 #include "chrome/browser/chromeos/input_method/candidate_window_view.h"
      6 
      7 #include <string>
      8 
      9 #include "base/strings/stringprintf.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/chromeos/input_method/candidate_view.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 chromeos {
     17 namespace input_method {
     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 InitIBusLookupTable(size_t page_size,
     42                          IBusLookupTable* table) {
     43   table->set_cursor_position(0);
     44   table->set_page_size(page_size);
     45   table->mutable_candidates()->clear();
     46   table->set_orientation(IBusLookupTable::VERTICAL);
     47 }
     48 
     49 void InitIBusLookupTableWithCandidatesFilled(size_t page_size,
     50                                              IBusLookupTable* table) {
     51   InitIBusLookupTable(page_size, table);
     52   for (size_t i = 0; i < page_size; ++i) {
     53     IBusLookupTable::Entry entry;
     54     entry.value = base::StringPrintf("value %lld",
     55                                      static_cast<unsigned long long>(i));
     56     entry.label = base::StringPrintf("%lld",
     57                                      static_cast<unsigned long long>(i));
     58     table->mutable_candidates()->push_back(entry);
     59   }
     60 }
     61 
     62 }  // namespace
     63 
     64 class CandidateWindowViewTest : public views::ViewsTestBase {
     65  protected:
     66   void ExpectLabels(const std::string& shortcut,
     67                     const std::string& candidate,
     68                     const std::string& annotation,
     69                     const CandidateView* row) {
     70     EXPECT_EQ(shortcut, UTF16ToUTF8(row->shortcut_label_->text()));
     71     EXPECT_EQ(candidate, UTF16ToUTF8(row->candidate_label_->text()));
     72     EXPECT_EQ(annotation, UTF16ToUTF8(row->annotation_label_->text()));
     73   }
     74 };
     75 
     76 TEST_F(CandidateWindowViewTest, UpdateCandidatesTest_CursorVisibility) {
     77   views::Widget* widget = new views::Widget;
     78   views::Widget::InitParams params =
     79       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
     80   widget->Init(params);
     81 
     82   CandidateWindowView candidate_window_view(widget);
     83   candidate_window_view.Init();
     84 
     85   // Visible (by default) cursor.
     86   IBusLookupTable table;
     87   const int table_size = 9;
     88   InitIBusLookupTableWithCandidatesFilled(table_size, &table);
     89   candidate_window_view.UpdateCandidates(table);
     90   EXPECT_EQ(0, candidate_window_view.selected_candidate_index_in_page_);
     91 
     92   // Invisible cursor.
     93   table.set_is_cursor_visible(false);
     94   candidate_window_view.UpdateCandidates(table);
     95   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
     96 
     97   // Move the cursor to the end.
     98   table.set_cursor_position(table_size - 1);
     99   candidate_window_view.UpdateCandidates(table);
    100   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
    101 
    102   // Change the cursor to visible.  The cursor must be at the end.
    103   table.set_is_cursor_visible(true);
    104   candidate_window_view.UpdateCandidates(table);
    105   EXPECT_EQ(table_size - 1,
    106             candidate_window_view.selected_candidate_index_in_page_);
    107 }
    108 
    109 TEST_F(CandidateWindowViewTest, SelectCandidateAtTest) {
    110   views::Widget* widget = new views::Widget;
    111   views::Widget::InitParams params =
    112       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    113   widget->Init(params);
    114 
    115   CandidateWindowView candidate_window_view(widget);
    116   candidate_window_view.Init();
    117 
    118   // Set 9 candidates.
    119   IBusLookupTable table_large;
    120   const int table_large_size = 9;
    121   InitIBusLookupTableWithCandidatesFilled(table_large_size, &table_large);
    122   table_large.set_cursor_position(table_large_size - 1);
    123   candidate_window_view.UpdateCandidates(table_large);
    124   // Select the last candidate.
    125   candidate_window_view.SelectCandidateAt(table_large_size - 1);
    126 
    127   // Reduce the number of candidates to 3.
    128   IBusLookupTable table_small;
    129   const int table_small_size = 3;
    130   InitIBusLookupTableWithCandidatesFilled(table_small_size, &table_small);
    131   table_small.set_cursor_position(table_small_size - 1);
    132   // Make sure the test doesn't crash if the candidate table reduced its size.
    133   // (crbug.com/174163)
    134   candidate_window_view.UpdateCandidates(table_small);
    135   candidate_window_view.SelectCandidateAt(table_small_size - 1);
    136 }
    137 
    138 TEST_F(CandidateWindowViewTest, ShortcutSettingTest) {
    139   const char* kEmptyLabel = "";
    140   const char* kCustomizedLabel[] = { "a", "s", "d" };
    141   const char* kExpectedHorizontalCustomizedLabel[] = { "a.", "s.", "d." };
    142 
    143   views::Widget* widget = new views::Widget;
    144   views::Widget::InitParams params =
    145       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    146   widget->Init(params);
    147 
    148   CandidateWindowView candidate_window_view(widget);
    149   candidate_window_view.Init();
    150 
    151   {
    152     SCOPED_TRACE("candidate_views allocation test");
    153     const size_t kMaxPageSize = 16;
    154     for (size_t i = 1; i < kMaxPageSize; ++i) {
    155       IBusLookupTable table;
    156       InitIBusLookupTable(i, &table);
    157       candidate_window_view.UpdateCandidates(table);
    158       EXPECT_EQ(i, candidate_window_view.candidate_views_.size());
    159     }
    160   }
    161   {
    162     SCOPED_TRACE("Empty string for each labels expects empty labels(vertical)");
    163     const size_t kPageSize = 3;
    164     IBusLookupTable table;
    165     InitIBusLookupTable(kPageSize, &table);
    166 
    167     table.set_orientation(IBusLookupTable::VERTICAL);
    168     for (size_t i = 0; i < kPageSize; ++i) {
    169       IBusLookupTable::Entry entry;
    170       entry.value = kSampleCandidate[i];
    171       entry.annotation = kSampleAnnotation[i];
    172       entry.description_title = kSampleDescriptionTitle[i];
    173       entry.description_body = kSampleDescriptionBody[i];
    174       entry.label = kEmptyLabel;
    175       table.mutable_candidates()->push_back(entry);
    176     }
    177 
    178     candidate_window_view.UpdateCandidates(table);
    179 
    180     ASSERT_EQ(kPageSize, candidate_window_view.candidate_views_.size());
    181     for (size_t i = 0; i < kPageSize; ++i) {
    182       ExpectLabels(kEmptyLabel, kSampleCandidate[i], kSampleAnnotation[i],
    183                    candidate_window_view.candidate_views_[i]);
    184     }
    185   }
    186   {
    187     SCOPED_TRACE(
    188         "Empty string for each labels expect empty labels(horizontal)");
    189     const size_t kPageSize = 3;
    190     IBusLookupTable table;
    191     InitIBusLookupTable(kPageSize, &table);
    192 
    193     table.set_orientation(IBusLookupTable::HORIZONTAL);
    194     for (size_t i = 0; i < kPageSize; ++i) {
    195       IBusLookupTable::Entry entry;
    196       entry.value = kSampleCandidate[i];
    197       entry.annotation = kSampleAnnotation[i];
    198       entry.description_title = kSampleDescriptionTitle[i];
    199       entry.description_body = kSampleDescriptionBody[i];
    200       entry.label = kEmptyLabel;
    201       table.mutable_candidates()->push_back(entry);
    202     }
    203 
    204     candidate_window_view.UpdateCandidates(table);
    205 
    206     ASSERT_EQ(kPageSize, candidate_window_view.candidate_views_.size());
    207     // Confirm actual labels not containing ".".
    208     for (size_t i = 0; i < kPageSize; ++i) {
    209       ExpectLabels(kEmptyLabel, kSampleCandidate[i], kSampleAnnotation[i],
    210                    candidate_window_view.candidate_views_[i]);
    211     }
    212   }
    213   {
    214     SCOPED_TRACE("Vertical customized label case");
    215     const size_t kPageSize = 3;
    216     IBusLookupTable table;
    217     InitIBusLookupTable(kPageSize, &table);
    218 
    219     table.set_orientation(IBusLookupTable::VERTICAL);
    220     for (size_t i = 0; i < kPageSize; ++i) {
    221       IBusLookupTable::Entry entry;
    222       entry.value = kSampleCandidate[i];
    223       entry.annotation = kSampleAnnotation[i];
    224       entry.description_title = kSampleDescriptionTitle[i];
    225       entry.description_body = kSampleDescriptionBody[i];
    226       entry.label = kCustomizedLabel[i];
    227       table.mutable_candidates()->push_back(entry);
    228     }
    229 
    230     candidate_window_view.UpdateCandidates(table);
    231 
    232     ASSERT_EQ(kPageSize, candidate_window_view.candidate_views_.size());
    233     // Confirm actual labels not containing ".".
    234     for (size_t i = 0; i < kPageSize; ++i) {
    235       ExpectLabels(kCustomizedLabel[i],
    236                    kSampleCandidate[i],
    237                    kSampleAnnotation[i],
    238                    candidate_window_view.candidate_views_[i]);
    239     }
    240   }
    241   {
    242     SCOPED_TRACE("Horizontal customized label case");
    243     const size_t kPageSize = 3;
    244     IBusLookupTable table;
    245     InitIBusLookupTable(kPageSize, &table);
    246 
    247     table.set_orientation(IBusLookupTable::HORIZONTAL);
    248     for (size_t i = 0; i < kPageSize; ++i) {
    249       IBusLookupTable::Entry entry;
    250       entry.value = kSampleCandidate[i];
    251       entry.annotation = kSampleAnnotation[i];
    252       entry.description_title = kSampleDescriptionTitle[i];
    253       entry.description_body = kSampleDescriptionBody[i];
    254       entry.label = kCustomizedLabel[i];
    255       table.mutable_candidates()->push_back(entry);
    256     }
    257 
    258     candidate_window_view.UpdateCandidates(table);
    259 
    260     ASSERT_EQ(kPageSize, candidate_window_view.candidate_views_.size());
    261     // Confirm actual labels not containing ".".
    262     for (size_t i = 0; i < kPageSize; ++i) {
    263       ExpectLabels(kExpectedHorizontalCustomizedLabel[i],
    264                    kSampleCandidate[i],
    265                    kSampleAnnotation[i],
    266                    candidate_window_view.candidate_views_[i]);
    267     }
    268   }
    269 
    270   // We should call CloseNow method, otherwise this test will leak memory.
    271   widget->CloseNow();
    272 }
    273 
    274 TEST_F(CandidateWindowViewTest, DoNotChangeRowHeightWithLabelSwitchTest) {
    275   const size_t kPageSize = 10;
    276   IBusLookupTable table;
    277   IBusLookupTable no_shortcut_table;
    278 
    279   const char kSampleCandidate1[] = "Sample String 1";
    280   const char kSampleCandidate2[] = "\xE3\x81\x82";  // multi byte string.
    281   const char kSampleCandidate3[] = ".....";
    282 
    283   const char kSampleShortcut1[] = "1";
    284   const char kSampleShortcut2[] = "b";
    285   const char kSampleShortcut3[] = "C";
    286 
    287   const char kSampleAnnotation1[] = "Sample Annotation 1";
    288   const char kSampleAnnotation2[] = "\xE3\x81\x82";  // multi byte string.
    289   const char kSampleAnnotation3[] = "......";
    290 
    291   // For testing, we have to prepare empty widget.
    292   // We should NOT manually free widget by default, otherwise double free will
    293   // be occurred. So, we should instantiate widget class with "new" operation.
    294   views::Widget* widget = new views::Widget;
    295   views::Widget::InitParams params =
    296       CreateParams(views::Widget::InitParams::TYPE_WINDOW);
    297   widget->Init(params);
    298 
    299   CandidateWindowView candidate_window_view(widget);
    300   candidate_window_view.Init();
    301 
    302   // Create LookupTable object.
    303   InitIBusLookupTable(kPageSize, &table);
    304 
    305   table.set_cursor_position(0);
    306   table.set_page_size(3);
    307   table.mutable_candidates()->clear();
    308   table.set_orientation(IBusLookupTable::VERTICAL);
    309   no_shortcut_table.CopyFrom(table);
    310 
    311   IBusLookupTable::Entry entry;
    312   entry.value = kSampleCandidate1;
    313   entry.annotation = kSampleAnnotation1;
    314   table.mutable_candidates()->push_back(entry);
    315   entry.label = kSampleShortcut1;
    316   no_shortcut_table.mutable_candidates()->push_back(entry);
    317 
    318   entry.value = kSampleCandidate2;
    319   entry.annotation = kSampleAnnotation2;
    320   table.mutable_candidates()->push_back(entry);
    321   entry.label = kSampleShortcut2;
    322   no_shortcut_table.mutable_candidates()->push_back(entry);
    323 
    324   entry.value = kSampleCandidate3;
    325   entry.annotation = kSampleAnnotation3;
    326   table.mutable_candidates()->push_back(entry);
    327   entry.label = kSampleShortcut3;
    328   no_shortcut_table.mutable_candidates()->push_back(entry);
    329 
    330   int before_height = 0;
    331 
    332   // Test for shortcut mode to no-shortcut mode.
    333   // Initialize with a shortcut mode lookup table.
    334   candidate_window_view.MaybeInitializeCandidateViews(table);
    335   ASSERT_EQ(3UL, candidate_window_view.candidate_views_.size());
    336   // Check the selected index is invalidated.
    337   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
    338   before_height =
    339       candidate_window_view.candidate_views_[0]->GetContentsBounds().height();
    340   // Checks all entry have same row height.
    341   for (size_t i = 1; i < candidate_window_view.candidate_views_.size(); ++i) {
    342     const CandidateView* view = candidate_window_view.candidate_views_[i];
    343     EXPECT_EQ(before_height, view->GetContentsBounds().height());
    344   }
    345 
    346   // Initialize with a no shortcut mode lookup table.
    347   candidate_window_view.MaybeInitializeCandidateViews(no_shortcut_table);
    348   ASSERT_EQ(3UL, candidate_window_view.candidate_views_.size());
    349   // Check the selected index is invalidated.
    350   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
    351   EXPECT_EQ(before_height,
    352             candidate_window_view.candidate_views_[0]->GetContentsBounds()
    353                 .height());
    354   // Checks all entry have same row height.
    355   for (size_t i = 1; i < candidate_window_view.candidate_views_.size(); ++i) {
    356     const CandidateView* view = candidate_window_view.candidate_views_[i];
    357     EXPECT_EQ(before_height, view->GetContentsBounds().height());
    358   }
    359 
    360   // Test for no-shortcut mode to shortcut mode.
    361   // Initialize with a no shortcut mode lookup table.
    362   candidate_window_view.MaybeInitializeCandidateViews(no_shortcut_table);
    363   ASSERT_EQ(3UL, candidate_window_view.candidate_views_.size());
    364   // Check the selected index is invalidated.
    365   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
    366   before_height =
    367       candidate_window_view.candidate_views_[0]->GetContentsBounds().height();
    368   // Checks all entry have same row height.
    369   for (size_t i = 1; i < candidate_window_view.candidate_views_.size(); ++i) {
    370     const CandidateView* view = candidate_window_view.candidate_views_[i];
    371     EXPECT_EQ(before_height, view->GetContentsBounds().height());
    372   }
    373 
    374   // Initialize with a shortcut mode lookup table.
    375   candidate_window_view.MaybeInitializeCandidateViews(table);
    376   ASSERT_EQ(3UL, candidate_window_view.candidate_views_.size());
    377   // Check the selected index is invalidated.
    378   EXPECT_EQ(-1, candidate_window_view.selected_candidate_index_in_page_);
    379   EXPECT_EQ(before_height,
    380             candidate_window_view.candidate_views_[0]->GetContentsBounds()
    381                 .height());
    382   // Checks all entry have same row height.
    383   for (size_t i = 1; i < candidate_window_view.candidate_views_.size(); ++i) {
    384     const CandidateView* view = candidate_window_view.candidate_views_[i];
    385     EXPECT_EQ(before_height, view->GetContentsBounds().height());
    386   }
    387 
    388   // We should call CloseNow method, otherwise this test will leak memory.
    389   widget->CloseNow();
    390 }
    391 }  // namespace input_method
    392 }  // namespace chromeos
    393