// Copyright (c) 2013 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 "ash/touch/touch_observer_hud.h" #include "ash/constants/ash_switches.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/touch/touch_devices_controller.h" #include "ash/touch/touch_hud_debug.h" #include "ash/touch/touch_hud_projection.h" #include "ash/touch/touch_hud_renderer.h" #include "base/command_line.h" #include "base/format_macros.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "ui/aura/window.h" #include "ui/display/manager/display_manager.h" #include "ui/display/test/display_manager_test_api.h" #include "ui/views/widget/widget.h" namespace ash { class TouchHudTestBase : public AshTestBase { public: TouchHudTestBase() = default; TouchHudTestBase(const TouchHudTestBase&) = delete; TouchHudTestBase& operator=(const TouchHudTestBase&) = delete; ~TouchHudTestBase() override = default; void SetUp() override { AshTestBase::SetUp(); // Initialize display infos. They should be initialized after Ash // environment is set up, i.e., after AshTestBase::SetUp(). internal_display_id_ = display::test::DisplayManagerTestApi(Shell::Get()->display_manager()) .SetFirstDisplayAsInternalDisplay(); external_display_id_ = 10; mirrored_display_id_ = 11; internal_display_info_ = CreateDisplayInfo(internal_display_id_, gfx::Rect(0, 0, 600, 500)); external_display_info_ = CreateDisplayInfo(external_display_id_, gfx::Rect(1, 1, 200, 100)); mirrored_display_info_ = CreateDisplayInfo(mirrored_display_id_, gfx::Rect(0, 0, 200, 100)); } display::Display GetPrimaryDisplay() { return display::Screen::GetScreen()->GetPrimaryDisplay(); } void SetupSingleDisplay() { display_info_list_.clear(); display_info_list_.push_back(internal_display_info_); display_manager()->OnNativeDisplaysChanged(display_info_list_); } void SetupDualDisplays() { display_info_list_.clear(); display_info_list_.push_back(internal_display_info_); display_info_list_.push_back(external_display_info_); display_manager()->OnNativeDisplaysChanged(display_info_list_); } void SetInternalAsPrimary() { GetWindowTreeHostManager()->SetPrimaryDisplayId(internal_display_id_); } void SetExternalAsPrimary() { GetWindowTreeHostManager()->SetPrimaryDisplayId(external_display_id_); } void MirrorDisplays() { DCHECK_EQ(2U, display_info_list_.size()); DCHECK_EQ(internal_display_id_, display_info_list_[0].id()); DCHECK_EQ(external_display_id_, display_info_list_[1].id()); display_info_list_[1] = mirrored_display_info_; display_manager()->OnNativeDisplaysChanged(display_info_list_); } void UnmirrorDisplays() { DCHECK_EQ(2U, display_info_list_.size()); DCHECK_EQ(internal_display_id_, display_info_list_[0].id()); DCHECK_EQ(mirrored_display_id_, display_info_list_[1].id()); display_info_list_[1] = external_display_info_; display_manager()->OnNativeDisplaysChanged(display_info_list_); } void RemoveInternalDisplay() { DCHECK_LT(0U, display_info_list_.size()); DCHECK_EQ(internal_display_id_, display_info_list_[0].id()); display_info_list_.erase(display_info_list_.begin()); display_manager()->OnNativeDisplaysChanged(display_info_list_); } void RemoveExternalDisplay() { DCHECK_EQ(2U, display_info_list_.size()); display_info_list_.pop_back(); display_manager()->OnNativeDisplaysChanged(display_info_list_); } void AddInternalDisplay() { DCHECK_EQ(0U, display_info_list_.size()); display_info_list_.push_back(internal_display_info_); display_manager()->OnNativeDisplaysChanged(display_info_list_); } void AddExternalDisplay() { DCHECK_EQ(1U, display_info_list_.size()); display_info_list_.push_back(external_display_info_); display_manager()->OnNativeDisplaysChanged(display_info_list_); } int64_t internal_display_id() const { return internal_display_id_; } int64_t external_display_id() const { return external_display_id_; } protected: WindowTreeHostManager* GetWindowTreeHostManager() { return Shell::Get()->window_tree_host_manager(); } const display::Display& GetInternalDisplay() { return display_manager()->GetDisplayForId(internal_display_id_); } const display::Display& GetExternalDisplay() { return display_manager()->GetDisplayForId(external_display_id_); } aura::Window* GetInternalRootWindow() { return Shell::GetRootWindowForDisplayId(internal_display_id_); } aura::Window* GetExternalRootWindow() { return Shell::GetRootWindowForDisplayId(external_display_id_); } aura::Window* GetPrimaryRootWindow() { const display::Display& display = GetPrimaryDisplay(); return Shell::GetRootWindowForDisplayId(display.id()); } aura::Window* GetSecondaryRootWindow() { const display::Display& display = display::test::DisplayManagerTestApi(display_manager()) .GetSecondaryDisplay(); return Shell::GetRootWindowForDisplayId(display.id()); } RootWindowController* GetInternalRootController() { aura::Window* root = GetInternalRootWindow(); return RootWindowController::ForWindow(root); } RootWindowController* GetExternalRootController() { aura::Window* root = GetExternalRootWindow(); return RootWindowController::ForWindow(root); } RootWindowController* GetPrimaryRootController() { aura::Window* root = GetPrimaryRootWindow(); return RootWindowController::ForWindow(root); } RootWindowController* GetSecondaryRootController() { aura::Window* root = GetSecondaryRootWindow(); return RootWindowController::ForWindow(root); } display::ManagedDisplayInfo CreateDisplayInfo(int64_t id, const gfx::Rect& bounds) { display::ManagedDisplayInfo info(id, base::StringPrintf("x-%" PRId64, id), false); info.SetBounds(bounds); return info; } aura::Window* GetRootWindowForTouchHud(TouchObserverHud* hud) { return hud->root_window_; } views::Widget* GetWidgetForTouchHud(TouchObserverHud* hud) { return hud->widget_; } int64_t internal_display_id_; int64_t external_display_id_; int64_t mirrored_display_id_; display::ManagedDisplayInfo internal_display_info_; display::ManagedDisplayInfo external_display_info_; display::ManagedDisplayInfo mirrored_display_info_; std::vector display_info_list_; }; class TouchHudDebugTest : public TouchHudTestBase { public: TouchHudDebugTest() = default; TouchHudDebugTest(const TouchHudDebugTest&) = delete; TouchHudDebugTest& operator=(const TouchHudDebugTest&) = delete; ~TouchHudDebugTest() override = default; void SetUp() override { // Add ash-touch-hud flag to enable debug touch HUD. This flag should be set // before Ash environment is set up, i.e., before TouchHudTestBase::SetUp(). base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kAshTouchHud); TouchHudTestBase::SetUp(); } void CheckInternalDisplay() { EXPECT_NE(nullptr, GetInternalTouchHudDebug()); EXPECT_EQ(internal_display_id(), GetInternalTouchHudDebug()->display_id()); EXPECT_EQ(GetInternalRootWindow(), GetRootWindowForTouchHud(GetInternalTouchHudDebug())); EXPECT_EQ(GetInternalRootWindow(), GetWidgetForTouchHud(GetInternalTouchHudDebug()) ->GetNativeView() ->GetRootWindow()); EXPECT_EQ(GetInternalDisplay().size(), GetWidgetForTouchHud(GetInternalTouchHudDebug()) ->GetWindowBoundsInScreen() .size()); } void CheckExternalDisplay() { EXPECT_NE(nullptr, GetExternalTouchHudDebug()); EXPECT_EQ(external_display_id(), GetExternalTouchHudDebug()->display_id()); EXPECT_EQ(GetExternalRootWindow(), GetRootWindowForTouchHud(GetExternalTouchHudDebug())); EXPECT_EQ(GetExternalRootWindow(), GetWidgetForTouchHud(GetExternalTouchHudDebug()) ->GetNativeView() ->GetRootWindow()); EXPECT_EQ(GetExternalDisplay().size(), GetWidgetForTouchHud(GetExternalTouchHudDebug()) ->GetWindowBoundsInScreen() .size()); } private: TouchHudDebug* GetInternalTouchHudDebug() { return GetInternalRootController()->touch_hud_debug(); } TouchHudDebug* GetExternalTouchHudDebug() { return GetExternalRootController()->touch_hud_debug(); } TouchHudDebug* GetPrimaryTouchHudDebug() { return GetPrimaryRootController()->touch_hud_debug(); } TouchHudDebug* GetSecondaryTouchHudDebug() { return GetSecondaryRootController()->touch_hud_debug(); } }; class TouchHudProjectionTest : public TouchHudTestBase { public: TouchHudProjectionTest() = default; TouchHudProjectionTest(const TouchHudProjectionTest&) = delete; TouchHudProjectionTest& operator=(const TouchHudProjectionTest&) = delete; ~TouchHudProjectionTest() override = default; // testing::Test: void SetUp() override { base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kShowTaps); TouchHudTestBase::SetUp(); } TouchHudProjection* GetInternalTouchHudProjection() { return GetInternalRootController()->touch_hud_projection(); } int GetInternalTouchPointsCount() { return GetInternalTouchHudProjection()->touch_hud_renderer_->points_.size(); } void SendTouchEventToInternalHud(ui::EventType type, const gfx::Point& location, int touch_id) { ui::TouchEvent event( type, location, event_time, ui::PointerDetails(ui::EventPointerType::kTouch, touch_id)); GetInternalTouchHudProjection()->OnTouchEvent(&event); // Advance time for next event. event_time += base::Milliseconds(100); } private: base::TimeTicks event_time; }; // Checks if debug touch HUD is correctly initialized for a single display. TEST_F(TouchHudDebugTest, SingleDisplay) { // Setup a single display setting. SetupSingleDisplay(); // Check if touch HUD is set correctly and associated with appropriate // display. CheckInternalDisplay(); } // Checks if debug touch HUDs are correctly initialized for two displays. TEST_F(TouchHudDebugTest, DualDisplays) { // Setup a dual display setting. SetupDualDisplays(); // Check if touch HUDs are set correctly and associated with appropriate // displays. CheckInternalDisplay(); CheckExternalDisplay(); } // Checks if debug touch HUDs are correctly handled when primary display is // changed. TEST_F(TouchHudDebugTest, SwapPrimaryDisplay) { // Setup a dual display setting. SetupDualDisplays(); // Set the primary display to the external one. SetExternalAsPrimary(); display::test::DisplayManagerTestApi display_manager_test(display_manager()); // Check if displays' touch HUDs are not swapped as root windows are. EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(internal_display_id(), display_manager_test.GetSecondaryDisplay().id()); CheckInternalDisplay(); CheckExternalDisplay(); // Set the primary display back to the internal one. SetInternalAsPrimary(); // Check if displays' touch HUDs are not swapped back as root windows are. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(external_display_id(), display_manager_test.GetSecondaryDisplay().id()); CheckInternalDisplay(); CheckExternalDisplay(); } // Checks if debug touch HUDs are correctly handled when displays are mirrored. TEST_F(TouchHudDebugTest, MirrorDisplays) { // Disable restoring mirror mode to prevent interference from previous // display configuration. display_manager()->set_disable_restoring_mirror_mode_for_test(true); // Setup a dual display setting. SetupDualDisplays(); // Mirror displays. MirrorDisplays(); // Check if the internal display is intact. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); CheckInternalDisplay(); // Unmirror displays. UnmirrorDisplays(); // Check if external display is added back correctly. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(external_display_id(), display::test::DisplayManagerTestApi(display_manager()) .GetSecondaryDisplay() .id()); CheckInternalDisplay(); CheckExternalDisplay(); display_manager()->set_disable_restoring_mirror_mode_for_test(false); } // Checks if debug touch HUDs are correctly handled when displays are mirrored // after setting the external display as the primary one. TEST_F(TouchHudDebugTest, SwapPrimaryThenMirrorDisplays) { display_manager()->set_disable_restoring_mirror_mode_for_test(true); // Setup a dual display setting. SetupDualDisplays(); // Set the primary display to the external one. SetExternalAsPrimary(); // Mirror displays. MirrorDisplays(); // Check if the internal display is set as the primary one. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); CheckInternalDisplay(); // Unmirror displays. UnmirrorDisplays(); // Check if the external display is added back as the primary display and // touch HUDs are set correctly. EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(internal_display_id(), display::test::DisplayManagerTestApi(display_manager()) .GetSecondaryDisplay() .id()); CheckInternalDisplay(); CheckExternalDisplay(); display_manager()->set_disable_restoring_mirror_mode_for_test(false); } // Checks if debug touch HUDs are correctly handled when the external display, // which is the secondary one, is removed. TEST_F(TouchHudDebugTest, RemoveSecondaryDisplay) { // Setup a dual display setting. SetupDualDisplays(); // Remove external display which is the secondary one. RemoveExternalDisplay(); // Check if the internal display is intact. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); CheckInternalDisplay(); // Add external display back. AddExternalDisplay(); // Check if displays' touch HUDs are set correctly. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(external_display_id(), display::test::DisplayManagerTestApi(display_manager()) .GetSecondaryDisplay() .id()); CheckInternalDisplay(); CheckExternalDisplay(); } // Checks if debug touch HUDs are correctly handled when the external display, // which is set as the primary display, is removed. TEST_F(TouchHudDebugTest, RemovePrimaryDisplay) { // Setup a dual display setting. SetupDualDisplays(); // Set the primary display to the external one. SetExternalAsPrimary(); // Remove the external display which is the primary display. RemoveExternalDisplay(); // Check if the internal display is set as the primary one. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); CheckInternalDisplay(); // Add the external display back. AddExternalDisplay(); // Check if the external display is set as primary and touch HUDs are set // correctly. EXPECT_EQ(external_display_id(), GetPrimaryDisplay().id()); EXPECT_EQ(internal_display_id(), display::test::DisplayManagerTestApi(display_manager()) .GetSecondaryDisplay() .id()); CheckInternalDisplay(); CheckExternalDisplay(); } // Checks if debug touch HUDs are correctly handled when all displays are // removed. TEST_F(TouchHudDebugTest, Headless) { // Setup a single display setting. SetupSingleDisplay(); // Remove the only display which is the internal one. RemoveInternalDisplay(); // Add the internal display back. AddInternalDisplay(); // Check if the display's touch HUD is set correctly. EXPECT_EQ(internal_display_id(), GetPrimaryDisplay().id()); CheckInternalDisplay(); } // Checks projection touch HUD with a sequence of touch-pressed, touch-moved, // and touch-released events. // Test if the WM sets correct work area under different density. TEST_F(TouchHudProjectionTest, TouchMoveRelease) { SetupSingleDisplay(); EXPECT_NE(nullptr, GetInternalTouchHudProjection()); EXPECT_EQ(0, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 1); EXPECT_EQ(0, GetInternalTouchPointsCount()); } // Checks projection touch HUD with a sequence of touch-pressed, touch-moved, // and touch-cancelled events. TEST_F(TouchHudProjectionTest, TouchMoveCancel) { SetupSingleDisplay(); EXPECT_NE(nullptr, GetInternalTouchHudProjection()); EXPECT_EQ(0, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_CANCELLED, gfx::Point(10, 20), 1); EXPECT_EQ(0, GetInternalTouchPointsCount()); } // Checks projection touch HUD with two simultaneous touches. TEST_F(TouchHudProjectionTest, DoubleTouch) { SetupSingleDisplay(); EXPECT_NE(nullptr, GetInternalTouchHudProjection()); EXPECT_EQ(0, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(20, 10), 2); EXPECT_EQ(2, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(10, 20), 1); EXPECT_EQ(2, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_MOVED, gfx::Point(20, 20), 2); EXPECT_EQ(2, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), 1); EXPECT_EQ(1, GetInternalTouchPointsCount()); SendTouchEventToInternalHud(ui::ET_TOUCH_RELEASED, gfx::Point(20, 20), 2); EXPECT_EQ(0, GetInternalTouchPointsCount()); } } // namespace ash