root/ui/app_list/views/apps_grid_view_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. page_changed_
  2. Wait
  3. OnWaitTimeOut
  4. TotalPagesChanged
  5. SelectedPageChanged
  6. TransitionStarted
  7. TransitionChanged
  8. SetUp
  9. TearDown
  10. EnsureFoldersEnabled
  11. GetItemViewAt
  12. GetItemViewForPoint
  13. GetItemTileRectAt
  14. SimulateDrag
  15. SimulateKeyPress
  16. UpdateFolderViewBackground
  17. ReparentItem
  18. DispatchDragEventForReparent
  19. DispatchEndDragEventForReparent
  20. IsPointOutsideOfFolderBoundary
  21. IsOEMFolder
  22. show_bubble
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F
  27. TEST_F
  28. TEST_F
  29. TEST_F
  30. TEST_F
  31. TEST_F
  32. TEST_F
  33. TEST_F
  34. TEST_F
  35. TEST_F
  36. TEST_F
  37. TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/app_list/views/apps_grid_view.h"

#include <string>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/timer/timer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/app_list/app_list_folder_item.h"
#include "ui/app_list/app_list_item.h"
#include "ui/app_list/app_list_model.h"
#include "ui/app_list/app_list_switches.h"
#include "ui/app_list/pagination_model.h"
#include "ui/app_list/test/app_list_test_model.h"
#include "ui/app_list/views/app_list_item_view.h"
#include "ui/app_list/views/apps_grid_view_folder_delegate.h"
#include "ui/app_list/views/test/apps_grid_view_test_api.h"
#include "ui/views/test/views_test_base.h"

namespace app_list {
namespace test {

namespace {

const int kIconDimension = 48;
const int kCols = 2;
const int kRows = 2;
const int kTilesPerPage = kCols * kRows;

const int kWidth = 320;
const int kHeight = 240;

class PageFlipWaiter : public PaginationModelObserver {
 public:
  PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
      : ui_loop_(ui_loop), model_(model), wait_(false), page_changed_(false) {
    model_->AddObserver(this);
  }

  virtual ~PageFlipWaiter() {
    model_->RemoveObserver(this);
  }

  bool Wait(int time_out_ms) {
    DCHECK(!wait_);
    wait_ = true;
    page_changed_ = false;

    if (time_out_ms) {
      wait_timer_.Stop();
      wait_timer_.Start(FROM_HERE,
                        base::TimeDelta::FromMilliseconds(time_out_ms),
                        this, &PageFlipWaiter::OnWaitTimeOut);
    }

    ui_loop_->Run();
    wait_ = false;
    return page_changed_;
  }

 private:
  void OnWaitTimeOut() {
    ui_loop_->Quit();
  }

  // PaginationModelObserver overrides:
  virtual void TotalPagesChanged() OVERRIDE {
  }
  virtual void SelectedPageChanged(int old_selected,
                                   int new_selected) OVERRIDE {
    page_changed_ = true;
    if (wait_)
      ui_loop_->Quit();
  }
  virtual void TransitionStarted() OVERRIDE {
  }
  virtual void TransitionChanged() OVERRIDE {
  }

  base::MessageLoopForUI* ui_loop_;
  PaginationModel* model_;
  bool wait_;
  bool page_changed_;
  base::OneShotTimer<PageFlipWaiter> wait_timer_;

  DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
};

}  // namespace

class AppsGridViewTest : public views::ViewsTestBase {
 public:
  AppsGridViewTest() {}
  virtual ~AppsGridViewTest() {}

  // testing::Test overrides:
  virtual void SetUp() OVERRIDE {
    views::ViewsTestBase::SetUp();
    model_.reset(new AppListTestModel);
    pagination_model_.reset(new PaginationModel);

    apps_grid_view_.reset(new AppsGridView(NULL, pagination_model_.get()));
    apps_grid_view_->SetLayout(kIconDimension, kCols, kRows);
    apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
    apps_grid_view_->SetModel(model_.get());
    apps_grid_view_->SetItemList(model_->top_level_item_list());

    test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
  }
  virtual void TearDown() OVERRIDE {
    apps_grid_view_.reset();  // Release apps grid view before models.
    views::ViewsTestBase::TearDown();
  }

