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