Home | History | Annotate | Download | only in views
      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/command_line.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 #include "ui/app_list/app_list_constants.h"
     18 #include "ui/app_list/app_list_folder_item.h"
     19 #include "ui/app_list/app_list_item.h"
     20 #include "ui/app_list/app_list_model.h"
     21 #include "ui/app_list/app_list_switches.h"
     22 #include "ui/app_list/pagination_model.h"
     23 #include "ui/app_list/test/app_list_test_model.h"
     24 #include "ui/app_list/views/app_list_item_view.h"
     25 #include "ui/app_list/views/apps_grid_view_folder_delegate.h"
     26 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
     27 #include "ui/views/test/views_test_base.h"
     28 
     29 namespace app_list {
     30 namespace test {
     31 
     32 namespace {
     33 
     34 const int kIconDimension = 48;
     35 const int kCols = 2;
     36 const int kRows = 2;
     37 const int kTilesPerPage = kCols * kRows;
     38 
     39 const int kWidth = 320;
     40 const int kHeight = 240;
     41 
     42 class PageFlipWaiter : public PaginationModelObserver {
     43  public:
     44   PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
     45       : ui_loop_(ui_loop), model_(model), wait_(false) {
     46     model_->AddObserver(this);
     47   }
     48 
     49   virtual ~PageFlipWaiter() {
     50     model_->RemoveObserver(this);
     51   }
     52 
     53   void Wait() {
     54     DCHECK(!wait_);
     55     wait_ = true;
     56 
     57     ui_loop_->Run();
     58     wait_ = false;
     59   }
     60 
     61   void Reset() { selected_pages_.clear(); }
     62 
     63   const std::string& selected_pages() const { return selected_pages_; }
     64 
     65  private:
     66   // PaginationModelObserver overrides:
     67   virtual void TotalPagesChanged() OVERRIDE {
     68   }
     69   virtual void SelectedPageChanged(int old_selected,
     70                                    int new_selected) OVERRIDE {
     71     if (!selected_pages_.empty())
     72       selected_pages_ += ',';
     73     selected_pages_ += base::IntToString(new_selected);
     74 
     75     if (wait_)
     76       ui_loop_->Quit();
     77   }
     78   virtual void TransitionStarted() OVERRIDE {
     79   }
     80   virtual void TransitionChanged() OVERRIDE {
     81   }
     82 
     83   base::MessageLoopForUI* ui_loop_;
     84   PaginationModel* model_;
     85   bool wait_;
     86   std::string selected_pages_;
     87 
     88   DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
     89 };
     90 
     91 }  // namespace
     92 
     93 class AppsGridViewTest : public views::ViewsTestBase {
     94  public:
     95   AppsGridViewTest() {}
     96   virtual ~AppsGridViewTest() {}
     97 
     98   // testing::Test overrides:
     99   virtual void SetUp() OVERRIDE {
    100     views::ViewsTestBase::SetUp();
    101     model_.reset(new AppListTestModel);
    102     model_->SetFoldersEnabled(true);
    103 
    104     apps_grid_view_.reset(new AppsGridView(NULL));
    105     apps_grid_view_->SetLayout(kIconDimension, kCols, kRows);
    106     apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
    107     apps_grid_view_->SetModel(model_.get());
    108     apps_grid_view_->SetItemList(model_->top_level_item_list());
    109 
    110     test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
    111   }
    112   virtual void TearDown() OVERRIDE {
    113     apps_grid_view_.reset();  // Release apps grid view before models.
    114     views::ViewsTestBase::TearDown();
    115   }
    116 
    117  protected:
    118   void EnsureFoldersEnabled() {
    119     // Folders require AppList sync to be enabled.
    120     CommandLine::ForCurrentProcess()->AppendSwitch(
    121         switches::kEnableSyncAppList);
    122   }
    123 
    124   AppListItemView* GetItemViewAt(int index) {
    125     return static_cast<AppListItemView*>(
    126         test_api_->GetViewAtModelIndex(index));
    127   }
    128 
    129   AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
    130     for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
    131       AppListItemView* view = GetItemViewAt(i);
    132       if (view->bounds().Contains(point))
    133         return view;
    134     }
    135     return NULL;
    136   }
    137 
    138   gfx::Rect GetItemTileRectAt(int row, int col) {
    139     DCHECK_GT(model_->top_level_item_list()->item_count(), 0u);
    140 
    141     gfx::Insets insets(apps_grid_view_->GetInsets());
    142     gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
    143                    GetItemViewAt(0)->bounds().size());
    144     rect.Offset(col * rect.width(), row * rect.height());
    145     return rect;
    146   }
    147 
    148   PaginationModel* GetPaginationModel() {
    149     return apps_grid_view_->pagination_model();
    150   }
    151 
    152   // Points are in |apps_grid_view_|'s coordinates.
    153   void SimulateDrag(AppsGridView::Pointer pointer,
    154                     const gfx::Point& from,
    155                     const gfx::Point& to) {
    156     AppListItemView* view = GetItemViewForPoint(from);
    157     DCHECK(view);
    158 
    159     gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
    160         from - view->bounds().origin());
    161     gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
    162         to - view->bounds().origin());
    163 
    164     ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
    165                                  translated_from, from, 0, 0);
    166     apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
    167 
    168     ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
    169                               translated_to, to, 0, 0);
    170     apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
    171   }
    172 
    173   void SimulateKeyPress(ui::KeyboardCode key_code) {
    174     ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false);
    175     apps_grid_view_->OnKeyPressed(key_event);
    176   }
    177 
    178   scoped_ptr<AppListTestModel> model_;
    179   scoped_ptr<AppsGridView> apps_grid_view_;
    180   scoped_ptr<AppsGridViewTestApi> test_api_;
    181 
    182  private:
    183   DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
    184 };
    185 
    186 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
    187  public:
    188   TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
    189   virtual ~TestAppsGridViewFolderDelegate() {}
    190 
    191   // Overridden from AppsGridViewFolderDelegate:
    192   virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE {
    193     show_bubble_ = show_bubble;
    194   }
    195 
    196   virtual void ReparentItem(AppListItemView* original_drag_view,
    197                             const gfx::Point& drag_point_in_folder_grid)
    198       OVERRIDE {}
    199 
    200   virtual void DispatchDragEventForReparent(
    201       AppsGridView::Pointer pointer,
    202       const gfx::Point& drag_point_in_folder_grid) OVERRIDE {}
    203 
    204   virtual void DispatchEndDragEventForReparent(
    205       bool events_forwarded_to_drag_drop_host,
    206       bool cancel_drag) OVERRIDE {}
    207 
    208   virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point)
    209       OVERRIDE {
    210     return false;
    211   }
    212 
    213   virtual bool IsOEMFolder() const OVERRIDE { return false; }
    214 
    215   virtual void SetRootLevelDragViewVisible(bool visible) OVERRIDE {}
    216 
    217   bool show_bubble() { return show_bubble_; }
    218 
    219  private:
    220   bool show_bubble_;
    221 
    222   DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
    223 };
    224 
    225 TEST_F(AppsGridViewTest, CreatePage) {
    226   // Fully populates a page.
    227   const int kPages = 1;
    228   model_->PopulateApps(kPages * kTilesPerPage);
    229   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    230 
    231   // Adds one more and gets a new page created.
    232   model_->CreateAndAddItem("Extra");
    233   EXPECT_EQ(kPages + 1, GetPaginationModel()->total_pages());
    234 }
    235 
    236 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
    237   const int kPages = 3;
    238   model_->PopulateApps(kPages * kTilesPerPage);
    239   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    240   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    241 
    242   // Highlight first one and last one one first page and first page should be
    243   // selected.
    244   model_->HighlightItemAt(0);
    245   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    246   model_->HighlightItemAt(kTilesPerPage - 1);
    247   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    248 
    249   // Highlight first one on 2nd page and 2nd page should be selected.
    250   model_->HighlightItemAt(kTilesPerPage + 1);
    251   EXPECT_EQ(1, GetPaginationModel()->selected_page());
    252 
    253   // Highlight last one in the model and last page should be selected.
    254   model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1);
    255   EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page());
    256 }
    257 
    258 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
    259   const int kTotalItems = 2;
    260   const int kLastItemIndex = kTotalItems - 1;
    261 
    262   model_->PopulateApps(kTotalItems);
    263 
    264   AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
    265   apps_grid_view_->SetSelectedView(last_view);
    266   model_->DeleteItem(model_->GetItemName(kLastItemIndex));
    267 
    268   EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
    269 
    270   // No crash happens.
    271   AppListItemView* view = GetItemViewAt(0);
    272   apps_grid_view_->SetSelectedView(view);
    273   EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
    274 }
    275 
    276 TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) {
    277   model_->SetFoldersEnabled(false);
    278   const int kTotalItems = 4;
    279   model_->PopulateApps(kTotalItems);
    280   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    281             model_->GetModelContent());
    282 
    283   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    284   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    285 
    286   // Dragging changes model order.
    287   SimulateDrag(AppsGridView::MOUSE, from, to);
    288   apps_grid_view_->EndDrag(false);
    289   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    290             model_->GetModelContent());
    291   test_api_->LayoutToIdealBounds();
    292 
    293   // Canceling drag should keep existing order.
    294   SimulateDrag(AppsGridView::MOUSE, from, to);
    295   apps_grid_view_->EndDrag(true);
    296   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    297             model_->GetModelContent());
    298   test_api_->LayoutToIdealBounds();
    299 
    300   // Deleting an item keeps remaining intact.
    301   SimulateDrag(AppsGridView::MOUSE, from, to);
    302   model_->DeleteItem(model_->GetItemName(0));
    303   apps_grid_view_->EndDrag(false);
    304   EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
    305             model_->GetModelContent());
    306   test_api_->LayoutToIdealBounds();
    307 
    308   // Adding a launcher item cancels the drag and respects the order.
    309   SimulateDrag(AppsGridView::MOUSE, from, to);
    310   model_->CreateAndAddItem("Extra");
    311   apps_grid_view_->EndDrag(false);
    312   EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
    313             model_->GetModelContent());
    314   test_api_->LayoutToIdealBounds();
    315 }
    316 
    317 TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) {
    318   EnsureFoldersEnabled();
    319 
    320   size_t kTotalItems = 3;
    321   model_->PopulateApps(kTotalItems);
    322   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
    323   EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());
    324 
    325   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    326   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
    327 
    328   // Dragging item_1 over item_0 creates a folder.
    329   SimulateDrag(AppsGridView::MOUSE, from, to);
    330   apps_grid_view_->EndDrag(false);
    331   EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
    332   EXPECT_EQ(AppListFolderItem::kItemType,
    333             model_->top_level_item_list()->item_at(0)->GetItemType());
    334   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    335       model_->top_level_item_list()->item_at(0));
    336   EXPECT_EQ(2u, folder_item->ChildItemCount());
    337   AppListItem* item_0 = model_->FindItem("Item 0");
    338   EXPECT_TRUE(item_0->IsInFolder());
    339   EXPECT_EQ(folder_item->id(), item_0->folder_id());
    340   AppListItem* item_1 = model_->FindItem("Item 1");
    341   EXPECT_TRUE(item_1->IsInFolder());
    342   EXPECT_EQ(folder_item->id(), item_1->folder_id());
    343   std::string expected_items = folder_item->id() + ",Item 2";
    344   EXPECT_EQ(expected_items, model_->GetModelContent());
    345   test_api_->LayoutToIdealBounds();
    346 
    347   // Dragging item_2 to the folder adds item_2 to the folder.
    348   SimulateDrag(AppsGridView::MOUSE, from, to);
    349   apps_grid_view_->EndDrag(false);
    350 
    351   EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
    352   EXPECT_EQ(folder_item->id(), model_->GetModelContent());
    353   EXPECT_EQ(3u, folder_item->ChildItemCount());
    354   item_0 = model_->FindItem("Item 0");
    355   EXPECT_TRUE(item_0->IsInFolder());
    356   EXPECT_EQ(folder_item->id(), item_0->folder_id());
    357   item_1 = model_->FindItem("Item 1");
    358   EXPECT_TRUE(item_1->IsInFolder());
    359   EXPECT_EQ(folder_item->id(), item_1->folder_id());
    360   AppListItem* item_2 = model_->FindItem("Item 2");
    361   EXPECT_TRUE(item_2->IsInFolder());
    362   EXPECT_EQ(folder_item->id(), item_2->folder_id());
    363   test_api_->LayoutToIdealBounds();
    364 }
    365 
    366 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) {
    367   EnsureFoldersEnabled();
    368 
    369   // Create and add a folder with 15 items in it.
    370   size_t kTotalItems = kMaxFolderItems - 1;
    371   model_->CreateAndPopulateFolderWithApps(kTotalItems);
    372   EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
    373   EXPECT_EQ(AppListFolderItem::kItemType,
    374             model_->top_level_item_list()->item_at(0)->GetItemType());
    375   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    376       model_->top_level_item_list()->item_at(0));
    377   EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
    378 
    379   // Create and add another 2 items.
    380   model_->PopulateAppWithId(kTotalItems);
    381   model_->PopulateAppWithId(kTotalItems + 1);
    382   EXPECT_EQ(3u, model_->top_level_item_list()->item_count());
    383   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
    384   EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1),
    385             model_->top_level_item_list()->item_at(1)->id());
    386   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    387             model_->top_level_item_list()->item_at(2)->id());
    388 
    389   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    390   gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
    391 
    392   // Dragging one item into the folder, the folder should accept the item.
    393   SimulateDrag(AppsGridView::MOUSE, from, to);
    394   apps_grid_view_->EndDrag(false);
    395   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    396   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
    397   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
    398   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    399             model_->top_level_item_list()->item_at(1)->id());
    400   test_api_->LayoutToIdealBounds();
    401 
    402   // Dragging the last item over the folder, the folder won't accept the new
    403   // item, instead, it will re-order the items.
    404   SimulateDrag(AppsGridView::MOUSE, from, to);
    405   apps_grid_view_->EndDrag(false);
    406   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    407   EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
    408             model_->top_level_item_list()->item_at(0)->id());
    409   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
    410   EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
    411   test_api_->LayoutToIdealBounds();
    412 }
    413 
    414 TEST_F(AppsGridViewTest, MouseDragItemReorder) {
    415   // This test assumes Folders are enabled.
    416   EnsureFoldersEnabled();
    417 
    418   size_t kTotalItems = 2;
    419   model_->PopulateApps(kTotalItems);
    420   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    421   EXPECT_EQ(std::string("Item 0,Item 1"), model_->GetModelContent());
    422 
    423   gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
    424   int reorder_offset = (GetItemTileRectAt(0, 1).CenterPoint() -
    425                         GetItemTileRectAt(0, 0).CenterPoint()).Length() -
    426                        kReorderDroppingCircleRadius -
    427                        kPreferredIconDimension / 2 + 5;
    428   gfx::Point to = gfx::Point(from.x() - reorder_offset, from.y());
    429 
    430   // Dragging item_1 closing to item_0 should leads to re-ordering these two
    431   // items.
    432   SimulateDrag(AppsGridView::MOUSE, from, to);
    433   apps_grid_view_->EndDrag(false);
    434   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    435   EXPECT_EQ(std::string("Item 1,Item 0"), model_->GetModelContent());
    436   test_api_->LayoutToIdealBounds();
    437 }
    438 
    439 TEST_F(AppsGridViewTest, MouseDragFolderReorder) {
    440   EnsureFoldersEnabled();
    441 
    442   size_t kTotalItems = 2;
    443   model_->CreateAndPopulateFolderWithApps(kTotalItems);
    444   model_->PopulateAppWithId(kTotalItems);
    445   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    446   EXPECT_EQ(AppListFolderItem::kItemType,
    447             model_->top_level_item_list()->item_at(0)->GetItemType());
    448   AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
    449       model_->top_level_item_list()->item_at(0));
    450   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id());
    451 
    452   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    453   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    454 
    455   // Dragging folder over item_1 should leads to re-ordering these two
    456   // items.
    457   SimulateDrag(AppsGridView::MOUSE, from, to);
    458   apps_grid_view_->EndDrag(false);
    459   EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
    460   EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
    461   EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
    462   test_api_->LayoutToIdealBounds();
    463 }
    464 
    465 TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
    466   size_t kTotalItems = 4;
    467   model_->PopulateApps(kTotalItems);
    468   EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
    469   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    470             model_->GetModelContent());
    471 
    472   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    473   gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
    474 
    475   // Canceling drag should keep existing order.
    476   SimulateDrag(AppsGridView::MOUSE, from, to);
    477   apps_grid_view_->EndDrag(true);
    478   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    479             model_->GetModelContent());
    480   test_api_->LayoutToIdealBounds();
    481 
    482   // Deleting an item keeps remaining intact.
    483   SimulateDrag(AppsGridView::MOUSE, from, to);
    484   model_->DeleteItem(model_->GetItemName(2));
    485   apps_grid_view_->EndDrag(false);
    486   EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
    487   test_api_->LayoutToIdealBounds();
    488 
    489   // Adding a launcher item cancels the drag and respects the order.
    490   SimulateDrag(AppsGridView::MOUSE, from, to);
    491   model_->CreateAndAddItem("Extra");
    492   apps_grid_view_->EndDrag(false);
    493   EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
    494             model_->GetModelContent());
    495   test_api_->LayoutToIdealBounds();
    496 }
    497 
    498 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
    499   test_api_->SetPageFlipDelay(10);
    500   GetPaginationModel()->SetTransitionDurations(10, 10);
    501 
    502   PageFlipWaiter page_flip_waiter(message_loop(), GetPaginationModel());
    503 
    504   const int kPages = 3;
    505   model_->PopulateApps(kPages * kTilesPerPage);
    506   EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
    507   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    508 
    509   gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
    510   gfx::Point to = gfx::Point(apps_grid_view_->width(),
    511                              apps_grid_view_->height() / 2);
    512 
    513   // Drag to right edge.
    514   page_flip_waiter.Reset();
    515   SimulateDrag(AppsGridView::MOUSE, from, to);
    516 
    517   // Page should be flipped after sometime to hit page 1 and 2 then stop.
    518   while (test_api_->HasPendingPageFlip()) {
    519     page_flip_waiter.Wait();
    520   }
    521   EXPECT_EQ("1,2", page_flip_waiter.selected_pages());
    522   EXPECT_EQ(2, GetPaginationModel()->selected_page());
    523 
    524   apps_grid_view_->EndDrag(true);
    525 
    526   // Now drag to the left edge and test the other direction.
    527   to.set_x(0);
    528 
    529   page_flip_waiter.Reset();
    530   SimulateDrag(AppsGridView::MOUSE, from, to);
    531 
    532   while (test_api_->HasPendingPageFlip()) {
    533     page_flip_waiter.Wait();
    534   }
    535   EXPECT_EQ("1,0", page_flip_waiter.selected_pages());
    536   EXPECT_EQ(0, GetPaginationModel()->selected_page());
    537 
    538   apps_grid_view_->EndDrag(true);
    539 }
    540 
    541 TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) {
    542   model_->SetFoldersEnabled(false);
    543   const int kTotalItems = 4;
    544   model_->PopulateApps(kTotalItems);
    545   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    546             model_->GetModelContent());
    547 
    548   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
    549   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
    550 
    551   gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
    552   gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
    553 
    554   // Starts a mouse drag first then a touch drag.
    555   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    556   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
    557   // Finishes the drag and mouse drag wins.
    558   apps_grid_view_->EndDrag(false);
    559   EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
    560             model_->GetModelContent());
    561   test_api_->LayoutToIdealBounds();
    562 
    563   // Starts a touch drag first then a mouse drag.
    564   SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
    565   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    566   // Finishes the drag and touch drag wins.
    567   apps_grid_view_->EndDrag(false);
    568   EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
    569             model_->GetModelContent());
    570   test_api_->LayoutToIdealBounds();
    571 }
    572 
    573 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
    574   EnsureFoldersEnabled();
    575 
    576   const int kTotalItems = 4;
    577   TestAppsGridViewFolderDelegate folder_delegate;
    578   apps_grid_view_->set_folder_delegate(&folder_delegate);
    579   model_->PopulateApps(kTotalItems);
    580   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    581             model_->GetModelContent());
    582 
    583   gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
    584   gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
    585 
    586   // Starts a mouse drag and then cancels it.
    587   SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
    588   EXPECT_TRUE(folder_delegate.show_bubble());
    589   apps_grid_view_->EndDrag(true);
    590   EXPECT_FALSE(folder_delegate.show_bubble());
    591   EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
    592             model_->GetModelContent());
    593 }
    594 
    595 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
    596   const int kPages = 3;
    597   const int kItems = (kPages - 1) * kTilesPerPage + 1;
    598   model_->PopulateApps(kItems);
    599 
    600   const int first_index = 0;
    601   const int last_index = kItems - 1;
    602   const int last_index_on_page1_first_row = kRows - 1;
    603   const int last_index_on_page1 = kTilesPerPage - 1;
    604   const int first_index_on_page2 = kTilesPerPage;
    605   const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
    606   const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
    607 
    608   // Try moving off the item beyond the first one.
    609   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
    610   SimulateKeyPress(ui::VKEY_UP);
    611   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
    612   SimulateKeyPress(ui::VKEY_LEFT);
    613   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
    614 
    615   // Move to the last item and try to go past it.
    616   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
    617   SimulateKeyPress(ui::VKEY_DOWN);
    618   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
    619   SimulateKeyPress(ui::VKEY_RIGHT);
    620   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
    621 
    622   // Move right on last item on page 1 should get to first item on page 2's last
    623   // row and vice versa.
    624   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
    625   SimulateKeyPress(ui::VKEY_RIGHT);
    626   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    627       first_index_on_page2_last_row)));
    628   SimulateKeyPress(ui::VKEY_LEFT);
    629   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    630       last_index_on_page1)));
    631 
    632   // Up/down on page boundary does nothing.
    633   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
    634   SimulateKeyPress(ui::VKEY_DOWN);
    635   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    636       last_index_on_page1)));
    637   apps_grid_view_->SetSelectedView(
    638       GetItemViewAt(first_index_on_page2_last_row));
    639   apps_grid_view_->
    640       SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
    641   SimulateKeyPress(ui::VKEY_UP);
    642   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    643       last_index_on_page1_first_row)));
    644 
    645   // Page up and down should go to the same item on the next and last page.
    646   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
    647   SimulateKeyPress(ui::VKEY_PRIOR);
    648   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    649       first_index)));
    650   SimulateKeyPress(ui::VKEY_NEXT);
    651   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    652       first_index_on_page2)));
    653 
    654   // Moving onto a a page with too few apps to support the expected index snaps
    655   // to the last available index.
    656   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
    657   SimulateKeyPress(ui::VKEY_RIGHT);
    658   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    659       last_index)));
    660   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
    661   SimulateKeyPress(ui::VKEY_NEXT);
    662   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    663       last_index)));
    664 
    665 
    666 
    667   // After page switch, arrow keys select first item on current page.
    668   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
    669   GetPaginationModel()->SelectPage(1, false);
    670   SimulateKeyPress(ui::VKEY_UP);
    671   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
    672       first_index_on_page2)));
    673 }
    674 
    675 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
    676   // If the app's full name and short name differ, the title label's tooltip
    677   // should always be the full name of the app.
    678   std::string expected_text("xyz");
    679   std::string expected_tooltip("tooltip");
    680   AppListItem* item = model_->CreateAndAddItem("Item with short name");
    681   model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);
    682 
    683   base::string16 actual_tooltip;
    684   AppListItemView* item_view = GetItemViewAt(0);
    685   ASSERT_TRUE(item_view);
    686   const views::Label* title_label = item_view->title();
    687   EXPECT_TRUE(title_label->GetTooltipText(
    688       title_label->bounds().CenterPoint(), &actual_tooltip));
    689   EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
    690   EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
    691 }
    692 
    693 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
    694   // If the app's full name and short name are the same, use the default tooltip
    695   // behavior of the label (only show a tooltip if the title is truncated).
    696   std::string title("a");
    697   AppListItem* item = model_->CreateAndAddItem(title);
    698   model_->SetItemNameAndShortName(item, title, "");
    699 
    700   base::string16 actual_tooltip;
    701   AppListItemView* item_view = GetItemViewAt(0);
    702   ASSERT_TRUE(item_view);
    703   const views::Label* title_label = item_view->title();
    704   EXPECT_FALSE(title_label->GetTooltipText(
    705       title_label->bounds().CenterPoint(), &actual_tooltip));
    706   EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
    707 }
    708 
    709 }  // namespace test
    710 }  // namespace app_list
    711