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 "ash/test/ash_test_base.h"
6
7#include <string>
8#include <vector>
9
10#include "ash/ash_switches.h"
11#include "ash/display/display_controller.h"
12#include "ash/shell.h"
13#include "ash/shell/toplevel_window.h"
14#include "ash/test/ash_test_helper.h"
15#include "ash/test/display_manager_test_api.h"
16#include "ash/test/test_session_state_delegate.h"
17#include "ash/test/test_shell_delegate.h"
18#include "ash/test/test_system_tray_delegate.h"
19#include "ash/wm/window_positioner.h"
20#include "base/command_line.h"
21#include "ui/aura/client/aura_constants.h"
22#include "ui/aura/client/screen_position_client.h"
23#include "ui/aura/client/window_tree_client.h"
24#include "ui/aura/test/event_generator_delegate_aura.h"
25#include "ui/aura/test/test_window_delegate.h"
26#include "ui/aura/window.h"
27#include "ui/aura/window_delegate.h"
28#include "ui/aura/window_tree_host.h"
29#include "ui/base/ime/input_method_initializer.h"
30#include "ui/events/gestures/gesture_configuration.h"
31#include "ui/gfx/display.h"
32#include "ui/gfx/point.h"
33#include "ui/gfx/screen.h"
34#include "ui/wm/core/coordinate_conversion.h"
35
36#if defined(OS_CHROMEOS)
37#include "ash/system/chromeos/tray_display.h"
38#endif
39
40#if defined(OS_WIN)
41#include "ash/test/test_metro_viewer_process_host.h"
42#include "base/win/metro.h"
43#include "base/win/windows_version.h"
44#include "ui/aura/remote_window_tree_host_win.h"
45#include "ui/aura/window_tree_host_win.h"
46#include "ui/platform_window/win/win_window.h"
47#include "win8/test/test_registrar_constants.h"
48#endif
49
50#if defined(USE_X11)
51#include "ui/gfx/x/x11_connection.h"
52#endif
53
54namespace ash {
55namespace test {
56namespace {
57
58class AshEventGeneratorDelegate
59    : public aura::test::EventGeneratorDelegateAura {
60 public:
61  AshEventGeneratorDelegate() {}
62  virtual ~AshEventGeneratorDelegate() {}
63
64  // aura::test::EventGeneratorDelegateAura overrides:
65  virtual aura::WindowTreeHost* GetHostAt(
66      const gfx::Point& point_in_screen) const OVERRIDE {
67    gfx::Screen* screen = Shell::GetScreen();
68    gfx::Display display = screen->GetDisplayNearestPoint(point_in_screen);
69    return Shell::GetInstance()->display_controller()->
70        GetRootWindowForDisplayId(display.id())->GetHost();
71  }
72
73  virtual aura::client::ScreenPositionClient* GetScreenPositionClient(
74      const aura::Window* window) const OVERRIDE {
75    return aura::client::GetScreenPositionClient(window->GetRootWindow());
76  }
77
78 private:
79  DISALLOW_COPY_AND_ASSIGN(AshEventGeneratorDelegate);
80};
81
82}  // namespace
83
84/////////////////////////////////////////////////////////////////////////////
85
86AshTestBase::AshTestBase()
87    : setup_called_(false),
88      teardown_called_(false),
89      start_session_(true) {
90#if defined(USE_X11)
91  // This is needed for tests which use this base class but are run in browser
92  // test binaries so don't get the default initialization in the unit test
93  // suite.
94  gfx::InitializeThreadedX11();
95#endif
96
97  thread_bundle_.reset(new content::TestBrowserThreadBundle);
98  // Must initialize |ash_test_helper_| here because some tests rely on
99  // AshTestBase methods before they call AshTestBase::SetUp().
100  ash_test_helper_.reset(new AshTestHelper(base::MessageLoopForUI::current()));
101}
102
103AshTestBase::~AshTestBase() {
104  CHECK(setup_called_)
105      << "You have overridden SetUp but never called AshTestBase::SetUp";
106  CHECK(teardown_called_)
107      << "You have overridden TearDown but never called AshTestBase::TearDown";
108}
109
110void AshTestBase::SetUp() {
111  setup_called_ = true;
112
113  // Clears the saved state so that test doesn't use on the wrong
114  // default state.
115  shell::ToplevelWindow::ClearSavedStateForTest();
116
117  // TODO(jamescook): Can we do this without changing command line?
118  // Use the origin (1,1) so that it doesn't over
119  // lap with the native mouse cursor.
120  CommandLine* command_line = CommandLine::ForCurrentProcess();
121  if (!command_line->HasSwitch(switches::kAshHostWindowBounds)) {
122    command_line->AppendSwitchASCII(
123        switches::kAshHostWindowBounds, "1+1-800x600");
124  }
125#if defined(OS_WIN)
126  ui::test::SetUsePopupAsRootWindowForTest(true);
127#endif
128  ash_test_helper_->SetUp(start_session_);
129
130  Shell::GetPrimaryRootWindow()->Show();
131  Shell::GetPrimaryRootWindow()->GetHost()->Show();
132  // Move the mouse cursor to far away so that native events doesn't
133  // interfere test expectations.
134  Shell::GetPrimaryRootWindow()->MoveCursorTo(gfx::Point(-1000, -1000));
135  ash::Shell::GetInstance()->cursor_manager()->EnableMouseEvents();
136
137  // Changing GestureConfiguration shouldn't make tests fail.
138  ui::GestureConfiguration::set_max_touch_move_in_pixels_for_click(5);
139
140#if defined(OS_WIN)
141  if (!command_line->HasSwitch(ash::switches::kForceAshToDesktop)) {
142    if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
143      ipc_thread_.reset(new base::Thread("test_metro_viewer_ipc_thread"));
144      base::Thread::Options options;
145      options.message_loop_type = base::MessageLoop::TYPE_IO;
146      ipc_thread_->StartWithOptions(options);
147      metro_viewer_host_.reset(
148          new TestMetroViewerProcessHost(ipc_thread_->message_loop_proxy()));
149      CHECK(metro_viewer_host_->LaunchViewerAndWaitForConnection(
150          win8::test::kDefaultTestAppUserModelId));
151      aura::RemoteWindowTreeHostWin* window_tree_host =
152          aura::RemoteWindowTreeHostWin::Instance();
153      CHECK(window_tree_host != NULL);
154    }
155    ash::WindowPositioner::SetMaximizeFirstWindow(true);
156  }
157#endif
158}
159
160void AshTestBase::TearDown() {
161  teardown_called_ = true;
162  // Flush the message loop to finish pending release tasks.
163  RunAllPendingInMessageLoop();
164
165#if defined(OS_WIN)
166  if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
167      !CommandLine::ForCurrentProcess()->HasSwitch(
168          ash::switches::kForceAshToDesktop)) {
169    // Check that our viewer connection is still established.
170    CHECK(!metro_viewer_host_->closed_unexpectedly());
171  }
172#endif
173
174  ash_test_helper_->TearDown();
175#if defined(OS_WIN)
176  ui::test::SetUsePopupAsRootWindowForTest(false);
177  // Kill the viewer process if we spun one up.
178  if (metro_viewer_host_) {
179    metro_viewer_host_->TerminateViewer();
180    metro_viewer_host_.reset();
181  }
182#endif
183
184  event_generator_.reset();
185  // Some tests set an internal display id,
186  // reset it here, so other tests will continue in a clean environment.
187  gfx::Display::SetInternalDisplayId(gfx::Display::kInvalidDisplayID);
188}
189
190ui::test::EventGenerator& AshTestBase::GetEventGenerator() {
191  if (!event_generator_) {
192    event_generator_.reset(
193        new ui::test::EventGenerator(new AshEventGeneratorDelegate()));
194  }
195  return *event_generator_.get();
196}
197
198bool AshTestBase::SupportsMultipleDisplays() {
199  return AshTestHelper::SupportsMultipleDisplays();
200}
201
202bool AshTestBase::SupportsHostWindowResize() {
203  return AshTestHelper::SupportsHostWindowResize();
204}
205
206void AshTestBase::UpdateDisplay(const std::string& display_specs) {
207  DisplayManagerTestApi display_manager_test_api(
208      Shell::GetInstance()->display_manager());
209  display_manager_test_api.UpdateDisplay(display_specs);
210}
211
212aura::Window* AshTestBase::CurrentContext() {
213  return ash_test_helper_->CurrentContext();
214}
215
216aura::Window* AshTestBase::CreateTestWindowInShellWithId(int id) {
217  return CreateTestWindowInShellWithDelegate(NULL, id, gfx::Rect());
218}
219
220aura::Window* AshTestBase::CreateTestWindowInShellWithBounds(
221    const gfx::Rect& bounds) {
222  return CreateTestWindowInShellWithDelegate(NULL, 0, bounds);
223}
224
225aura::Window* AshTestBase::CreateTestWindowInShell(SkColor color,
226                                                   int id,
227                                                   const gfx::Rect& bounds) {
228  return CreateTestWindowInShellWithDelegate(
229      new aura::test::ColorTestWindowDelegate(color), id, bounds);
230}
231
232aura::Window* AshTestBase::CreateTestWindowInShellWithDelegate(
233    aura::WindowDelegate* delegate,
234    int id,
235    const gfx::Rect& bounds) {
236  return CreateTestWindowInShellWithDelegateAndType(
237      delegate, ui::wm::WINDOW_TYPE_NORMAL, id, bounds);
238}
239
240aura::Window* AshTestBase::CreateTestWindowInShellWithDelegateAndType(
241    aura::WindowDelegate* delegate,
242    ui::wm::WindowType type,
243    int id,
244    const gfx::Rect& bounds) {
245  aura::Window* window = new aura::Window(delegate);
246  window->set_id(id);
247  window->SetType(type);
248  window->Init(aura::WINDOW_LAYER_TEXTURED);
249  window->Show();
250
251  if (bounds.IsEmpty()) {
252    ParentWindowInPrimaryRootWindow(window);
253  } else {
254    gfx::Display display =
255        Shell::GetScreen()->GetDisplayMatching(bounds);
256    aura::Window* root = ash::Shell::GetInstance()->display_controller()->
257        GetRootWindowForDisplayId(display.id());
258    gfx::Point origin = bounds.origin();
259    ::wm::ConvertPointFromScreen(root, &origin);
260    window->SetBounds(gfx::Rect(origin, bounds.size()));
261    aura::client::ParentWindowWithContext(window, root, bounds);
262  }
263  window->SetProperty(aura::client::kCanMaximizeKey, true);
264  return window;
265}
266
267void AshTestBase::ParentWindowInPrimaryRootWindow(aura::Window* window) {
268  aura::client::ParentWindowWithContext(
269      window, Shell::GetPrimaryRootWindow(), gfx::Rect());
270}
271
272void AshTestBase::RunAllPendingInMessageLoop() {
273  ash_test_helper_->RunAllPendingInMessageLoop();
274}
275
276TestScreenshotDelegate* AshTestBase::GetScreenshotDelegate() {
277  return ash_test_helper_->test_screenshot_delegate();
278}
279
280TestSystemTrayDelegate* AshTestBase::GetSystemTrayDelegate() {
281  return static_cast<TestSystemTrayDelegate*>(
282      Shell::GetInstance()->system_tray_delegate());
283}
284
285void AshTestBase::SetSessionStarted(bool session_started) {
286  ash_test_helper_->test_shell_delegate()->test_session_state_delegate()->
287      SetActiveUserSessionStarted(session_started);
288}
289
290void AshTestBase::SetUserLoggedIn(bool user_logged_in) {
291  ash_test_helper_->test_shell_delegate()->test_session_state_delegate()->
292      SetHasActiveUser(user_logged_in);
293}
294
295void AshTestBase::SetCanLockScreen(bool can_lock_screen) {
296  ash_test_helper_->test_shell_delegate()->test_session_state_delegate()->
297      SetCanLockScreen(can_lock_screen);
298}
299
300void AshTestBase::SetShouldLockScreenBeforeSuspending(bool should_lock) {
301  ash_test_helper_->test_shell_delegate()->test_session_state_delegate()->
302      SetShouldLockScreenBeforeSuspending(should_lock);
303}
304
305void AshTestBase::SetUserAddingScreenRunning(bool user_adding_screen_running) {
306  ash_test_helper_->test_shell_delegate()->test_session_state_delegate()->
307      SetUserAddingScreenRunning(user_adding_screen_running);
308}
309
310void AshTestBase::BlockUserSession(UserSessionBlockReason block_reason) {
311  switch (block_reason) {
312    case BLOCKED_BY_LOCK_SCREEN:
313      SetSessionStarted(true);
314      SetUserAddingScreenRunning(false);
315      Shell::GetInstance()->session_state_delegate()->LockScreen();
316      break;
317    case BLOCKED_BY_LOGIN_SCREEN:
318      SetUserAddingScreenRunning(false);
319      SetSessionStarted(false);
320      break;
321    case BLOCKED_BY_USER_ADDING_SCREEN:
322      SetUserAddingScreenRunning(true);
323      SetSessionStarted(true);
324      break;
325    default:
326      NOTREACHED();
327      break;
328  }
329}
330
331void AshTestBase::UnblockUserSession() {
332  Shell::GetInstance()->session_state_delegate()->UnlockScreen();
333  SetSessionStarted(true);
334  SetUserAddingScreenRunning(false);
335}
336
337
338}  // namespace test
339}  // namespace ash
340