- add sources.
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / apps_grid_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/app_list/views/apps_grid_view.h"
6
7 #include <string>
8
9 #include "base/basictypes.h"
10 #include "base/compiler_specific.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/timer/timer.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/app_list/app_list_item_model.h"
17 #include "ui/app_list/app_list_model.h"
18 #include "ui/app_list/pagination_model.h"
19 #include "ui/app_list/test/app_list_test_model.h"
20 #include "ui/app_list/views/app_list_item_view.h"
21 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
22 #include "ui/views/test/views_test_base.h"
23
24 namespace app_list {
25 namespace test {
26
27 namespace {
28
29 const int kIconDimension = 48;
30 const int kCols = 2;
31 const int kRows = 2;
32 const int kTilesPerPage = kCols * kRows;
33
34 const int kWidth = 320;
35 const int kHeight = 240;
36
37 class PageFlipWaiter : public PaginationModelObserver {
38  public:
39   PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
40       : ui_loop_(ui_loop), model_(model), wait_(false), page_changed_(false) {
41     model_->AddObserver(this);
42   }
43
44   virtual ~PageFlipWaiter() {
45     model_->RemoveObserver(this);
46   }
47
48   bool Wait(int time_out_ms) {
49     DCHECK(!wait_);
50     wait_ = true;
51     page_changed_ = false;
52
53     if (time_out_ms) {
54       wait_timer_.Stop();
55       wait_timer_.Start(FROM_HERE,
56                         base::TimeDelta::FromMilliseconds(time_out_ms),
57                         this, &PageFlipWaiter::OnWaitTimeOut);
58     }
59
60     ui_loop_->Run();
61     wait_ = false;
62     return page_changed_;
63   }
64
65  private:
66   void OnWaitTimeOut() {
67     ui_loop_->Quit();
68   }
69
70   // PaginationModelObserver overrides:
71   virtual void TotalPagesChanged() OVERRIDE {
72   }
73   virtual void SelectedPageChanged(int old_selected,
74                                    int new_selected) OVERRIDE {
75     page_changed_ = true;
76     if (wait_)
77       ui_loop_->Quit();
78   }
79   virtual void TransitionStarted() OVERRIDE {
80   }
81   virtual void TransitionChanged() OVERRIDE {
82   }
83
84   base::MessageLoopForUI* ui_loop_;
85   PaginationModel* model_;
86   bool wait_;
87   bool page_changed_;
88   base::OneShotTimer<PageFlipWaiter> wait_timer_;
89
90   DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
91 };
92
93 }  // namespace
94
95 class AppsGridViewTest : public views::ViewsTestBase {
96  public:
97   AppsGridViewTest() {}
98   virtual ~AppsGridViewTest() {}
99
100   // testing::Test overrides:
101   virtual void SetUp() OVERRIDE {
102     views::ViewsTestBase::SetUp();
103     model_.reset(new AppListTestModel);
104     pagination_model_.reset(new PaginationModel);
105
106     apps_grid_view_.reset(
107         new AppsGridView(NULL, pagination_model_.get(), NULL));
108     apps_grid_view_->SetLayout(kIconDimension, kCols, kRows);
109     apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
110     apps_grid_view_->SetModel(model_.get());
111     apps_grid_view_->SetItemList(model_->item_list());
112
113     test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
114   }
115   virtual void TearDown() OVERRIDE {
116     apps_grid_view_.reset();  // Release apps grid view before models.
117     views::ViewsTestBase::TearDown();
118   }
119
120  protected:
121   AppListItemView* GetItemViewAt(int index) {
122     return static_cast<AppListItemView*>(
123         test_api_->GetViewAtModelIndex(index));
124   }
125
126   AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
127     for (size_t i = 0; i < model_->item_list()->item_count(); ++i) {
128       AppListItemView* view = GetItemViewAt(i);
129       if (view->bounds().Contains(point))
130         return view;
131     }
132     return NULL;
133   }
134
135   gfx::Rect GetItemTileRectAt(int row, int col) {
136     DCHECK_GT(model_->item_list()->item_count(), 0u);
137
138     gfx::Insets insets(apps_grid_view_->GetInsets());
139     gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
140                    GetItemViewAt(0)->bounds().size());
141     rect.Offset(col * rect.width(), row * rect.height());
142     return rect;
143   }
144
145   // Points are in |apps_grid_view_|'s coordinates.
146   void SimulateDrag(AppsGridView::Pointer pointer,
147                     const gfx::Point& from,
148                     const gfx::Point& to) {
149     AppListItemView* view = GetItemViewForPoint(from);
150     DCHECK(view);
151
152     gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
153         from - view->bounds().origin());
154     gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
155         to - view->bounds().origin());
156
157     ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
158                                  translated_from, from, 0);
159     apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
160
161     ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
162                               translated_to, to, 0);
163     apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
164   }
165
166   void SimulateKeyPress(ui::KeyboardCode key_code) {
167     ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false);
168     apps_grid_view_->OnKeyPressed(key_event);
169   }
170
171   scoped_ptr<AppListTestModel> model_;
172   scoped_ptr<PaginationModel> pagination_model_;
173   scoped_ptr<AppsGridView> apps_grid_view_;
174   scoped_ptr<AppsGridViewTestApi> test_api_;
175
176  private:
177   DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
178 };
179
180 TEST_F(AppsGridViewTest, CreatePage) {
181   // Fully populates a page.
182   const int kPages = 1;
183   model_->PopulateApps(kPages * kTilesPerPage);
184   EXPECT_EQ(kPages, pagination_model_->total_pages());
185
186   // Adds one more and gets a new page created.
187   model_->CreateAndAddItem("Extra");
188   EXPECT_EQ(kPages + 1, pagination_model_->total_pages());
189 }
190
191 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
192   const int kPages = 3;
193   model_->PopulateApps(kPages * kTilesPerPage);
194   EXPECT_EQ(kPages, pagination_model_->total_pages());
195   EXPECT_EQ(0, pagination_model_->selected_page());
196
197   // Highlight first one and last one one first page and first page should be
198   // selected.
199   model_->HighlightItemAt(0);
200   EXPECT_EQ(0, pagination_model_->selected_page());
201   model_->HighlightItemAt(kTilesPerPage - 1);
202   EXPECT_EQ(0, pagination_model_->selected_page());
203
204   // Highlight first one on 2nd page and 2nd page should be selected.
205   model_->HighlightItemAt(kTilesPerPage + 1);
206   EXPECT_EQ(1, pagination_model_->selected_page());
207
208   // Highlight last one in the model and last page should be selected.
209   model_->HighlightItemAt(model_->item_list()->item_count() - 1);
210   EXPECT_EQ(kPages - 1, pagination_model_->selected_page());
211 }
212
213 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
214   const int kTotalItems = 2;
215   const int kLastItemIndex = kTotalItems - 1;
216
217   model_->PopulateApps(kTotalItems);
218
219   AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
220   apps_grid_view_->SetSelectedView(last_view);
221   model_->item_list()->DeleteItem(model_->GetItemName(kLastItemIndex));
222
223   EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
224
225   // No crash happens.
226   AppListItemView* view = GetItemViewAt(0);
227   apps_grid_view_->SetSelectedView(view);
228   EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
229 }
230
231 TEST_F(AppsGridViewTest, MouseDrag) {
232   const int kTotalItems = 4;
233   model_->PopulateApps(kTotalItems);
234   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
235             model_->GetModelContent());
236
237   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
238   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
239
240   // Dragging changes model order.
241   SimulateDrag(AppsGridView::MOUSE, from, to);
242   apps_grid_view_->EndDrag(false);
243   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
244             model_->GetModelContent());
245   test_api_->LayoutToIdealBounds();
246
247   // Canceling drag should keep existing order.
248   SimulateDrag(AppsGridView::MOUSE, from, to);
249   apps_grid_view_->EndDrag(true);
250   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
251             model_->GetModelContent());
252   test_api_->LayoutToIdealBounds();
253
254   // Deleting an item keeps remaining intact.
255   SimulateDrag(AppsGridView::MOUSE, from, to);
256   model_->item_list()->DeleteItem(model_->GetItemName(0));
257   apps_grid_view_->EndDrag(false);
258   EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
259             model_->GetModelContent());
260   test_api_->LayoutToIdealBounds();
261
262   // Adding a launcher item cancels the drag and respects the order.
263   SimulateDrag(AppsGridView::MOUSE, from, to);
264   model_->CreateAndAddItem("Extra");
265   apps_grid_view_->EndDrag(false);
266   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
267             model_->GetModelContent());
268   test_api_->LayoutToIdealBounds();
269 }
270
271 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
272   test_api_->SetPageFlipDelay(10);
273   pagination_model_->SetTransitionDurations(10, 10);
274
275   PageFlipWaiter page_flip_waiter(message_loop(),
276                                   pagination_model_.get());
277
278   const int kPages = 3;
279   model_->PopulateApps(kPages * kTilesPerPage);
280   EXPECT_EQ(kPages, pagination_model_->total_pages());
281   EXPECT_EQ(0, pagination_model_->selected_page());
282
283   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
284   gfx::Point to = gfx::Point(apps_grid_view_->width(),
285                              apps_grid_view_->height() / 2);
286
287   // Drag to right edge.
288   SimulateDrag(AppsGridView::MOUSE, from, to);
289
290   // Page should be flipped after sometime.
291   EXPECT_TRUE(page_flip_waiter.Wait(0));
292   EXPECT_EQ(1, pagination_model_->selected_page());
293
294   // Stay there and page should be flipped again.
295   EXPECT_TRUE(page_flip_waiter.Wait(0));
296   EXPECT_EQ(2, pagination_model_->selected_page());
297
298   // Stay there longer and no page flip happen since we are at the last page.
299   EXPECT_FALSE(page_flip_waiter.Wait(100));
300   EXPECT_EQ(2, pagination_model_->selected_page());
301
302   apps_grid_view_->EndDrag(true);
303
304   // Now drag to the left edge and test the other direction.
305   to.set_x(0);
306
307   SimulateDrag(AppsGridView::MOUSE, from, to);
308
309   EXPECT_TRUE(page_flip_waiter.Wait(0));
310   EXPECT_EQ(1, pagination_model_->selected_page());
311
312   EXPECT_TRUE(page_flip_waiter.Wait(0));
313   EXPECT_EQ(0, pagination_model_->selected_page());
314
315   EXPECT_FALSE(page_flip_waiter.Wait(100));
316   EXPECT_EQ(0, pagination_model_->selected_page());
317   apps_grid_view_->EndDrag(true);
318 }
319
320 TEST_F(AppsGridViewTest, SimultaneousDrag) {
321   const int kTotalItems = 4;
322   model_->PopulateApps(kTotalItems);
323   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
324             model_->GetModelContent());
325
326   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
327   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
328
329   gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
330   gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
331
332   // Starts a mouse drag first then a touch drag.
333   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
334   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
335   // Finishes the drag and mouse drag wins.
336   apps_grid_view_->EndDrag(false);
337   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
338             model_->GetModelContent());
339   test_api_->LayoutToIdealBounds();
340
341   // Starts a touch drag first then a mouse drag.
342   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
343   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
344   // Finishes the drag and touch drag wins.
345   apps_grid_view_->EndDrag(false);
346   EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
347             model_->GetModelContent());
348   test_api_->LayoutToIdealBounds();
349 }
350
351 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
352   const int kPages = 3;
353   const int kItems = (kPages - 1) * kTilesPerPage + 1;
354   model_->PopulateApps(kItems);
355
356   const int first_index = 0;
357   const int last_index = kItems - 1;
358   const int last_index_on_page1_first_row = kRows - 1;
359   const int last_index_on_page1 = kTilesPerPage - 1;
360   const int first_index_on_page2 = kTilesPerPage;
361   const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
362   const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
363
364   // Try moving off the item beyond the first one.
365   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
366   SimulateKeyPress(ui::VKEY_UP);
367   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
368   SimulateKeyPress(ui::VKEY_LEFT);
369   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
370
371   // Move to the last item and try to go past it.
372   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
373   SimulateKeyPress(ui::VKEY_DOWN);
374   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
375   SimulateKeyPress(ui::VKEY_RIGHT);
376   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
377
378   // Move right on last item on page 1 should get to first item on page 2's last
379   // row and vice versa.
380   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
381   SimulateKeyPress(ui::VKEY_RIGHT);
382   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
383       first_index_on_page2_last_row)));
384   SimulateKeyPress(ui::VKEY_LEFT);
385   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
386       last_index_on_page1)));
387
388   // Up/down on page boundary does nothing.
389   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
390   SimulateKeyPress(ui::VKEY_DOWN);
391   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
392       last_index_on_page1)));
393   apps_grid_view_->SetSelectedView(
394       GetItemViewAt(first_index_on_page2_last_row));
395   apps_grid_view_->
396       SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
397   SimulateKeyPress(ui::VKEY_UP);
398   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
399       last_index_on_page1_first_row)));
400
401   // Page up and down should go to the same item on the next and last page.
402   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
403   SimulateKeyPress(ui::VKEY_PRIOR);
404   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
405       first_index)));
406   SimulateKeyPress(ui::VKEY_NEXT);
407   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
408       first_index_on_page2)));
409
410   // Moving onto a a page with too few apps to support the expected index snaps
411   // to the last available index.
412   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
413   SimulateKeyPress(ui::VKEY_RIGHT);
414   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
415       last_index)));
416   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
417   SimulateKeyPress(ui::VKEY_NEXT);
418   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
419       last_index)));
420
421
422
423   // After page switch, arrow keys select first item on current page.
424   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
425   pagination_model_->SelectPage(1, false);
426   SimulateKeyPress(ui::VKEY_UP);
427   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
428       first_index_on_page2)));
429 }
430
431 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
432   // If the app's full name and short name differ, the title label's tooltip
433   // should always be the full name of the app.
434   std::string expected_text("xyz");
435   std::string expected_tooltip("tooltip");
436   model_->CreateAndAddItem(expected_text, expected_tooltip);
437
438   string16 actual_tooltip;
439   AppListItemView* item_view = GetItemViewAt(0);
440   ASSERT_TRUE(item_view);
441   const views::Label* title_label = item_view->title();
442   EXPECT_TRUE(title_label->GetTooltipText(
443       title_label->bounds().CenterPoint(), &actual_tooltip));
444   EXPECT_EQ(expected_tooltip, UTF16ToUTF8(actual_tooltip));
445   EXPECT_EQ(expected_text, UTF16ToUTF8(title_label->text()));
446 }
447
448 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
449   // If the app's full name and short name are the same, use the default tooltip
450   // behavior of the label (only show a tooltip if the title is truncated).
451   std::string title("a");
452   model_->CreateAndAddItem(title, title);
453
454   string16 actual_tooltip;
455   AppListItemView* item_view = GetItemViewAt(0);
456   ASSERT_TRUE(item_view);
457   const views::Label* title_label = item_view->title();
458   EXPECT_FALSE(title_label->GetTooltipText(
459       title_label->bounds().CenterPoint(), &actual_tooltip));
460   EXPECT_EQ(title, UTF16ToUTF8(title_label->text()));
461 }
462
463 }  // namespace test
464 }  // namespace app_list