Home | History | Annotate | Download | only in table
      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 "ui/views/controls/table/table_view.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 #include "ui/views/controls/table/table_grouper.h"
     11 #include "ui/views/controls/table/table_header.h"
     12 #include "ui/views/controls/table/table_view_observer.h"
     13 
     14 // Put the tests in the views namespace to make it easier to declare them as
     15 // friend classes.
     16 namespace views {
     17 
     18 class TableViewTestHelper {
     19  public:
     20   explicit TableViewTestHelper(TableView* table) : table_(table) {}
     21 
     22   std::string GetPaintRegion(const gfx::Rect& bounds) {
     23     TableView::PaintRegion region(table_->GetPaintRegion(bounds));
     24     return "rows=" + base::IntToString(region.min_row) + " " +
     25         base::IntToString(region.max_row) + " cols=" +
     26         base::IntToString(region.min_column) + " " +
     27         base::IntToString(region.max_column);
     28   }
     29 
     30   size_t visible_col_count() {
     31     return table_->visible_columns().size();
     32   }
     33 
     34   TableHeader* header() { return table_->header_; }
     35 
     36  private:
     37   TableView* table_;
     38 
     39   DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper);
     40 };
     41 
     42 namespace {
     43 
     44 // TestTableModel2 -------------------------------------------------------------
     45 
     46 // Trivial TableModel implementation that is backed by a vector of vectors.
     47 // Provides methods for adding/removing/changing the contents that notify the
     48 // observer appropriately.
     49 //
     50 // Initial contents are:
     51 // 0, 1
     52 // 1, 1
     53 // 2, 2
     54 // 3, 0
     55 class TestTableModel2 : public ui::TableModel {
     56  public:
     57   TestTableModel2();
     58 
     59   // Adds a new row at index |row| with values |c1_value| and |c2_value|.
     60   void AddRow(int row, int c1_value, int c2_value);
     61 
     62   // Removes the row at index |row|.
     63   void RemoveRow(int row);
     64 
     65   // Changes the values of the row at |row|.
     66   void ChangeRow(int row, int c1_value, int c2_value);
     67 
     68   // ui::TableModel:
     69   virtual int RowCount() OVERRIDE;
     70   virtual base::string16 GetText(int row, int column_id) OVERRIDE;
     71   virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE;
     72   virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE;
     73 
     74  private:
     75   ui::TableModelObserver* observer_;
     76 
     77   // The data.
     78   std::vector<std::vector<int> > rows_;
     79 
     80   DISALLOW_COPY_AND_ASSIGN(TestTableModel2);
     81 };
     82 
     83 TestTableModel2::TestTableModel2() : observer_(NULL) {
     84   AddRow(0, 0, 1);
     85   AddRow(1, 1, 1);
     86   AddRow(2, 2, 2);
     87   AddRow(3, 3, 0);
     88 }
     89 
     90 void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
     91   DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
     92   std::vector<int> new_row;
     93   new_row.push_back(c1_value);
     94   new_row.push_back(c2_value);
     95   rows_.insert(rows_.begin() + row, new_row);
     96   if (observer_)
     97     observer_->OnItemsAdded(row, 1);
     98 }
     99 void TestTableModel2::RemoveRow(int row) {
    100   DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
    101   rows_.erase(rows_.begin() + row);
    102   if (observer_)
    103     observer_->OnItemsRemoved(row, 1);
    104 }
    105 
    106 void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
    107   DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
    108   rows_[row][0] = c1_value;
    109   rows_[row][1] = c2_value;
    110   if (observer_)
    111     observer_->OnItemsChanged(row, 1);
    112 }
    113 
    114 int TestTableModel2::RowCount() {
    115   return static_cast<int>(rows_.size());
    116 }
    117 
    118 base::string16 TestTableModel2::GetText(int row, int column_id) {
    119   return base::IntToString16(rows_[row][column_id]);
    120 }
    121 
    122 void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
    123   observer_ = observer;
    124 }
    125 
    126 int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
    127   return rows_[row1][column_id] - rows_[row2][column_id];
    128 }
    129 
    130 // Returns the view to model mapping as a string.
    131 std::string GetViewToModelAsString(TableView* table) {
    132   std::string result;
    133   for (int i = 0; i < table->RowCount(); ++i) {
    134     if (i != 0)
    135       result += " ";
    136     result += base::IntToString(table->ViewToModel(i));
    137   }
    138   return result;
    139 }
    140 
    141 // Returns the model to view mapping as a string.
    142 std::string GetModelToViewAsString(TableView* table) {
    143   std::string result;
    144   for (int i = 0; i < table->RowCount(); ++i) {
    145     if (i != 0)
    146       result += " ";
    147     result += base::IntToString(table->ModelToView(i));
    148   }
    149   return result;
    150 }
    151 
    152 class TestTableView : public TableView {
    153  public:
    154   TestTableView(ui::TableModel* model,
    155                 const std::vector<ui::TableColumn>& columns)
    156       : TableView(model, columns, TEXT_ONLY, false) {
    157   }
    158 
    159   // View overrides:
    160   virtual bool HasFocus() const OVERRIDE {
    161     // Overriden so key processing works.
    162     return true;
    163   }
    164 
    165  private:
    166   DISALLOW_COPY_AND_ASSIGN(TestTableView);
    167 };
    168 
    169 }  // namespace
    170 
    171 class TableViewTest : public testing::Test {
    172  public:
    173   TableViewTest() : table_(NULL) {}
    174 
    175   virtual void SetUp() OVERRIDE {
    176     model_.reset(new TestTableModel2);
    177     std::vector<ui::TableColumn> columns(2);
    178     columns[0].title = base::ASCIIToUTF16("Title Column 0");
    179     columns[0].sortable = true;
    180     columns[1].title = base::ASCIIToUTF16("Title Column 1");
    181     columns[1].id = 1;
    182     columns[1].sortable = true;
    183     table_ = new TestTableView(model_.get(), columns);
    184     parent_.reset(table_->CreateParentIfNecessary());
    185     parent_->SetBounds(0, 0, 10000, 10000);
    186     parent_->Layout();
    187     helper_.reset(new TableViewTestHelper(table_));
    188   }
    189 
    190   void ClickOnRow(int row, int flags) {
    191     const int y = row * table_->row_height();
    192     const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
    193                                  gfx::Point(0, y),
    194                                  ui::EF_LEFT_MOUSE_BUTTON | flags,
    195                                  ui::EF_LEFT_MOUSE_BUTTON);
    196     table_->OnMousePressed(pressed);
    197   }
    198 
    199   void TapOnRow(int row) {
    200     const int y = row * table_->row_height();
    201     const ui::GestureEventDetails event_details(ui::ET_GESTURE_TAP,
    202                                                 .0f, .0f);
    203     ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, y, 0, base::TimeDelta(),
    204                          event_details, 1);
    205     table_->OnGestureEvent(&tap);
    206   }
    207 
    208   // Returns the state of the selection model as a string. The format is:
    209   // 'active=X anchor=X selection=X X X...'.
    210   std::string SelectionStateAsString() const {
    211     const ui::ListSelectionModel& model(table_->selection_model());
    212     std::string result = "active=" + base::IntToString(model.active()) +
    213         " anchor=" + base::IntToString(model.anchor()) +
    214         " selection=";
    215     const ui::ListSelectionModel::SelectedIndices& selection(
    216         model.selected_indices());
    217     for (size_t i = 0; i < selection.size(); ++i) {
    218       if (i != 0)
    219         result += " ";
    220       result += base::IntToString(selection[i]);
    221     }
    222     return result;
    223   }
    224 
    225   void PressKey(ui::KeyboardCode code) {
    226     ui::KeyEvent event(ui::ET_KEY_PRESSED, code, 0, false);
    227     table_->OnKeyPressed(event);
    228   }
    229 
    230  protected:
    231   scoped_ptr<TestTableModel2> model_;
    232 
    233   // Owned by |parent_|.
    234   TableView* table_;
    235 
    236   scoped_ptr<TableViewTestHelper> helper_;
    237 
    238  private:
    239   scoped_ptr<View> parent_;
    240 
    241   DISALLOW_COPY_AND_ASSIGN(TableViewTest);
    242 };
    243 
    244 // Verifies GetPaintRegion.
    245 TEST_F(TableViewTest, GetPaintRegion) {
    246   // Two columns should be visible.
    247   EXPECT_EQ(2u, helper_->visible_col_count());
    248 
    249   EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
    250   EXPECT_EQ("rows=0 4 cols=0 1",
    251             helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
    252 }
    253 
    254 // Verifies SetColumnVisibility().
    255 TEST_F(TableViewTest, ColumnVisibility) {
    256   // Two columns should be visible.
    257   EXPECT_EQ(2u, helper_->visible_col_count());
    258 
    259   // Should do nothing (column already visible).
    260   table_->SetColumnVisibility(0, true);
    261   EXPECT_EQ(2u, helper_->visible_col_count());
    262 
    263   // Hide the first column.
    264   table_->SetColumnVisibility(0, false);
    265   ASSERT_EQ(1u, helper_->visible_col_count());
    266   EXPECT_EQ(1, table_->visible_columns()[0].column.id);
    267   EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
    268 
    269   // Hide the second column.
    270   table_->SetColumnVisibility(1, false);
    271   EXPECT_EQ(0u, helper_->visible_col_count());
    272 
    273   // Show the second column.
    274   table_->SetColumnVisibility(1, true);
    275   ASSERT_EQ(1u, helper_->visible_col_count());
    276   EXPECT_EQ(1, table_->visible_columns()[0].column.id);
    277   EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
    278 
    279   // Show the first column.
    280   table_->SetColumnVisibility(0, true);
    281   ASSERT_EQ(2u, helper_->visible_col_count());
    282   EXPECT_EQ(1, table_->visible_columns()[0].column.id);
    283   EXPECT_EQ(0, table_->visible_columns()[1].column.id);
    284   EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
    285 }
    286 
    287 // Verifies resizing a column works.
    288 TEST_F(TableViewTest, Resize) {
    289   const int x = table_->visible_columns()[0].width;
    290   EXPECT_NE(0, x);
    291   // Drag the mouse 1 pixel to the left.
    292   const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
    293                                gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
    294                                ui::EF_LEFT_MOUSE_BUTTON);
    295   helper_->header()->OnMousePressed(pressed);
    296   const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
    297                                gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON,
    298                                0);
    299   helper_->header()->OnMouseDragged(dragged);
    300 
    301   // This should shrink the first column and pull the second column in.
    302   EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
    303   EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
    304 }
    305 
    306 // Verifies resizing a column works with a gesture.
    307 TEST_F(TableViewTest, ResizeViaGesture) {
    308   const int x = table_->visible_columns()[0].width;
    309   EXPECT_NE(0, x);
    310   // Drag the mouse 1 pixel to the left.
    311   const ui::GestureEventDetails event_details(ui::ET_GESTURE_SCROLL_BEGIN,
    312                                               .0f, .0f);
    313   ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, x, 0, 0,
    314                                 base::TimeDelta(), event_details, 1);
    315   helper_->header()->OnGestureEvent(&scroll_begin);
    316   ui::GestureEvent scroll_update(ui::ET_GESTURE_SCROLL_UPDATE, x - 1, 0,
    317                                  0, base::TimeDelta(), event_details, 1);
    318   helper_->header()->OnGestureEvent(&scroll_update);
    319 
    320   // This should shrink the first column and pull the second column in.
    321   EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
    322   EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
    323 }
    324 
    325 // Assertions for table sorting.
    326 TEST_F(TableViewTest, Sort) {
    327   // Toggle the sort order of the first column, shouldn't change anything.
    328   table_->ToggleSortOrder(0);
    329   ASSERT_EQ(1u, table_->sort_descriptors().size());
    330   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    331   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    332   EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
    333   EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
    334 
    335   // Invert the sort (first column descending).
    336   table_->ToggleSortOrder(0);
    337   ASSERT_EQ(1u, table_->sort_descriptors().size());
    338   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    339   EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
    340   EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_));
    341   EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_));
    342 
    343   // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column).
    344   model_->ChangeRow(3, -1, 0);
    345   ASSERT_EQ(1u, table_->sort_descriptors().size());
    346   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    347   EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
    348   EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_));
    349   EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_));
    350 
    351   // Invert sort again (first column ascending).
    352   table_->ToggleSortOrder(0);
    353   ASSERT_EQ(1u, table_->sort_descriptors().size());
    354   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    355   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    356   EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_));
    357   EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_));
    358 
    359   // Add a row so that model has 0, 3, 1, 2, -1.
    360   model_->AddRow(1, 3, 4);
    361   ASSERT_EQ(1u, table_->sort_descriptors().size());
    362   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    363   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    364   EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_));
    365   EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_));
    366 
    367   // Delete the first row, ending up with 3, 1, 2, -1.
    368   model_->RemoveRow(0);
    369   ASSERT_EQ(1u, table_->sort_descriptors().size());
    370   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    371   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    372   EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_));
    373   EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_));
    374 }
    375 
    376 // Verfies clicking on the header sorts.
    377 TEST_F(TableViewTest, SortOnMouse) {
    378   EXPECT_TRUE(table_->sort_descriptors().empty());
    379 
    380   const int x = table_->visible_columns()[0].width / 2;
    381   EXPECT_NE(0, x);
    382   // Press and release the mouse.
    383   const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
    384                                gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
    385                                ui::EF_LEFT_MOUSE_BUTTON);
    386   // The header must return true, else it won't normally get the release.
    387   EXPECT_TRUE(helper_->header()->OnMousePressed(pressed));
    388   const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0),
    389                                gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
    390                                ui::EF_LEFT_MOUSE_BUTTON);
    391   helper_->header()->OnMouseReleased(release);
    392 
    393   ASSERT_EQ(1u, table_->sort_descriptors().size());
    394   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    395   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    396 }
    397 
    398 namespace {
    399 
    400 class TableGrouperImpl : public TableGrouper {
    401  public:
    402   TableGrouperImpl() {}
    403 
    404   void SetRanges(const std::vector<int>& ranges) {
    405     ranges_ = ranges;
    406   }
    407 
    408   // TableGrouper overrides:
    409   virtual void GetGroupRange(int model_index, GroupRange* range) OVERRIDE {
    410     int offset = 0;
    411     size_t range_index = 0;
    412     for (; range_index < ranges_.size() && offset < model_index; ++range_index)
    413       offset += ranges_[range_index];
    414 
    415     if (offset == model_index) {
    416       range->start = model_index;
    417       range->length = ranges_[range_index];
    418     } else {
    419       range->start = offset - ranges_[range_index - 1];
    420       range->length = ranges_[range_index - 1];
    421     }
    422   }
    423 
    424  private:
    425   std::vector<int> ranges_;
    426 
    427   DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl);
    428 };
    429 
    430 }  // namespace
    431 
    432 // Assertions around grouping.
    433 TEST_F(TableViewTest, Grouping) {
    434   // Configure the grouper so that there are two groups:
    435   // A 0
    436   //   1
    437   // B 2
    438   //   3
    439   TableGrouperImpl grouper;
    440   std::vector<int> ranges;
    441   ranges.push_back(2);
    442   ranges.push_back(2);
    443   grouper.SetRanges(ranges);
    444   table_->SetGrouper(&grouper);
    445 
    446   // Toggle the sort order of the first column, shouldn't change anything.
    447   table_->ToggleSortOrder(0);
    448   ASSERT_EQ(1u, table_->sort_descriptors().size());
    449   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    450   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    451   EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
    452   EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
    453 
    454   // Sort descending, resulting:
    455   // B 2
    456   //   3
    457   // A 0
    458   //   1
    459   table_->ToggleSortOrder(0);
    460   ASSERT_EQ(1u, table_->sort_descriptors().size());
    461   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    462   EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
    463   EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
    464   EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
    465 
    466   // Change the entry in the 4th row to -1. The model now becomes:
    467   // A 0
    468   //   1
    469   // B 2
    470   //   -1
    471   // Since the first entry in the range didn't change the sort isn't impacted.
    472   model_->ChangeRow(3, -1, 0);
    473   ASSERT_EQ(1u, table_->sort_descriptors().size());
    474   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    475   EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
    476   EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
    477   EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
    478 
    479   // Change the entry in the 3rd row to -1. The model now becomes:
    480   // A 0
    481   //   1
    482   // B -1
    483   //   -1
    484   model_->ChangeRow(2, -1, 0);
    485   ASSERT_EQ(1u, table_->sort_descriptors().size());
    486   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    487   EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
    488   EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
    489   EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
    490 
    491   // Toggle to ascending sort.
    492   table_->ToggleSortOrder(0);
    493   ASSERT_EQ(1u, table_->sort_descriptors().size());
    494   EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
    495   EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
    496   EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
    497   EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
    498 }
    499 
    500 namespace {
    501 
    502 class TableViewObserverImpl : public TableViewObserver {
    503  public:
    504   TableViewObserverImpl() : selection_changed_count_(0) {}
    505 
    506   int GetChangedCountAndClear() {
    507     const int count = selection_changed_count_;
    508     selection_changed_count_ = 0;
    509     return count;
    510   }
    511 
    512   // TableViewObserver overrides:
    513   virtual void OnSelectionChanged() OVERRIDE {
    514     selection_changed_count_++;
    515   }
    516 
    517  private:
    518   int selection_changed_count_;
    519 
    520   DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl);
    521 };
    522 
    523 }  // namespace
    524 
    525 // Assertions around changing the selection.
    526 TEST_F(TableViewTest, Selection) {
    527   TableViewObserverImpl observer;
    528   table_->SetObserver(&observer);
    529 
    530   // Initially no selection.
    531   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    532 
    533   // Select the last row.
    534   table_->Select(3);
    535   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    536   EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
    537 
    538   // Change sort, shouldn't notify of change (toggle twice so that order
    539   // actually changes).
    540   table_->ToggleSortOrder(0);
    541   table_->ToggleSortOrder(0);
    542   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    543   EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
    544 
    545   // Remove the selected row, this should notify of a change and update the
    546   // selection.
    547   model_->RemoveRow(3);
    548   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    549   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    550 
    551   // Insert a row, since the selection in terms of the original model hasn't
    552   // changed the observer is not notified.
    553   model_->AddRow(0, 1, 2);
    554   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    555   EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
    556 
    557   table_->SetObserver(NULL);
    558 }
    559 
    560 // Verifies selection works by way of a gesture.
    561 TEST_F(TableViewTest, SelectOnTap) {
    562   // Initially no selection.
    563   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    564 
    565   TableViewObserverImpl observer;
    566   table_->SetObserver(&observer);
    567 
    568   // Click on the first row, should select it.
    569   TapOnRow(0);
    570   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    571   EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
    572 
    573   table_->SetObserver(NULL);
    574 }
    575 
    576 // Verifies up/down correctly navigates through groups.
    577 TEST_F(TableViewTest, KeyUpDown) {
    578   // Configure the grouper so that there are three groups:
    579   // A 0
    580   //   1
    581   // B 5
    582   // C 2
    583   //   3
    584   model_->AddRow(2, 5, 0);
    585   TableGrouperImpl grouper;
    586   std::vector<int> ranges;
    587   ranges.push_back(2);
    588   ranges.push_back(1);
    589   ranges.push_back(2);
    590   grouper.SetRanges(ranges);
    591   table_->SetGrouper(&grouper);
    592 
    593   TableViewObserverImpl observer;
    594   table_->SetObserver(&observer);
    595 
    596   // Initially no selection.
    597   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    598 
    599   PressKey(ui::VKEY_DOWN);
    600   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    601   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    602 
    603   PressKey(ui::VKEY_DOWN);
    604   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    605   EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
    606 
    607   PressKey(ui::VKEY_DOWN);
    608   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    609   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    610 
    611   PressKey(ui::VKEY_DOWN);
    612   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    613   EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
    614 
    615   PressKey(ui::VKEY_DOWN);
    616   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    617   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    618 
    619   PressKey(ui::VKEY_DOWN);
    620   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    621   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    622 
    623   PressKey(ui::VKEY_UP);
    624   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    625   EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
    626 
    627   PressKey(ui::VKEY_UP);
    628   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    629   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    630 
    631   PressKey(ui::VKEY_UP);
    632   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    633   EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
    634 
    635   PressKey(ui::VKEY_UP);
    636   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    637   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    638 
    639   PressKey(ui::VKEY_UP);
    640   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    641   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    642 
    643   // Sort the table descending by column 1, view now looks like:
    644   // B 5   model: 2
    645   // C 2          3
    646   //   3          4
    647   // A 0          0
    648   //   1          1
    649   table_->ToggleSortOrder(0);
    650   table_->ToggleSortOrder(0);
    651 
    652   EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
    653 
    654   table_->Select(-1);
    655   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    656 
    657   observer.GetChangedCountAndClear();
    658   // Up with nothing selected selects the first row.
    659   PressKey(ui::VKEY_UP);
    660   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    661   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    662 
    663   PressKey(ui::VKEY_DOWN);
    664   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    665   EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
    666 
    667   PressKey(ui::VKEY_DOWN);
    668   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    669   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    670 
    671   PressKey(ui::VKEY_DOWN);
    672   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    673   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    674 
    675   PressKey(ui::VKEY_DOWN);
    676   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    677   EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
    678 
    679   PressKey(ui::VKEY_DOWN);
    680   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    681   EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
    682 
    683   PressKey(ui::VKEY_UP);
    684   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    685   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    686 
    687   PressKey(ui::VKEY_UP);
    688   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    689   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    690 
    691   PressKey(ui::VKEY_UP);
    692   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    693   EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
    694 
    695   PressKey(ui::VKEY_UP);
    696   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    697   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    698 
    699   PressKey(ui::VKEY_UP);
    700   EXPECT_EQ(0, observer.GetChangedCountAndClear());
    701   EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
    702 
    703   table_->SetObserver(NULL);
    704 }
    705 
    706 // Verifies home/end do the right thing.
    707 TEST_F(TableViewTest, HomeEnd) {
    708   // Configure the grouper so that there are three groups:
    709   // A 0
    710   //   1
    711   // B 5
    712   // C 2
    713   //   3
    714   model_->AddRow(2, 5, 0);
    715   TableGrouperImpl grouper;
    716   std::vector<int> ranges;
    717   ranges.push_back(2);
    718   ranges.push_back(1);
    719   ranges.push_back(2);
    720   grouper.SetRanges(ranges);
    721   table_->SetGrouper(&grouper);
    722 
    723   TableViewObserverImpl observer;
    724   table_->SetObserver(&observer);
    725 
    726   // Initially no selection.
    727   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    728 
    729   PressKey(ui::VKEY_HOME);
    730   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    731   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    732 
    733   PressKey(ui::VKEY_END);
    734   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    735   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    736 
    737   table_->SetObserver(NULL);
    738 }
    739 
    740 // Verifies multiple selection gestures work (control-click, shift-click ...).
    741 TEST_F(TableViewTest, Multiselection) {
    742   // Configure the grouper so that there are three groups:
    743   // A 0
    744   //   1
    745   // B 5
    746   // C 2
    747   //   3
    748   model_->AddRow(2, 5, 0);
    749   TableGrouperImpl grouper;
    750   std::vector<int> ranges;
    751   ranges.push_back(2);
    752   ranges.push_back(1);
    753   ranges.push_back(2);
    754   grouper.SetRanges(ranges);
    755   table_->SetGrouper(&grouper);
    756 
    757   // Initially no selection.
    758   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    759 
    760   TableViewObserverImpl observer;
    761   table_->SetObserver(&observer);
    762 
    763   // Click on the first row, should select it and the second row.
    764   ClickOnRow(0, 0);
    765   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    766   EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
    767 
    768   // Click on the last row, should select it and the row before it.
    769   ClickOnRow(4, 0);
    770   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    771   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    772 
    773   // Shift click on the third row, should extend selection to it.
    774   ClickOnRow(2, ui::EF_SHIFT_DOWN);
    775   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    776   EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
    777 
    778   // Control click on third row, should toggle it.
    779   ClickOnRow(2, ui::EF_CONTROL_DOWN);
    780   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    781   EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString());
    782 
    783   // Control-shift click on second row, should extend selection to it.
    784   ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
    785   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    786   EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString());
    787 
    788   // Click on last row again.
    789   ClickOnRow(4, 0);
    790   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    791   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    792 
    793   table_->SetObserver(NULL);
    794 }
    795 
    796 // Verifies multiple selection gestures work when sorted.
    797 TEST_F(TableViewTest, MultiselectionWithSort) {
    798   // Configure the grouper so that there are three groups:
    799   // A 0
    800   //   1
    801   // B 5
    802   // C 2
    803   //   3
    804   model_->AddRow(2, 5, 0);
    805   TableGrouperImpl grouper;
    806   std::vector<int> ranges;
    807   ranges.push_back(2);
    808   ranges.push_back(1);
    809   ranges.push_back(2);
    810   grouper.SetRanges(ranges);
    811   table_->SetGrouper(&grouper);
    812 
    813   // Sort the table descending by column 1, view now looks like:
    814   // B 5   model: 2
    815   // C 2          3
    816   //   3          4
    817   // A 0          0
    818   //   1          1
    819   table_->ToggleSortOrder(0);
    820   table_->ToggleSortOrder(0);
    821 
    822   // Initially no selection.
    823   EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
    824 
    825   TableViewObserverImpl observer;
    826   table_->SetObserver(&observer);
    827 
    828   // Click on the third row, should select it and the second row.
    829   ClickOnRow(2, 0);
    830   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    831   EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
    832 
    833   // Extend selection to first row.
    834   ClickOnRow(0, ui::EF_SHIFT_DOWN);
    835   EXPECT_EQ(1, observer.GetChangedCountAndClear());
    836   EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
    837 
    838   table_->SetObserver(NULL);
    839 }
    840 
    841 }  // namespace views
    842