1// Copyright (c) 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/touch/touch_observer_hud.h"
6
7#include "ash/ash_switches.h"
8#include "ash/display/display_manager.h"
9#include "ash/root_window_controller.h"
10#include "ash/screen_util.h"
11#include "ash/shell.h"
12#include "ash/test/ash_test_base.h"
13#include "ash/test/display_manager_test_api.h"
14#include "ash/touch/touch_hud_debug.h"
15#include "ash/touch/touch_hud_projection.h"
16#include "base/command_line.h"
17#include "base/format_macros.h"
18#include "base/strings/stringprintf.h"
19#include "ui/aura/window.h"
20#include "ui/views/widget/widget.h"
21
22namespace ash {
23
24class TouchHudTestBase : public test::AshTestBase {
25 public:
26  TouchHudTestBase() {}
27  virtual ~TouchHudTestBase() {}
28
29  virtual void SetUp() OVERRIDE {
30    test::AshTestBase::SetUp();
31
32    // Initialize display infos. They should be initialized after Ash
33    // environment is set up, i.e., after test::AshTestBase::SetUp().
34    internal_display_id_ = test::DisplayManagerTestApi(GetDisplayManager()).
35        SetFirstDisplayAsInternalDisplay();
36    external_display_id_ = 10;
37    mirrored_display_id_ = 11;
38
39    internal_display_info_ =
40        CreateDisplayInfo(internal_display_id_, gfx::Rect(0, 0, 500, 500));
41    external_display_info_ =
42        CreateDisplayInfo(external_display_id_, gfx::Rect(1, 1, 100, 100));
43    mirrored_display_info_ =
44        CreateDisplayInfo(mirrored_display_id_, gfx::Rect(0, 0, 100, 100));
45  }
46
47  gfx::Display GetPrimaryDisplay() {
48    return Shell::GetScreen()->GetPrimaryDisplay();
49  }
50
51  const gfx::Display& GetSecondaryDisplay() {
52    return ScreenUtil::GetSecondaryDisplay();
53  }
54
55  void SetupSingleDisplay() {
56    display_info_list_.clear();
57    display_info_list_.push_back(internal_display_info_);
58    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
59  }
60
61  void SetupDualDisplays() {
62    display_info_list_.clear();
63    display_info_list_.push_back(internal_display_info_);
64    display_info_list_.push_back(external_display_info_);
65    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
66  }
67
68  void SetInternalAsPrimary() {
69    const gfx::Display& internal_display =
70        GetDisplayManager()->GetDisplayForId(internal_display_id_);
71    GetDisplayController()->SetPrimaryDisplay(internal_display);
72  }
73
74  void SetExternalAsPrimary() {
75    const gfx::Display& external_display =
76        GetDisplayManager()->GetDisplayForId(external_display_id_);
77    GetDisplayController()->SetPrimaryDisplay(external_display);
78  }
79
80  void MirrorDisplays() {
81    DCHECK_EQ(2U, display_info_list_.size());
82    DCHECK_EQ(internal_display_id_, display_info_list_[0].id());
83    DCHECK_EQ(external_display_id_, display_info_list_[1].id());
84    display_info_list_[1] = mirrored_display_info_;
85    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
86  }
87
88  void UnmirrorDisplays() {
89    DCHECK_EQ(2U, display_info_list_.size());
90    DCHECK_EQ(internal_display_id_, display_info_list_[0].id());
91    DCHECK_EQ(mirrored_display_id_, display_info_list_[1].id());
92    display_info_list_[1] = external_display_info_;
93    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
94  }
95
96  void RemoveInternalDisplay() {
97    DCHECK_LT(0U, display_info_list_.size());
98    DCHECK_EQ(internal_display_id_, display_info_list_[0].id());
99    display_info_list_.erase(display_info_list_.begin());
100    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
101  }
102
103  void RemoveExternalDisplay() {
104    DCHECK_EQ(2U, display_info_list_.size());
105    display_info_list_.pop_back();
106    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
107  }
108
109  void AddInternalDisplay() {
110    DCHECK_EQ(0U, display_info_list_.size());
111    display_info_list_.push_back(internal_display_info_);
112    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
113  }
114
115  void AddExternalDisplay() {
116    DCHECK_EQ(1U, display_info_list_.size());
117    display_info_list_.push_back(external_display_info_);
118    GetDisplayManager()->OnNativeDisplaysChanged(display_info_list_);
119  }
120
121  int64 internal_display_id() const {
122    return internal_display_id_;
123  }
124
125  int64 external_display_id() const {
126    return external_display_id_;
127  }
128
129 protected:
130  DisplayManager* GetDisplayManager() {
131    return Shell::GetInstance()->display_manager();
132  }
133
134  DisplayController* GetDisplayController() {
135    return Shell::GetInstance()->display_controller();
136  }
137
138  const gfx::Display& GetInternalDisplay() {
139    return GetDisplayManager()->GetDisplayForId(internal_display_id_);
140  }
141
142  const gfx::Display& GetExternalDisplay() {
143    return GetDisplayManager()->GetDisplayForId(external_display_id_);
144  }
145
146  aura::Window* GetInternalRootWindow() {
147    return GetDisplayController()->GetRootWindowForDisplayId(
148        internal_display_id_);
149  }
150
151  aura::Window* GetExternalRootWindow() {
152    return GetDisplayController()->GetRootWindowForDisplayId(
153        external_display_id_);
154  }
155
156  aura::Window* GetPrimaryRootWindow() {
157    const gfx::Display& display = GetPrimaryDisplay();
158    return GetDisplayController()->GetRootWindowForDisplayId(display.id());
159  }
160
161  aura::Window* GetSecondaryRootWindow() {
162    const gfx::Display& display = GetSecondaryDisplay();
163    return GetDisplayController()->GetRootWindowForDisplayId(display.id());
164  }
165
166  RootWindowController* GetInternalRootController() {
167    aura::Window* root = GetInternalRootWindow();
168    return GetRootWindowController(root);
169  }
170
171  RootWindowController* GetExternalRootController() {
172    aura::Window* root = GetExternalRootWindow();
173    return GetRootWindowController(root);
174  }
175
176  RootWindowController* GetPrimaryRootController() {
177    aura::Window* root = GetPrimaryRootWindow();
178    return GetRootWindowController(root);
179  }
180
181  RootWindowController* GetSecondaryRootController() {
182    aura::Window* root = GetSecondaryRootWindow();
183    return GetRootWindowController(root);
184  }
185
186  DisplayInfo CreateDisplayInfo(int64 id, const gfx::Rect& bounds) {
187    DisplayInfo info(id, base::StringPrintf("x-%" PRId64, id), false);
188    info.SetBounds(bounds);
189    return info;
190  }
191
192  aura::Window* GetRootWindowForTouchHud(TouchObserverHUD* hud) {
193    return hud->root_window_;
194  }
195
196  views::Widget* GetWidgetForTouchHud(TouchObserverHUD* hud) {
197    return hud->widget_;
198  }
199
200  int64 internal_display_id_;
201  int64 external_display_id_;
202  int64 mirrored_display_id_;
203  DisplayInfo internal_display_info_;
204  DisplayInfo external_display_info_;
205  DisplayInfo mirrored_display_info_;
206
207  std::vector<DisplayInfo> display_info_list_;
208
209  DISALLOW_COPY_AND_ASSIGN(TouchHudTestBase);
210};
211
212class TouchHudDebugTest : public TouchHudTestBase {
213 public:
214  TouchHudDebugTest() {}
215  virtual ~TouchHudDebugTest() {}
216
217  virtual void SetUp() OVERRIDE {
218    // Add ash-touch-hud flag to enable debug touch HUD. This flag should be set
219    // before Ash environment is set up, i.e., before TouchHudTestBase::SetUp().
220    CommandLine::ForCurrentProcess()->AppendSwitch(
221        ash::switches::kAshTouchHud);
222
223    TouchHudTestBase::SetUp();
224  }
225
226  void CheckInternalDisplay() {
227    EXPECT_NE(static_cast<TouchObserverHUD*>(NULL), GetInternalTouchHudDebug());
228    EXPECT_EQ(internal_display_id(), GetInternalTouchHudDebug()->display_id());
229    EXPECT_EQ(GetInternalRootWindow(),
230              GetRootWindowForTouchHud(GetInternalTouchHudDebug()));
231    EXPECT_EQ(GetInternalRootWindow(),
232              GetWidgetForTouchHud(GetInternalTouchHudDebug())->
233                  GetNativeView()->GetRootWindow());
234    EXPECT_EQ(GetInternalDisplay().size(),
235              GetWidgetForTouchHud(GetInternalTouchHudDebug())->
236                  GetWindowBoundsInScreen().size());
237  }
238
239  void CheckExternalDisplay() {
240    EXPECT_NE(static_cast<TouchHudDebug*>(NULL), GetExternalTouchHudDebug());
241    EXPECT_EQ(external_display_id(), GetExternalTouchHudDebug()->display_id());
242    EXPECT_EQ(GetExternalRootWindow(),
243              GetRootWindowForTouchHud(GetExternalTouchHudDebug()));
244    EXPECT_EQ(GetExternalRootWindow(),
245              GetWidgetForTouchHud(GetExternalTouchHudDebug())->
246                  GetNativeView()->GetRootWindow());
247    EXPECT_EQ(GetExternalDisplay().size(),
248              GetWidgetForTouchHud(GetExternalTouchHudDebug())->
249                  GetWindowBoundsInScreen().size());
250  }
251
252 private:
253  TouchHudDebug* GetInternalTouchHudDebug() {
254    return GetInternalRootController()->touch_hud_debug();
255  }
256
257  TouchHudDebug* GetExternalTouchHudDebug() {
258    return GetExternalRootController()->touch_hud_debug();
259  }
260
261  TouchHudDebug* GetPrimaryTouchHudDebug() {
262    return GetPrimaryRootController()->touch_hud_debug();
263  }
264
265  TouchHudDebug* GetSecondaryTouchHudDebug() {
266    return GetSecondaryRootController()->touch_hud_debug();
267  }
268
269  DISALLOW_COPY_AND_ASSIGN(TouchHudDebugTest);
270};
271
272class TouchHudProjectionTest : public TouchHudTestBase {
273 public:
274  TouchHudProjectionTest() {}
275  virtual ~TouchHudProjectionTest() {}
276
277  void EnableTouchHudProjection() {
278    Shell::GetInstance()->SetTouchHudProjectionEnabled(true);
279  }
280
281  void DisableTouchHudProjection() {
282    Shell::GetInstance()->SetTouchHudProjectionEnabled(false);
283  }
284
285  TouchHudProjection* GetInternalTouchHudProjection() {
286    return GetInternalRootController()->touch_hud_projection();
287  }
288
289  int GetInternalTouchPointsCount() {
290    return GetInternalTouchHudProjection()->points_.size();
291  }
292
293  void SendTouchEventToInternalHud(ui::EventType type,
294                                   const gfx::Point& location,
295                                   int touch_id) {
296    ui::TouchEvent event(type, location, touch_id, event_time);
297    GetInternalTouchHudProjection()->OnTouchEvent(&event);
298
299    // Advance time for next event.
300    event_time += base::TimeDelta::FromMilliseconds(100);
301  }
302
303 private:
304  base::TimeDelta event_time;
305
306  DISALLOW_COPY_AND_ASSIGN(TouchHudProjectionTest);
307};
308
309// Checks if debug touch HUD is correctly initialized for a single display.
310TEST_F(TouchHudDebugTest, SingleDisplay) {
311  // Setup a single display setting.
312  SetupSingleDisplay();
313
314  // Check if touch HUD is set correctly and associated with appropriate
315  // display.
316  CheckInternalDisplay();
317}
318
319// Checks if debug touch HUDs are correctly initialized for two displays.
320TEST_F(TouchHudDebugTest, DualDisplays) {
321  if (!SupportsMultipleDisplays())
322    return;
323
324  // Setup a dual display setting.
325  SetupDualDisplays();
326
327  // Check if touch HUDs are set correctly and associated with appropriate
328  // displays.
329  CheckInternalDisplay();
330  CheckExternalDisplay();
331}
332
333// Checks if debug touch HUDs are correctly handled when primary display is
334// changed.
335TEST_F(TouchHudDebugTest, SwapPrimaryDisplay) {
336  if (!SupportsMultipleDisplays())
337    return;
338
339  // Setup a dual display setting.
340  SetupDualDisplays();
341
342  // Set the primary display to the external one.
343  SetExternalAsPrimary();
344
345  // Check if displays' touch HUDs are not swapped as root windows are.
346  EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id());
347  EXPECT_EQ(internal_display_id(), GetSecondaryDisplay().id());
348  CheckInternalDisplay();
349  CheckExternalDisplay();
350
351  // Set the primary display back to the internal one.
352  SetInternalAsPrimary();
353
354  // Check if displays' touch HUDs are not swapped back as root windows are.
355  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
356  EXPECT_EQ(external_display_id(), GetSecondaryDisplay().id());
357  CheckInternalDisplay();
358  CheckExternalDisplay();
359}
360
361// Checks if debug touch HUDs are correctly handled when displays are mirrored.
362TEST_F(TouchHudDebugTest, MirrorDisplays) {
363  if (!SupportsMultipleDisplays())
364    return;
365
366  // Setup a dual display setting.
367  SetupDualDisplays();
368
369  // Mirror displays.
370  MirrorDisplays();
371
372  // Check if the internal display is intact.
373  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
374  CheckInternalDisplay();
375
376  // Unmirror displays.
377  UnmirrorDisplays();
378
379  // Check if external display is added back correctly.
380  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
381  EXPECT_EQ(external_display_id(), GetSecondaryDisplay().id());
382  CheckInternalDisplay();
383  CheckExternalDisplay();
384}
385
386// Checks if debug touch HUDs are correctly handled when displays are mirrored
387// after setting the external display as the primary one.
388TEST_F(TouchHudDebugTest, SwapPrimaryThenMirrorDisplays) {
389  if (!SupportsMultipleDisplays())
390    return;
391
392  // Setup a dual display setting.
393  SetupDualDisplays();
394
395  // Set the primary display to the external one.
396  SetExternalAsPrimary();
397
398  // Mirror displays.
399  MirrorDisplays();
400
401  // Check if the internal display is set as the primary one.
402  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
403  CheckInternalDisplay();
404
405  // Unmirror displays.
406  UnmirrorDisplays();
407
408  // Check if the external display is added back as the primary display and
409  // touch HUDs are set correctly.
410  EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id());
411  EXPECT_EQ(internal_display_id(), GetSecondaryDisplay().id());
412  CheckInternalDisplay();
413  CheckExternalDisplay();
414}
415
416// Checks if debug touch HUDs are correctly handled when the external display,
417// which is the secondary one, is removed.
418TEST_F(TouchHudDebugTest, RemoveSecondaryDisplay) {
419  if (!SupportsMultipleDisplays())
420    return;
421
422  // Setup a dual display setting.
423  SetupDualDisplays();
424
425  // Remove external display which is the secondary one.
426  RemoveExternalDisplay();
427
428  // Check if the internal display is intact.
429  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
430  CheckInternalDisplay();
431
432  // Add external display back.
433  AddExternalDisplay();
434
435  // Check if displays' touch HUDs are set correctly.
436  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
437  EXPECT_EQ(external_display_id(), GetSecondaryDisplay().id());
438  CheckInternalDisplay();
439  CheckExternalDisplay();
440}
441
442// Checks if debug touch HUDs are correctly handled when the external display,
443// which is set as the primary display, is removed.
444TEST_F(TouchHudDebugTest, RemovePrimaryDisplay) {
445  if (!SupportsMultipleDisplays())
446    return;
447
448  // Setup a dual display setting.
449  SetupDualDisplays();
450
451  // Set the primary display to the external one.
452  SetExternalAsPrimary();
453
454  // Remove the external display which is the primary display.
455  RemoveExternalDisplay();
456
457  // Check if the internal display is set as the primary one.
458  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
459  CheckInternalDisplay();
460
461  // Add the external display back.
462  AddExternalDisplay();
463
464  // Check if the external display is set as primary and touch HUDs are set
465  // correctly.
466  EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id());
467  EXPECT_EQ(internal_display_id(), GetSecondaryDisplay().id());
468  CheckInternalDisplay();
469  CheckExternalDisplay();
470}
471
472// Checks if debug touch HUDs are correctly handled when all displays are
473// removed.
474TEST_F(TouchHudDebugTest, Headless) {
475  if (!SupportsMultipleDisplays())
476    return;
477
478  // Setup a single display setting.
479  SetupSingleDisplay();
480
481  // Remove the only display which is the internal one.
482  RemoveInternalDisplay();
483
484  // Add the internal display back.
485  AddInternalDisplay();
486
487  // Check if the display's touch HUD is set correctly.
488  EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id());
489  CheckInternalDisplay();
490}
491
492// Checks projection touch HUD with a sequence of touch-pressed, touch-moved,
493// and touch-released events.
494TEST_F(TouchHudProjectionTest, TouchMoveRelease) {
495  SetupSingleDisplay();
496  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
497
498  EnableTouchHudProjection();
499  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
500            GetInternalTouchHudProjection());
501  EXPECT_EQ(0, GetInternalTouchPointsCount());
502
503  SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
504  EXPECT_EQ(1, GetInternalTouchPointsCount());
505
506  SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1);
507  EXPECT_EQ(1, GetInternalTouchPointsCount());
508
509  SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 1);
510  EXPECT_EQ(0, GetInternalTouchPointsCount());
511
512  // Disabling projection touch HUD shoud remove it without crashing.
513  DisableTouchHudProjection();
514  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
515}
516
517// Checks projection touch HUD with a sequence of touch-pressed, touch-moved,
518// and touch-cancelled events.
519TEST_F(TouchHudProjectionTest, TouchMoveCancel) {
520  SetupSingleDisplay();
521  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
522
523  EnableTouchHudProjection();
524  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
525            GetInternalTouchHudProjection());
526  EXPECT_EQ(0, GetInternalTouchPointsCount());
527
528  SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
529  EXPECT_EQ(1, GetInternalTouchPointsCount());
530
531  SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1);
532  EXPECT_EQ(1, GetInternalTouchPointsCount());
533
534  SendTouchEventToInternalHud(ui::ET_TOUCH_CANCELLED, gfx::Point(10, 20), 1);
535  EXPECT_EQ(0, GetInternalTouchPointsCount());
536
537  // Disabling projection touch HUD shoud remove it without crashing.
538  DisableTouchHudProjection();
539  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
540}
541
542// Checks projection touch HUD with two simultaneous touches.
543TEST_F(TouchHudProjectionTest, DoubleTouch) {
544  SetupSingleDisplay();
545  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
546
547  EnableTouchHudProjection();
548  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
549            GetInternalTouchHudProjection());
550  EXPECT_EQ(0, GetInternalTouchPointsCount());
551
552  SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
553  EXPECT_EQ(1, GetInternalTouchPointsCount());
554
555  SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(20, 10), 2);
556  EXPECT_EQ(2, GetInternalTouchPointsCount());
557
558  SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1);
559  EXPECT_EQ(2, GetInternalTouchPointsCount());
560
561  SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(20, 20), 2);
562  EXPECT_EQ(2, GetInternalTouchPointsCount());
563
564  SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 1);
565  EXPECT_EQ(1, GetInternalTouchPointsCount());
566
567  SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(20, 20), 2);
568  EXPECT_EQ(0, GetInternalTouchPointsCount());
569
570  // Disabling projection touch HUD shoud remove it without crashing.
571  DisableTouchHudProjection();
572  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
573}
574
575// Checks if turning off touch HUD projection while touching the screen is
576// handled correctly.
577TEST_F(TouchHudProjectionTest, DisableWhileTouching) {
578  SetupSingleDisplay();
579  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
580
581  EnableTouchHudProjection();
582  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
583            GetInternalTouchHudProjection());
584  EXPECT_EQ(0, GetInternalTouchPointsCount());
585
586  SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
587  EXPECT_EQ(1, GetInternalTouchPointsCount());
588
589  // Disabling projection touch HUD shoud remove it without crashing.
590  DisableTouchHudProjection();
591  EXPECT_EQ(NULL, GetInternalTouchHudProjection());
592}
593
594}  // namespace ash
595