1// Copyright 2013 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 "ash/shell.h"
6#include "ash/shell_window_ids.h"
7#include "ash/test/ash_test_base.h"
8#include "ash/test/test_shell_delegate.h"
9#include "ash/wm/window_util.h"
10#include "base/command_line.h"
11#include "base/memory/scoped_ptr.h"
12#include "ui/app_list/app_list_switches.h"
13#include "ui/app_list/views/app_list_view.h"
14#include "ui/aura/test/test_windows.h"
15#include "ui/aura/window.h"
16#include "ui/events/test/event_generator.h"
17
18namespace ash {
19
20namespace {
21
22const int kMinimalCenteredAppListMargin = 10;
23
24}
25
26// The parameter is true to test the centered app list, false for normal.
27// (The test name ends in "/0" for normal, "/1" for centered.)
28class AppListControllerTest : public test::AshTestBase,
29                              public ::testing::WithParamInterface<bool> {
30 public:
31  AppListControllerTest();
32  virtual ~AppListControllerTest();
33  virtual void SetUp() OVERRIDE;
34
35  bool IsCentered() const;
36};
37
38AppListControllerTest::AppListControllerTest() {
39}
40
41AppListControllerTest::~AppListControllerTest() {
42}
43
44void AppListControllerTest::SetUp() {
45  AshTestBase::SetUp();
46  if (IsCentered()) {
47    CommandLine* command_line = CommandLine::ForCurrentProcess();
48    command_line->AppendSwitch(app_list::switches::kEnableCenteredAppList);
49  }
50}
51
52bool AppListControllerTest::IsCentered() const {
53  return GetParam();
54}
55
56// Tests that app launcher hides when focus moves to a normal window.
57TEST_P(AppListControllerTest, HideOnFocusOut) {
58  Shell::GetInstance()->ShowAppList(NULL);
59  EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
60
61  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
62  wm::ActivateWindow(window.get());
63
64  EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility());
65}
66
67// Tests that app launcher remains visible when focus is moved to a different
68// window in kShellWindowId_AppListContainer.
69TEST_P(AppListControllerTest, RemainVisibleWhenFocusingToApplistContainer) {
70  Shell::GetInstance()->ShowAppList(NULL);
71  EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
72
73  aura::Window* applist_container = Shell::GetContainer(
74      Shell::GetPrimaryRootWindow(), kShellWindowId_AppListContainer);
75  scoped_ptr<aura::Window> window(
76      aura::test::CreateTestWindowWithId(0, applist_container));
77  wm::ActivateWindow(window.get());
78
79  EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
80}
81
82// Tests that clicking outside the app-list bubble closes it.
83TEST_P(AppListControllerTest, ClickOutsideBubbleClosesBubble) {
84  Shell* shell = Shell::GetInstance();
85  shell->ShowAppList(NULL);
86
87  aura::Window* app_window = shell->GetAppListWindow();
88  ASSERT_TRUE(app_window);
89  ui::test::EventGenerator generator(shell->GetPrimaryRootWindow(), app_window);
90  // Click on the bubble itself. The bubble should remain visible.
91  generator.ClickLeftButton();
92  EXPECT_TRUE(shell->GetAppListTargetVisibility());
93
94  // Click outside the bubble. This should close it.
95  gfx::Rect app_window_bounds = app_window->GetBoundsInRootWindow();
96  gfx::Point point_outside =
97      gfx::Point(app_window_bounds.right(), app_window_bounds.y()) +
98      gfx::Vector2d(10, 0);
99  EXPECT_TRUE(shell->GetPrimaryRootWindow()->bounds().Contains(point_outside));
100  generator.MoveMouseToInHost(point_outside);
101  generator.ClickLeftButton();
102  EXPECT_FALSE(shell->GetAppListTargetVisibility());
103}
104
105// Tests that clicking outside the app-list bubble closes it.
106TEST_P(AppListControllerTest, TapOutsideBubbleClosesBubble) {
107  Shell* shell = Shell::GetInstance();
108  shell->ShowAppList(NULL);
109
110  aura::Window* app_window = shell->GetAppListWindow();
111  ASSERT_TRUE(app_window);
112  gfx::Rect app_window_bounds = app_window->GetBoundsInRootWindow();
113
114  ui::test::EventGenerator generator(shell->GetPrimaryRootWindow());
115  // Click on the bubble itself. The bubble should remain visible.
116  generator.GestureTapAt(app_window_bounds.CenterPoint());
117  EXPECT_TRUE(shell->GetAppListTargetVisibility());
118
119  // Click outside the bubble. This should close it.
120  gfx::Point point_outside =
121      gfx::Point(app_window_bounds.right(), app_window_bounds.y()) +
122      gfx::Vector2d(10, 0);
123  EXPECT_TRUE(shell->GetPrimaryRootWindow()->bounds().Contains(point_outside));
124  generator.GestureTapAt(point_outside);
125  EXPECT_FALSE(shell->GetAppListTargetVisibility());
126}
127
128// Tests opening the app launcher on a non-primary display, then deleting the
129// display.
130TEST_P(AppListControllerTest, NonPrimaryDisplay) {
131  if (!SupportsMultipleDisplays())
132    return;
133
134  // Set up a screen with two displays (horizontally adjacent).
135  UpdateDisplay("800x600,800x600");
136
137  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
138  ASSERT_EQ(2u, root_windows.size());
139  aura::Window* secondary_window = root_windows[1];
140  EXPECT_EQ("800,0 800x600", secondary_window->GetBoundsInScreen().ToString());
141
142  Shell::GetInstance()->ShowAppList(secondary_window);
143  EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
144
145  // Remove the secondary display. Shouldn't crash (http://crbug.com/368990).
146  UpdateDisplay("800x600");
147
148  // Updating the displays should close the app list.
149  EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility());
150}
151
152// Tests opening the app launcher on a tiny display that is too small to contain
153// it.
154TEST_P(AppListControllerTest, TinyDisplay) {
155  // Don't test this for the non-centered app list case; it isn't designed for
156  // small displays. The most common case of a small display --- when the
157  // virtual keyboard is open --- switches into the centered app list mode, so
158  // we just want to run this test in that case.
159  if (!IsCentered())
160    return;
161
162  // UpdateDisplay is not supported in this case, so just skip the test.
163  if (!SupportsHostWindowResize())
164    return;
165
166  // Set up a screen with a tiny display (height smaller than the app list).
167  UpdateDisplay("400x300");
168
169  Shell::GetInstance()->ShowAppList(NULL);
170  EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
171
172  // The top of the app list should be on-screen (even if the bottom is not).
173  // We need to manually calculate the Y coordinate of the top of the app list
174  // from the anchor (center) and height. There isn't a bounds rect that gives
175  // the actual app list position (the widget bounds include the bubble border
176  // which is much bigger than the actual app list size).
177  app_list::AppListView* app_list = Shell::GetInstance()->GetAppListView();
178  int app_list_view_top =
179      app_list->anchor_rect().y() - app_list->bounds().height() / 2;
180  EXPECT_GE(app_list_view_top, kMinimalCenteredAppListMargin);
181}
182
183INSTANTIATE_TEST_CASE_P(AppListControllerTestInstance,
184                        AppListControllerTest,
185                        ::testing::Bool());
186
187}  // namespace ash
188