 protected:
  void EnsureFoldersEnabled() {
    // Folders require AppList sync to be enabled.
    CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableSyncAppList);
  }

  AppListItemView* GetItemViewAt(int index) {
    return static_cast<AppListItemView*>(
        test_api_->GetViewAtModelIndex(index));
  }

  AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
    for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
      AppListItemView* view = GetItemViewAt(i);
      if (view->bounds().Contains(point))
        return view;
    }
    return NULL;
  }

  gfx::Rect GetItemTileRectAt(int row, int col) {
    DCHECK_GT(model_->top_level_item_list()->item_count(), 0u);

    gfx::Insets insets(apps_grid_view_->GetInsets());
    gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
                   GetItemViewAt(0)->bounds().size());
    rect.Offset(col * rect.width(), row * rect.height());
    return rect;
  }

  // Points are in |apps_grid_view_|'s coordinates.
  void SimulateDrag(AppsGridView::Pointer pointer,
                    const gfx::Point& from,
                    const gfx::Point& to) {
    AppListItemView* view = GetItemViewForPoint(from);
    DCHECK(view);

    gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
        from - view->bounds().origin());
    gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
        to - view->bounds().origin());

    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
                                 translated_from, from, 0, 0);
    apps_grid_view_->InitiateDrag(view, pointer, pressed_event);

    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
                              translated_to, to, 0, 0);
    apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
  }

  void SimulateKeyPress(ui::KeyboardCode key_code) {
    ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false);
    apps_grid_view_->OnKeyPressed(key_event);
  }

  scoped_ptr<AppListTestModel> model_;
  scoped_ptr<PaginationModel> pagination_model_;
  scoped_ptr<AppsGridView> apps_grid_view_;
  scoped_ptr<AppsGridViewTestApi> test_api_;

 private:
  DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
};

class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
 public:
  TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
  virtual ~TestAppsGridViewFolderDelegate() {}

  // Overridden from AppsGridViewFolderDelegate:
  virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE {
    show_bubble_ = show_bubble;
  }

  virtual void ReparentItem(AppListItemView* original_drag_view,
                            const gfx::Point& drag_point_in_folder_grid)
      OVERRIDE {}

  virtual void DispatchDragEventForReparent(
      AppsGridView::Pointer pointer,
      const gfx::Point& drag_point_in_folder_grid) OVERRIDE {}

  virtual void DispatchEndDragEventForReparent(
      bool events_forwarded_to_drag_drop_host) OVERRIDE {}

  virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point)
      OVERRIDE {
    return false;
  }

  virtual bool IsOEMFolder() const OVERRIDE { return false; }

  bool show_bubble() { return show_bubble_; }

 private:
  bool show_bubble_;

  DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
};

TEST_F(AppsGridViewTest, CreatePage) {
  // Fully populates a page.
  const int kPages = 1;
  model_->PopulateApps(kPages * kTilesPerPage);
  EXPECT_EQ(kPages, pagination_model_->total_pages());

  // Adds one more and gets a new page created.
  model_->CreateAndAddItem("Extra");
  EXPECT_EQ(kPages + 1, pagination_model_->total_pages());
}

TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
  const int kPages = 3;
  model_->PopulateApps(kPages * kTilesPerPage);
  EXPECT_EQ(kPages, pagination_model_->total_pages());
  EXPECT_EQ(0, pagination_model_->selected_page());

  // Highlight first one and last one one first page and first page should be
  // selected.
  model_->HighlightItemAt(0);
  EXPECT_EQ(0, pagination_model_->selected_page());
  model_->HighlightItemAt(kTilesPerPage - 1);
  EXPECT_EQ(0, pagination_model_->selected_page());

  // Highlight first one on 2nd page and 2nd page should be selected.
  model_->HighlightItemAt(kTilesPerPage + 1);
  EXPECT_EQ(1, pagination_model_->selected_page());

  // Highlight last one in the model and last page should be selected.
  model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1);
  EXPECT_EQ(kPages - 1, pagination_model_->selected_page());
}

TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
  const int kTotalItems = 2;
  const int kLastItemIndex = kTotalItems - 1;

  model_->PopulateApps(kTotalItems);

  AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
  apps_grid_view_->SetSelectedView(last_view);
  model_->DeleteItem(model_->GetItemName(kLastItemIndex));

  EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));

  // No crash happens.
  AppListItemView* view = GetItemViewAt(0);
  apps_grid_view_->SetSelectedView(view);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
}

TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) {
  CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableSyncAppList);
  const int kTotalItems = 4;
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());

  gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();

  // Dragging changes model order.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Canceling drag should keep existing order.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(true);
  EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Deleting an item keeps remaining intact.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  model_->DeleteItem(model_->GetItemName(0));
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Adding a launcher item cancels the drag and respects the order.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  model_->CreateAndAddItem("Extra");
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) {
  EnsureFoldersEnabled();

  size_t kTotalItems = 3;
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());

  gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
  gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();

  // Dragging item_1 over item_0 creates a folder.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
  EXPECT_EQ(AppListFolderItem::kItemType,
            model_->top_level_item_list()->item_at(0)->GetItemType());
  AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
      model_->top_level_item_list()->item_at(0));
  EXPECT_EQ(2u, folder_item->ChildItemCount());
  AppListItem* item_0 = model_->FindItem("Item 0");
  EXPECT_TRUE(item_0->IsInFolder());
  EXPECT_EQ(folder_item->id(), item_0->folder_id());
  AppListItem* item_1 = model_->FindItem("Item 1");
  EXPECT_TRUE(item_1->IsInFolder());
  EXPECT_EQ(folder_item->id(), item_1->folder_id());
  std::string expected_items = folder_item->id() + ",Item 2";
  EXPECT_EQ(expected_items, model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Dragging item_2 to the folder adds item_2 to the folder.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);

  EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
  EXPECT_EQ(folder_item->id(), model_->GetModelContent());
  EXPECT_EQ(3u, folder_item->ChildItemCount());
  item_0 = model_->FindItem("Item 0");
  EXPECT_TRUE(item_0->IsInFolder());
  EXPECT_EQ(folder_item->id(), item_0->folder_id());
  item_1 = model_->FindItem("Item 1");
  EXPECT_TRUE(item_1->IsInFolder());
  EXPECT_EQ(folder_item->id(), item_1->folder_id());
  AppListItem* item_2 = model_->FindItem("Item 2");
  EXPECT_TRUE(item_2->IsInFolder());
  EXPECT_EQ(folder_item->id(), item_2->folder_id());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) {
  EnsureFoldersEnabled();

  // Create and add a folder with 15 items in it.
  size_t kTotalItems = kMaxFolderItems - 1;
  model_->CreateAndPopulateFolderWithApps(kTotalItems);
  EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(AppListFolderItem::kItemType,
            model_->top_level_item_list()->item_at(0)->GetItemType());
  AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
      model_->top_level_item_list()->item_at(0));
  EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());

  // Create and add another 2 items.
  model_->PopulateAppWithId(kTotalItems);
  model_->PopulateAppWithId(kTotalItems + 1);
  EXPECT_EQ(3u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
  EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1),
            model_->top_level_item_list()->item_at(1)->id());
  EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
            model_->top_level_item_list()->item_at(2)->id());

  gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
  gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();

  // Dragging one item into the folder, the folder should accept the item.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
  EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
  EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
            model_->top_level_item_list()->item_at(1)->id());
  test_api_->LayoutToIdealBounds();

  // Dragging the last item over the folder, the folder won't accept the new
  // item, instead, it will re-order the items.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
            model_->top_level_item_list()->item_at(0)->id());
  EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
  EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragItemReorder) {
  // This test assumes Folders are enabled.
  EnsureFoldersEnabled();

  size_t kTotalItems = 2;
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(std::string("Item 0,Item 1"), model_->GetModelContent());

  gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
  int reorder_offset = (GetItemTileRectAt(0, 1).CenterPoint() -
                        GetItemTileRectAt(0, 0).CenterPoint()).Length() -
                       kReorderDroppingCircleRadius -
                       kPreferredIconDimension / 2 + 5;
  gfx::Point to = gfx::Point(from.x() - reorder_offset, from.y());

  // Dragging item_1 closing to item_0 should leads to re-ordering these two
  // items.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(std::string("Item 1,Item 0"), model_->GetModelContent());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragFolderReorder) {
  EnsureFoldersEnabled();

  size_t kTotalItems = 2;
  model_->CreateAndPopulateFolderWithApps(kTotalItems);
  model_->PopulateAppWithId(kTotalItems);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ(AppListFolderItem::kItemType,
            model_->top_level_item_list()->item_at(0)->GetItemType());
  AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
      model_->top_level_item_list()->item_at(0));
  EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id());

  gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();

  // Dragging folder over item_1 should leads to re-ordering these two
  // items.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
  EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
  EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
  size_t kTotalItems = 4;
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());

  gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();

  // Canceling drag should keep existing order.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  apps_grid_view_->EndDrag(true);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Deleting an item keeps remaining intact.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  model_->DeleteItem(model_->GetItemName(2));
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Adding a launcher item cancels the drag and respects the order.
  SimulateDrag(AppsGridView::MOUSE, from, to);
  model_->CreateAndAddItem("Extra");
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, MouseDragFlipPage) {
  test_api_->SetPageFlipDelay(10);
  pagination_model_->SetTransitionDurations(10, 10);

  PageFlipWaiter page_flip_waiter(message_loop(),
                                  pagination_model_.get());

  const int kPages = 3;
  model_->PopulateApps(kPages * kTilesPerPage);
  EXPECT_EQ(kPages, pagination_model_->total_pages());
  EXPECT_EQ(0, pagination_model_->selected_page());

  gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point to = gfx::Point(apps_grid_view_->width(),
                             apps_grid_view_->height() / 2);

  // Drag to right edge.
  SimulateDrag(AppsGridView::MOUSE, from, to);

  // Page should be flipped after sometime.
  EXPECT_TRUE(page_flip_waiter.Wait(0));
  EXPECT_EQ(1, pagination_model_->selected_page());

  // Stay there and page should be flipped again.
  EXPECT_TRUE(page_flip_waiter.Wait(0));
  EXPECT_EQ(2, pagination_model_->selected_page());

  // Stay there longer and no page flip happen since we are at the last page.
  EXPECT_FALSE(page_flip_waiter.Wait(100));
  EXPECT_EQ(2, pagination_model_->selected_page());

  apps_grid_view_->EndDrag(true);

  // Now drag to the left edge and test the other direction.
  to.set_x(0);

  SimulateDrag(AppsGridView::MOUSE, from, to);

  EXPECT_TRUE(page_flip_waiter.Wait(0));
  EXPECT_EQ(1, pagination_model_->selected_page());

  EXPECT_TRUE(page_flip_waiter.Wait(0));
  EXPECT_EQ(0, pagination_model_->selected_page());

  EXPECT_FALSE(page_flip_waiter.Wait(100));
  EXPECT_EQ(0, pagination_model_->selected_page());
  apps_grid_view_->EndDrag(true);
}

TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) {
  CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableSyncAppList);
  const int kTotalItems = 4;
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());

  gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();

  gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
  gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();

  // Starts a mouse drag first then a touch drag.
  SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
  SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
  // Finishes the drag and mouse drag wins.
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();

  // Starts a touch drag first then a mouse drag.
  SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
  SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
  // Finishes the drag and touch drag wins.
  apps_grid_view_->EndDrag(false);
  EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
            model_->GetModelContent());
  test_api_->LayoutToIdealBounds();
}

TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
  EnsureFoldersEnabled();

  const int kTotalItems = 4;
  TestAppsGridViewFolderDelegate folder_delegate;
  apps_grid_view_->set_folder_delegate(&folder_delegate);
  model_->PopulateApps(kTotalItems);
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());

  gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
  gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();

  // Starts a mouse drag and then cancels it.
  SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
  EXPECT_TRUE(folder_delegate.show_bubble());
  apps_grid_view_->EndDrag(true);
  EXPECT_FALSE(folder_delegate.show_bubble());
  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
            model_->GetModelContent());
}

TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
  const int kPages = 3;
  const int kItems = (kPages - 1) * kTilesPerPage + 1;
  model_->PopulateApps(kItems);

  const int first_index = 0;
  const int last_index = kItems - 1;
  const int last_index_on_page1_first_row = kRows - 1;
  const int last_index_on_page1 = kTilesPerPage - 1;
  const int first_index_on_page2 = kTilesPerPage;
  const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
  const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;

  // Try moving off the item beyond the first one.
  apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
  SimulateKeyPress(ui::VKEY_UP);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
  SimulateKeyPress(ui::VKEY_LEFT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));

  // Move to the last item and try to go past it.
  apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
  SimulateKeyPress(ui::VKEY_DOWN);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
  SimulateKeyPress(ui::VKEY_RIGHT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));

  // Move right on last item on page 1 should get to first item on page 2's last
  // row and vice versa.
  apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
  SimulateKeyPress(ui::VKEY_RIGHT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      first_index_on_page2_last_row)));
  SimulateKeyPress(ui::VKEY_LEFT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      last_index_on_page1)));

  // Up/down on page boundary does nothing.
  apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
  SimulateKeyPress(ui::VKEY_DOWN);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      last_index_on_page1)));
  apps_grid_view_->SetSelectedView(
      GetItemViewAt(first_index_on_page2_last_row));
  apps_grid_view_->
      SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
  SimulateKeyPress(ui::VKEY_UP);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      last_index_on_page1_first_row)));

  // Page up and down should go to the same item on the next and last page.
  apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
  SimulateKeyPress(ui::VKEY_PRIOR);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      first_index)));
  SimulateKeyPress(ui::VKEY_NEXT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      first_index_on_page2)));

  // Moving onto a a page with too few apps to support the expected index snaps
  // to the last available index.
  apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
  SimulateKeyPress(ui::VKEY_RIGHT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      last_index)));
  apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
  SimulateKeyPress(ui::VKEY_NEXT);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      last_index)));



  // After page switch, arrow keys select first item on current page.
  apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
  pagination_model_->SelectPage(1, false);
  SimulateKeyPress(ui::VKEY_UP);
  EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
      first_index_on_page2)));
}

TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
  // If the app's full name and short name differ, the title label's tooltip
  // should always be the full name of the app.
  std::string expected_text("xyz");
  std::string expected_tooltip("tooltip");
  AppListItem* item = model_->CreateAndAddItem("Item with short name");
  model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);

  base::string16 actual_tooltip;
  AppListItemView* item_view = GetItemViewAt(0);
  ASSERT_TRUE(item_view);
  const views::Label* title_label = item_view->title();
  EXPECT_TRUE(title_label->GetTooltipText(
      title_label->bounds().CenterPoint(), &actual_tooltip));
  EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
  EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
}

TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
  // If the app's full name and short name are the same, use the default tooltip
  // behavior of the label (only show a tooltip if the title is truncated).
  std::string title("a");
  AppListItem* item = model_->CreateAndAddItem(title);
  model_->SetItemNameAndShortName(item, title, "");

  base::string16 actual_tooltip;
  AppListItemView* item_view = GetItemViewAt(0);
  ASSERT_TRUE(item_view);
  const views::Label* title_label = item_view->title();
  EXPECT_FALSE(title_label->GetTooltipText(
      title_label->bounds().CenterPoint(), &actual_tooltip));
  EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
}

}  // namespace test
}  // namespace app_list

/* [<][>][^][v][top][bottom][index][help] */