Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / controls / table / table_view_unittest.cc
blobe7d9f87fef474510b63479b66da42f4a8c5e27a6
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.
5 #include "ui/views/controls/table/table_view.h"
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/events/event_utils.h"
11 #include "ui/views/controls/table/table_grouper.h"
12 #include "ui/views/controls/table/table_header.h"
13 #include "ui/views/controls/table/table_view_observer.h"
15 // Put the tests in the views namespace to make it easier to declare them as
16 // friend classes.
17 namespace views {
19 class TableViewTestHelper {
20 public:
21 explicit TableViewTestHelper(TableView* table) : table_(table) {}
23 std::string GetPaintRegion(const gfx::Rect& bounds) {
24 TableView::PaintRegion region(table_->GetPaintRegion(bounds));
25 return "rows=" + base::IntToString(region.min_row) + " " +
26 base::IntToString(region.max_row) + " cols=" +
27 base::IntToString(region.min_column) + " " +
28 base::IntToString(region.max_column);
31 size_t visible_col_count() {
32 return table_->visible_columns().size();
35 TableHeader* header() { return table_->header_; }
37 void SetSelectionModel(const ui::ListSelectionModel& new_selection) {
38 table_->SetSelectionModel(new_selection);
41 void OnFocus() {
42 table_->OnFocus();
45 private:
46 TableView* table_;
48 DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper);
51 namespace {
53 // TestTableModel2 -------------------------------------------------------------
55 // Trivial TableModel implementation that is backed by a vector of vectors.
56 // Provides methods for adding/removing/changing the contents that notify the
57 // observer appropriately.
59 // Initial contents are:
60 // 0, 1
61 // 1, 1
62 // 2, 2
63 // 3, 0
64 class TestTableModel2 : public ui::TableModel {
65 public:
66 TestTableModel2();
68 // Adds a new row at index |row| with values |c1_value| and |c2_value|.
69 void AddRow(int row, int c1_value, int c2_value);
71 // Removes the row at index |row|.
72 void RemoveRow(int row);
74 // Changes the values of the row at |row|.
75 void ChangeRow(int row, int c1_value, int c2_value);
77 // ui::TableModel:
78 int RowCount() override;
79 base::string16 GetText(int row, int column_id) override;
80 void SetObserver(ui::TableModelObserver* observer) override;
81 int CompareValues(int row1, int row2, int column_id) override;
83 private:
84 ui::TableModelObserver* observer_;
86 // The data.
87 std::vector<std::vector<int> > rows_;
89 DISALLOW_COPY_AND_ASSIGN(TestTableModel2);
92 TestTableModel2::TestTableModel2() : observer_(NULL) {
93 AddRow(0, 0, 1);
94 AddRow(1, 1, 1);
95 AddRow(2, 2, 2);
96 AddRow(3, 3, 0);
99 void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
100 DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
101 std::vector<int> new_row;
102 new_row.push_back(c1_value);
103 new_row.push_back(c2_value);
104 rows_.insert(rows_.begin() + row, new_row);
105 if (observer_)
106 observer_->OnItemsAdded(row, 1);
108 void TestTableModel2::RemoveRow(int row) {
109 DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
110 rows_.erase(rows_.begin() + row);
111 if (observer_)
112 observer_->OnItemsRemoved(row, 1);
115 void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
116 DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
117 rows_[row][0] = c1_value;
118 rows_[row][1] = c2_value;
119 if (observer_)
120 observer_->OnItemsChanged(row, 1);
123 int TestTableModel2::RowCount() {
124 return static_cast<int>(rows_.size());
127 base::string16 TestTableModel2::GetText(int row, int column_id) {
128 return base::IntToString16(rows_[row][column_id]);
131 void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
132 observer_ = observer;
135 int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
136 return rows_[row1][column_id] - rows_[row2][column_id];
139 // Returns the view to model mapping as a string.
140 std::string GetViewToModelAsString(TableView* table) {
141 std::string result;
142 for (int i = 0; i < table->RowCount(); ++i) {
143 if (i != 0)
144 result += " ";
145 result += base::IntToString(table->ViewToModel(i));
147 return result;
150 // Returns the model to view mapping as a string.
151 std::string GetModelToViewAsString(TableView* table) {
152 std::string result;
153 for (int i = 0; i < table->RowCount(); ++i) {
154 if (i != 0)
155 result += " ";
156 result += base::IntToString(table->ModelToView(i));
158 return result;
161 class TestTableView : public TableView {
162 public:
163 TestTableView(ui::TableModel* model,
164 const std::vector<ui::TableColumn>& columns)
165 : TableView(model, columns, TEXT_ONLY, false) {
168 // View overrides:
169 bool HasFocus() const override {
170 // Overriden so key processing works.
171 return true;
174 private:
175 DISALLOW_COPY_AND_ASSIGN(TestTableView);
178 } // namespace
180 class TableViewTest : public testing::Test {
181 public:
182 TableViewTest() : table_(NULL) {}
184 void SetUp() override {
185 model_.reset(new TestTableModel2);
186 std::vector<ui::TableColumn> columns(2);
187 columns[0].title = base::ASCIIToUTF16("Title Column 0");
188 columns[0].sortable = true;
189 columns[1].title = base::ASCIIToUTF16("Title Column 1");
190 columns[1].id = 1;
191 columns[1].sortable = true;
192 table_ = new TestTableView(model_.get(), columns);
193 parent_.reset(table_->CreateParentIfNecessary());
194 parent_->SetBounds(0, 0, 10000, 10000);
195 parent_->Layout();
196 helper_.reset(new TableViewTestHelper(table_));
199 void ClickOnRow(int row, int flags) {
200 const int y = row * table_->row_height();
201 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
202 gfx::Point(0, y), ui::EventTimeForNow(),
203 ui::EF_LEFT_MOUSE_BUTTON | flags,
204 ui::EF_LEFT_MOUSE_BUTTON);
205 table_->OnMousePressed(pressed);
208 void TapOnRow(int row) {
209 const int y = row * table_->row_height();
210 const ui::GestureEventDetails event_details(ui::ET_GESTURE_TAP);
211 ui::GestureEvent tap(0, y, 0, base::TimeDelta(), event_details);
212 table_->OnGestureEvent(&tap);
215 // Returns the state of the selection model as a string. The format is:
216 // 'active=X anchor=X selection=X X X...'.
217 std::string SelectionStateAsString() const {
218 const ui::ListSelectionModel& model(table_->selection_model());
219 std::string result = "active=" + base::IntToString(model.active()) +
220 " anchor=" + base::IntToString(model.anchor()) +
221 " selection=";
222 const ui::ListSelectionModel::SelectedIndices& selection(
223 model.selected_indices());
224 for (size_t i = 0; i < selection.size(); ++i) {
225 if (i != 0)
226 result += " ";
227 result += base::IntToString(selection[i]);
229 return result;
232 void PressKey(ui::KeyboardCode code) {
233 ui::KeyEvent event(ui::ET_KEY_PRESSED, code, ui::EF_NONE);
234 table_->OnKeyPressed(event);
237 protected:
238 scoped_ptr<TestTableModel2> model_;
240 // Owned by |parent_|.
241 TableView* table_;
243 scoped_ptr<TableViewTestHelper> helper_;
245 private:
246 scoped_ptr<View> parent_;
248 DISALLOW_COPY_AND_ASSIGN(TableViewTest);
251 // Verifies GetPaintRegion.
252 TEST_F(TableViewTest, GetPaintRegion) {
253 // Two columns should be visible.
254 EXPECT_EQ(2u, helper_->visible_col_count());
256 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
257 EXPECT_EQ("rows=0 4 cols=0 1",
258 helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height())));
261 // Verifies SetColumnVisibility().
262 TEST_F(TableViewTest, ColumnVisibility) {
263 // Two columns should be visible.
264 EXPECT_EQ(2u, helper_->visible_col_count());
266 // Should do nothing (column already visible).
267 table_->SetColumnVisibility(0, true);
268 EXPECT_EQ(2u, helper_->visible_col_count());
270 // Hide the first column.
271 table_->SetColumnVisibility(0, false);
272 ASSERT_EQ(1u, helper_->visible_col_count());
273 EXPECT_EQ(1, table_->visible_columns()[0].column.id);
274 EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
276 // Hide the second column.
277 table_->SetColumnVisibility(1, false);
278 EXPECT_EQ(0u, helper_->visible_col_count());
280 // Show the second column.
281 table_->SetColumnVisibility(1, true);
282 ASSERT_EQ(1u, helper_->visible_col_count());
283 EXPECT_EQ(1, table_->visible_columns()[0].column.id);
284 EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds()));
286 // Show the first column.
287 table_->SetColumnVisibility(0, true);
288 ASSERT_EQ(2u, helper_->visible_col_count());
289 EXPECT_EQ(1, table_->visible_columns()[0].column.id);
290 EXPECT_EQ(0, table_->visible_columns()[1].column.id);
291 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds()));
294 // Verifies resizing a column works.
295 TEST_F(TableViewTest, Resize) {
296 const int x = table_->visible_columns()[0].width;
297 EXPECT_NE(0, x);
298 // Drag the mouse 1 pixel to the left.
299 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
300 gfx::Point(x, 0), ui::EventTimeForNow(),
301 ui::EF_LEFT_MOUSE_BUTTON,
302 ui::EF_LEFT_MOUSE_BUTTON);
303 helper_->header()->OnMousePressed(pressed);
304 const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
305 gfx::Point(x - 1, 0), ui::EventTimeForNow(),
306 ui::EF_LEFT_MOUSE_BUTTON, 0);
307 helper_->header()->OnMouseDragged(dragged);
309 // This should shrink the first column and pull the second column in.
310 EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
311 EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
314 // Verifies resizing a column works with a gesture.
315 TEST_F(TableViewTest, ResizeViaGesture) {
316 const int x = table_->visible_columns()[0].width;
317 EXPECT_NE(0, x);
318 // Drag the mouse 1 pixel to the left.
319 ui::GestureEvent scroll_begin(
323 base::TimeDelta(),
324 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
325 helper_->header()->OnGestureEvent(&scroll_begin);
326 ui::GestureEvent scroll_update(
327 x - 1,
330 base::TimeDelta(),
331 ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE));
332 helper_->header()->OnGestureEvent(&scroll_update);
334 // This should shrink the first column and pull the second column in.
335 EXPECT_EQ(x - 1, table_->visible_columns()[0].width);
336 EXPECT_EQ(x - 1, table_->visible_columns()[1].x);
339 // Assertions for table sorting.
340 TEST_F(TableViewTest, Sort) {
341 // Toggle the sort order of the first column, shouldn't change anything.
342 table_->ToggleSortOrder(0);
343 ASSERT_EQ(1u, table_->sort_descriptors().size());
344 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
345 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
346 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
347 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
349 // Invert the sort (first column descending).
350 table_->ToggleSortOrder(0);
351 ASSERT_EQ(1u, table_->sort_descriptors().size());
352 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
353 EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
354 EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_));
355 EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_));
357 // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column).
358 model_->ChangeRow(3, -1, 0);
359 ASSERT_EQ(1u, table_->sort_descriptors().size());
360 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
361 EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
362 EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_));
363 EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_));
365 // Invert sort again (first column ascending).
366 table_->ToggleSortOrder(0);
367 ASSERT_EQ(1u, table_->sort_descriptors().size());
368 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
369 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
370 EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_));
371 EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_));
373 // Add a row so that model has 0, 3, 1, 2, -1.
374 model_->AddRow(1, 3, 4);
375 ASSERT_EQ(1u, table_->sort_descriptors().size());
376 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
377 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
378 EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_));
379 EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_));
381 // Delete the first row, ending up with 3, 1, 2, -1.
382 model_->RemoveRow(0);
383 ASSERT_EQ(1u, table_->sort_descriptors().size());
384 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
385 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
386 EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_));
387 EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_));
390 // Verfies clicking on the header sorts.
391 TEST_F(TableViewTest, SortOnMouse) {
392 EXPECT_TRUE(table_->sort_descriptors().empty());
394 const int x = table_->visible_columns()[0].width / 2;
395 EXPECT_NE(0, x);
396 // Press and release the mouse.
397 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
398 gfx::Point(x, 0), ui::EventTimeForNow(),
399 ui::EF_LEFT_MOUSE_BUTTON,
400 ui::EF_LEFT_MOUSE_BUTTON);
401 // The header must return true, else it won't normally get the release.
402 EXPECT_TRUE(helper_->header()->OnMousePressed(pressed));
403 const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0),
404 gfx::Point(x, 0), ui::EventTimeForNow(),
405 ui::EF_LEFT_MOUSE_BUTTON,
406 ui::EF_LEFT_MOUSE_BUTTON);
407 helper_->header()->OnMouseReleased(release);
409 ASSERT_EQ(1u, table_->sort_descriptors().size());
410 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
411 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
414 namespace {
416 class TableGrouperImpl : public TableGrouper {
417 public:
418 TableGrouperImpl() {}
420 void SetRanges(const std::vector<int>& ranges) {
421 ranges_ = ranges;
424 // TableGrouper overrides:
425 void GetGroupRange(int model_index, GroupRange* range) override {
426 int offset = 0;
427 size_t range_index = 0;
428 for (; range_index < ranges_.size() && offset < model_index; ++range_index)
429 offset += ranges_[range_index];
431 if (offset == model_index) {
432 range->start = model_index;
433 range->length = ranges_[range_index];
434 } else {
435 range->start = offset - ranges_[range_index - 1];
436 range->length = ranges_[range_index - 1];
440 private:
441 std::vector<int> ranges_;
443 DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl);
446 } // namespace
448 // Assertions around grouping.
449 TEST_F(TableViewTest, Grouping) {
450 // Configure the grouper so that there are two groups:
451 // A 0
452 // 1
453 // B 2
454 // 3
455 TableGrouperImpl grouper;
456 std::vector<int> ranges;
457 ranges.push_back(2);
458 ranges.push_back(2);
459 grouper.SetRanges(ranges);
460 table_->SetGrouper(&grouper);
462 // Toggle the sort order of the first column, shouldn't change anything.
463 table_->ToggleSortOrder(0);
464 ASSERT_EQ(1u, table_->sort_descriptors().size());
465 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
466 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
467 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
468 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
470 // Sort descending, resulting:
471 // B 2
472 // 3
473 // A 0
474 // 1
475 table_->ToggleSortOrder(0);
476 ASSERT_EQ(1u, table_->sort_descriptors().size());
477 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
478 EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
479 EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
480 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
482 // Change the entry in the 4th row to -1. The model now becomes:
483 // A 0
484 // 1
485 // B 2
486 // -1
487 // Since the first entry in the range didn't change the sort isn't impacted.
488 model_->ChangeRow(3, -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("2 3 0 1", GetViewToModelAsString(table_));
493 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
495 // Change the entry in the 3rd row to -1. The model now becomes:
496 // A 0
497 // 1
498 // B -1
499 // -1
500 model_->ChangeRow(2, -1, 0);
501 ASSERT_EQ(1u, table_->sort_descriptors().size());
502 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
503 EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
504 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_));
505 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_));
507 // Toggle to ascending sort.
508 table_->ToggleSortOrder(0);
509 ASSERT_EQ(1u, table_->sort_descriptors().size());
510 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id);
511 EXPECT_TRUE(table_->sort_descriptors()[0].ascending);
512 EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_));
513 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_));
516 namespace {
518 class TableViewObserverImpl : public TableViewObserver {
519 public:
520 TableViewObserverImpl() : selection_changed_count_(0) {}
522 int GetChangedCountAndClear() {
523 const int count = selection_changed_count_;
524 selection_changed_count_ = 0;
525 return count;
528 // TableViewObserver overrides:
529 void OnSelectionChanged() override { selection_changed_count_++; }
531 private:
532 int selection_changed_count_;
534 DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl);
537 } // namespace
539 // Assertions around changing the selection.
540 TEST_F(TableViewTest, Selection) {
541 TableViewObserverImpl observer;
542 table_->SetObserver(&observer);
544 // Initially no selection.
545 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
547 // Select the last row.
548 table_->Select(3);
549 EXPECT_EQ(1, observer.GetChangedCountAndClear());
550 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
552 // Change sort, shouldn't notify of change (toggle twice so that order
553 // actually changes).
554 table_->ToggleSortOrder(0);
555 table_->ToggleSortOrder(0);
556 EXPECT_EQ(0, observer.GetChangedCountAndClear());
557 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
559 // Remove the selected row, this should notify of a change and update the
560 // selection.
561 model_->RemoveRow(3);
562 EXPECT_EQ(1, observer.GetChangedCountAndClear());
563 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
565 // Insert a row, since the selection in terms of the original model hasn't
566 // changed the observer is not notified.
567 model_->AddRow(0, 1, 2);
568 EXPECT_EQ(0, observer.GetChangedCountAndClear());
569 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
571 table_->SetObserver(NULL);
574 // Verifies selection works by way of a gesture.
575 TEST_F(TableViewTest, SelectOnTap) {
576 // Initially no selection.
577 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
579 TableViewObserverImpl observer;
580 table_->SetObserver(&observer);
582 // Click on the first row, should select it.
583 TapOnRow(0);
584 EXPECT_EQ(1, observer.GetChangedCountAndClear());
585 EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
587 table_->SetObserver(NULL);
590 // Verifies up/down correctly navigates through groups.
591 TEST_F(TableViewTest, KeyUpDown) {
592 // Configure the grouper so that there are three groups:
593 // A 0
594 // 1
595 // B 5
596 // C 2
597 // 3
598 model_->AddRow(2, 5, 0);
599 TableGrouperImpl grouper;
600 std::vector<int> ranges;
601 ranges.push_back(2);
602 ranges.push_back(1);
603 ranges.push_back(2);
604 grouper.SetRanges(ranges);
605 table_->SetGrouper(&grouper);
607 TableViewObserverImpl observer;
608 table_->SetObserver(&observer);
610 // Initially no selection.
611 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
613 PressKey(ui::VKEY_DOWN);
614 EXPECT_EQ(1, observer.GetChangedCountAndClear());
615 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
617 PressKey(ui::VKEY_DOWN);
618 EXPECT_EQ(1, observer.GetChangedCountAndClear());
619 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
621 PressKey(ui::VKEY_DOWN);
622 EXPECT_EQ(1, observer.GetChangedCountAndClear());
623 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
625 PressKey(ui::VKEY_DOWN);
626 EXPECT_EQ(1, observer.GetChangedCountAndClear());
627 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
629 PressKey(ui::VKEY_DOWN);
630 EXPECT_EQ(1, observer.GetChangedCountAndClear());
631 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
633 PressKey(ui::VKEY_DOWN);
634 EXPECT_EQ(0, observer.GetChangedCountAndClear());
635 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
637 PressKey(ui::VKEY_UP);
638 EXPECT_EQ(1, observer.GetChangedCountAndClear());
639 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
641 PressKey(ui::VKEY_UP);
642 EXPECT_EQ(1, observer.GetChangedCountAndClear());
643 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
645 PressKey(ui::VKEY_UP);
646 EXPECT_EQ(1, observer.GetChangedCountAndClear());
647 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
649 PressKey(ui::VKEY_UP);
650 EXPECT_EQ(1, observer.GetChangedCountAndClear());
651 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
653 PressKey(ui::VKEY_UP);
654 EXPECT_EQ(0, observer.GetChangedCountAndClear());
655 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
657 // Sort the table descending by column 1, view now looks like:
658 // B 5 model: 2
659 // C 2 3
660 // 3 4
661 // A 0 0
662 // 1 1
663 table_->ToggleSortOrder(0);
664 table_->ToggleSortOrder(0);
666 EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
668 table_->Select(-1);
669 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
671 observer.GetChangedCountAndClear();
672 // Up with nothing selected selects the first row.
673 PressKey(ui::VKEY_UP);
674 EXPECT_EQ(1, observer.GetChangedCountAndClear());
675 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
677 PressKey(ui::VKEY_DOWN);
678 EXPECT_EQ(1, observer.GetChangedCountAndClear());
679 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
681 PressKey(ui::VKEY_DOWN);
682 EXPECT_EQ(1, observer.GetChangedCountAndClear());
683 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
685 PressKey(ui::VKEY_DOWN);
686 EXPECT_EQ(1, observer.GetChangedCountAndClear());
687 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
689 PressKey(ui::VKEY_DOWN);
690 EXPECT_EQ(1, observer.GetChangedCountAndClear());
691 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
693 PressKey(ui::VKEY_DOWN);
694 EXPECT_EQ(0, observer.GetChangedCountAndClear());
695 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
697 PressKey(ui::VKEY_UP);
698 EXPECT_EQ(1, observer.GetChangedCountAndClear());
699 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
701 PressKey(ui::VKEY_UP);
702 EXPECT_EQ(1, observer.GetChangedCountAndClear());
703 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
705 PressKey(ui::VKEY_UP);
706 EXPECT_EQ(1, observer.GetChangedCountAndClear());
707 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
709 PressKey(ui::VKEY_UP);
710 EXPECT_EQ(1, observer.GetChangedCountAndClear());
711 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
713 PressKey(ui::VKEY_UP);
714 EXPECT_EQ(0, observer.GetChangedCountAndClear());
715 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
717 table_->SetObserver(NULL);
720 // Verifies home/end do the right thing.
721 TEST_F(TableViewTest, HomeEnd) {
722 // Configure the grouper so that there are three groups:
723 // A 0
724 // 1
725 // B 5
726 // C 2
727 // 3
728 model_->AddRow(2, 5, 0);
729 TableGrouperImpl grouper;
730 std::vector<int> ranges;
731 ranges.push_back(2);
732 ranges.push_back(1);
733 ranges.push_back(2);
734 grouper.SetRanges(ranges);
735 table_->SetGrouper(&grouper);
737 TableViewObserverImpl observer;
738 table_->SetObserver(&observer);
740 // Initially no selection.
741 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
743 PressKey(ui::VKEY_HOME);
744 EXPECT_EQ(1, observer.GetChangedCountAndClear());
745 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
747 PressKey(ui::VKEY_END);
748 EXPECT_EQ(1, observer.GetChangedCountAndClear());
749 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
751 table_->SetObserver(NULL);
754 // Verifies multiple selection gestures work (control-click, shift-click ...).
755 TEST_F(TableViewTest, Multiselection) {
756 // Configure the grouper so that there are three groups:
757 // A 0
758 // 1
759 // B 5
760 // C 2
761 // 3
762 model_->AddRow(2, 5, 0);
763 TableGrouperImpl grouper;
764 std::vector<int> ranges;
765 ranges.push_back(2);
766 ranges.push_back(1);
767 ranges.push_back(2);
768 grouper.SetRanges(ranges);
769 table_->SetGrouper(&grouper);
771 // Initially no selection.
772 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
774 TableViewObserverImpl observer;
775 table_->SetObserver(&observer);
777 // Click on the first row, should select it and the second row.
778 ClickOnRow(0, 0);
779 EXPECT_EQ(1, observer.GetChangedCountAndClear());
780 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
782 // Click on the last row, should select it and the row before it.
783 ClickOnRow(4, 0);
784 EXPECT_EQ(1, observer.GetChangedCountAndClear());
785 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
787 // Shift click on the third row, should extend selection to it.
788 ClickOnRow(2, ui::EF_SHIFT_DOWN);
789 EXPECT_EQ(1, observer.GetChangedCountAndClear());
790 EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
792 // Control click on third row, should toggle it.
793 ClickOnRow(2, ui::EF_CONTROL_DOWN);
794 EXPECT_EQ(1, observer.GetChangedCountAndClear());
795 EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString());
797 // Control-shift click on second row, should extend selection to it.
798 ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
799 EXPECT_EQ(1, observer.GetChangedCountAndClear());
800 EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString());
802 // Click on last row again.
803 ClickOnRow(4, 0);
804 EXPECT_EQ(1, observer.GetChangedCountAndClear());
805 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
807 table_->SetObserver(NULL);
810 // Verifies multiple selection gestures work when sorted.
811 TEST_F(TableViewTest, MultiselectionWithSort) {
812 // Configure the grouper so that there are three groups:
813 // A 0
814 // 1
815 // B 5
816 // C 2
817 // 3
818 model_->AddRow(2, 5, 0);
819 TableGrouperImpl grouper;
820 std::vector<int> ranges;
821 ranges.push_back(2);
822 ranges.push_back(1);
823 ranges.push_back(2);
824 grouper.SetRanges(ranges);
825 table_->SetGrouper(&grouper);
827 // Sort the table descending by column 1, view now looks like:
828 // B 5 model: 2
829 // C 2 3
830 // 3 4
831 // A 0 0
832 // 1 1
833 table_->ToggleSortOrder(0);
834 table_->ToggleSortOrder(0);
836 // Initially no selection.
837 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
839 TableViewObserverImpl observer;
840 table_->SetObserver(&observer);
842 // Click on the third row, should select it and the second row.
843 ClickOnRow(2, 0);
844 EXPECT_EQ(1, observer.GetChangedCountAndClear());
845 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
847 // Extend selection to first row.
848 ClickOnRow(0, ui::EF_SHIFT_DOWN);
849 EXPECT_EQ(1, observer.GetChangedCountAndClear());
850 EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString());
852 table_->SetObserver(NULL);
855 // Verifies we don't crash after removing the selected row when there is
856 // sorting and the anchor/active index also match the selected row.
857 TEST_F(TableViewTest, FocusAfterRemovingAnchor) {
858 table_->ToggleSortOrder(0);
860 ui::ListSelectionModel new_selection;
861 new_selection.AddIndexToSelection(0);
862 new_selection.AddIndexToSelection(1);
863 new_selection.set_active(0);
864 new_selection.set_anchor(0);
865 helper_->SetSelectionModel(new_selection);
866 model_->RemoveRow(0);
867 helper_->OnFocus();
870 } // namespace views