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