| // Copyright 2016 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/system/power/power_button_controller.h" |
| |
| #include "ash/accessibility/test_accessibility_controller_client.h" |
| #include "ash/display/screen_orientation_controller.h" |
| #include "ash/display/screen_orientation_controller_test_api.h" |
| #include "ash/media/media_controller_impl.h" |
| #include "ash/public/cpp/ash_switches.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/system/power/power_button_controller_test_api.h" |
| #include "ash/system/power/power_button_menu_item_view.h" |
| #include "ash/system/power/power_button_menu_view.h" |
| #include "ash/system/power/power_button_test_base.h" |
| #include "ash/test_media_client.h" |
| #include "ash/touch/touch_devices_controller.h" |
| #include "ash/wm/lock_state_controller_test_api.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller.h" |
| #include "ash/wm/test_session_state_animator.h" |
| #include "ash/wm/window_util.h" |
| #include "base/command_line.h" |
| #include "base/json/json_writer.h" |
| #include "base/run_loop.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "chromeos/dbus/power/fake_power_manager_client.h" |
| #include "chromeos/dbus/power_manager/suspend.pb.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/events/event.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/widget/widget.h" |
| |
| using PowerManagerClient = chromeos::PowerManagerClient; |
| |
| namespace ash { |
| |
| namespace { |
| |
| // A non-zero brightness used for test. |
| constexpr double kNonZeroBrightness = 10.; |
| |
| // Width of the display. |
| constexpr int kDisplayWidth = 2000; |
| |
| // Height of the display. |
| constexpr int kDisplayHeight = 1200; |
| |
| // Power button position offset percentage. |
| constexpr double kPowerButtonPercentage = 0.9f; |
| |
| // Shorthand for some long constants. |
| constexpr power_manager::BacklightBrightnessChange_Cause kUserCause = |
| power_manager::BacklightBrightnessChange_Cause_USER_REQUEST; |
| constexpr power_manager::BacklightBrightnessChange_Cause kOtherCause = |
| power_manager::BacklightBrightnessChange_Cause_OTHER; |
| |
| } // namespace |
| |
| using PowerButtonPosition = PowerButtonController::PowerButtonPosition; |
| |
| class PowerButtonControllerTest : public PowerButtonTestBase { |
| public: |
| PowerButtonControllerTest() = default; |
| ~PowerButtonControllerTest() override = default; |
| |
| void SetUp() override { |
| PowerButtonTestBase::SetUp(); |
| InitPowerButtonControllerMembers(PowerManagerClient::TabletMode::ON); |
| |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Advance a duration longer than |kIgnorePowerButtonAfterResumeDelay| to |
| // avoid events being ignored. |
| tick_clock_.Advance( |
| PowerButtonController::kIgnorePowerButtonAfterResumeDelay + |
| base::TimeDelta::FromMilliseconds(2)); |
| |
| // Run the event loop so that PowerButtonDisplayController can receive the |
| // initial backlights-forced-off state. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| protected: |
| void SendBrightnessChange( |
| double percent, |
| power_manager::BacklightBrightnessChange_Cause cause) { |
| power_manager::BacklightBrightnessChange change; |
| change.set_percent(percent); |
| change.set_cause(cause); |
| power_manager_client()->SendScreenBrightnessChanged(change); |
| } |
| |
| bool GetLockedState() { |
| // LockScreen is an async mojo call. |
| GetSessionControllerClient()->FlushForTest(); |
| return Shell::Get()->session_controller()->IsScreenLocked(); |
| } |
| |
| bool GetGlobalTouchscreenEnabled() const { |
| return Shell::Get()->touch_devices_controller()->GetTouchscreenEnabled( |
| TouchDeviceEnabledSource::GLOBAL); |
| } |
| |
| // Tapping power button when screen is off will turn the screen on but not |
| // showing the menu. |
| void TappingPowerButtonWhenScreenIsIdleOff() { |
| SendBrightnessChange(0, kUserCause); |
| PressPowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| } |
| |
| // Press the power button to show the menu. |
| void OpenPowerButtonMenu() { |
| PressPowerButton(); |
| if (Shell::Get()->tablet_mode_controller()->InTabletMode()) { |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ASSERT_TRUE(power_button_test_api_->TriggerPowerButtonMenuTimeout()); |
| } |
| ReleasePowerButton(); |
| ASSERT_TRUE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tap outside of the menu view to dismiss the menu. |
| void TapToDismissPowerButtonMenu() { |
| gfx::Rect menu_bounds = power_button_test_api_->GetMenuBoundsInScreen(); |
| GetEventGenerator()->GestureTapAt( |
| gfx::Point(menu_bounds.x() - 5, menu_bounds.y() - 5)); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| void PressLockButton() { |
| power_button_controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); |
| } |
| |
| void ReleaseLockButton() { |
| power_button_controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(PowerButtonControllerTest); |
| }; |
| |
| TEST_F(PowerButtonControllerTest, LockScreenIfRequired) { |
| Initialize(ButtonType::NORMAL, LoginStatus::USER); |
| EnableTabletMode(true); |
| SetShouldLockScreenAutomatically(true); |
| ASSERT_FALSE(GetLockedState()); |
| |
| // On User logged in status, power-button-press-release should lock screen if |
| // automatic screen-locking was requested. |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(GetLockedState()); |
| |
| // On locked state, power-button-press-release should do nothing. |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(GetLockedState()); |
| |
| // Unlock the sceen. |
| UnlockScreen(); |
| ASSERT_FALSE(GetLockedState()); |
| |
| // power-button-press-release should not lock the screen if automatic |
| // screen-locking wasn't requested. |
| SetShouldLockScreenAutomatically(false); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(GetLockedState()); |
| } |
| |
| // Tests tapping the power button of a clamshell device. |
| TEST_F(PowerButtonControllerTest, TappingPowerButtonOfClamshell) { |
| // Should not turn the screen off when screen is on. |
| InitPowerButtonControllerMembers(PowerManagerClient::TabletMode::UNSUPPORTED); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| power_button_test_api_->SetShowMenuAnimationDone(false); |
| // Start the showing power menu animation immediately as pressing the |
| // clamshell power button. |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| // Start the dimissing power menu animation immediately as releasing the |
| // clamsehll power button if showing animation hasn't finished. |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| AdvanceClockToAvoidIgnoring(); |
| // Should turn screen on if screen is off. |
| TappingPowerButtonWhenScreenIsIdleOff(); |
| ASSERT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| AdvanceClockToAvoidIgnoring(); |
| // Should not start the dismissing menu animation if showing menu animation |
| // has finished. |
| PressPowerButton(); |
| // Start the showing power menu animation immediately as pressing the |
| // clamshell power button. |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| power_button_test_api_->SetShowMenuAnimationDone(true); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| // Power button menu should keep opened if showing animation has finished. |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| // Tapping power button when menu is already shown should keep the screen on |
| // and dismiss the power menu. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests tapping the power button of a device that has a tablet mode switch. |
| TEST_F(PowerButtonControllerTest, TappingPowerButtonOfTablet) { |
| EnableTabletMode(true); |
| // Should turn screen off if screen is on and power button menu will not be |
| // shown. |
| PressPowerButton(); |
| // Showing power menu animation hasn't started as power menu timer is running. |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| // Should turn screen on if screen is off. |
| AdvanceClockToAvoidIgnoring(); |
| TappingPowerButtonWhenScreenIsIdleOff(); |
| |
| // Showing power menu animation should start until power menu timer is |
| // timeout. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| power_button_test_api_->SetShowMenuAnimationDone(false); |
| EXPECT_TRUE(power_button_test_api_->TriggerPowerButtonMenuTimeout()); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| ReleasePowerButton(); |
| // Showing animation will continue until show the power button menu even |
| // release the power button. |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| // Tapping power button when menu is already shown should still turn screen |
| // off and dismiss the menu. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PreShutdownTimerIsRunning()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PreShutdownTimerIsRunning()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| // Should turn screen on if screen is off. |
| AdvanceClockToAvoidIgnoring(); |
| TappingPowerButtonWhenScreenIsIdleOff(); |
| } |
| |
| // Tests that power button taps turn the screen off while in tablet mode but not |
| // in laptop mode. |
| TEST_F(PowerButtonControllerTest, ModeSpecificPowerButton) { |
| // While the device is in tablet mode, tapping the power button should turn |
| // the display off (and then back on if pressed a second time). |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // In laptop mode, tapping the power button shouldn't turn the screen off. |
| // Instead, we should start showing the power menu animation. |
| EnableTabletMode(false); |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Tapping power button again in laptop mode when menu is opened should |
| // dismiss the menu but keep the screen on. |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_button_test_api_->PreShutdownTimerIsRunning()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests that when the kForceTabletPowerButton flag is passed (indicating that |
| // the device is tablet-like) tapping the power button turns the screen off |
| // regardless of what the tablet mode switch reports. |
| TEST_F(PowerButtonControllerTest, ForceTabletPowerButton) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kForceTabletPowerButton); |
| ResetPowerButtonController(); |
| |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| EnableTabletMode(false); |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that release power button after menu is opened but before trigger |
| // shutdown will not turn screen off. |
| TEST_F(PowerButtonControllerTest, ReleasePowerButtonBeforeTriggerShutdown) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| ASSERT_TRUE(power_button_test_api_->TriggerPowerButtonMenuTimeout()); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running()); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running()); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that tapping the power button dismisses the menu while in laptop mode. |
| TEST_F(PowerButtonControllerTest, HoldPowerButtonWhileMenuShownInLaptopMode) { |
| // Hold the power button long enough to show the menu and start the |
| // cancellable shutdown animation. The menu should remain open. |
| PressPowerButton(); |
| ASSERT_TRUE(power_button_test_api_->IsMenuOpened()); |
| power_button_test_api_->SetShowMenuAnimationDone(true); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| ASSERT_TRUE(lock_state_test_api_->shutdown_timer_is_running()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| // Hold the power button long enough to start the cancellable shutdown |
| // animation again. The menu should remain open. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| ASSERT_TRUE(lock_state_test_api_->shutdown_timer_is_running()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| // This time, just tap the power button (i.e. release it before the |
| // cancellable shutdown animation starts). The menu should be dismissed. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests press lock button and power button in sequence. |
| TEST_F(PowerButtonControllerTest, PressAfterAnotherReleased) { |
| // Tap power button after press lock button should still turn screen off. |
| Initialize(ButtonType::NORMAL, LoginStatus::USER); |
| EnableTabletMode(true); |
| PressLockButton(); |
| ReleaseLockButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // Press lock button after tap power button should still lock screen. |
| PressPowerButton(); |
| ReleasePowerButton(); |
| PressLockButton(); |
| ReleaseLockButton(); |
| EXPECT_TRUE(lock_state_test_api_->is_animating_lock()); |
| EXPECT_TRUE(GetLockedState()); |
| } |
| |
| // Tests press lock/power button before release power/lock button. |
| TEST_F(PowerButtonControllerTest, PressBeforeAnotherReleased) { |
| // Press lock button when power button is still being pressed will be ignored |
| // and continue to turn screen off. |
| Initialize(ButtonType::NORMAL, LoginStatus::USER); |
| EnableTabletMode(true); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| PressPowerButton(); |
| PressLockButton(); |
| ReleaseLockButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(lock_state_test_api_->is_animating_lock()); |
| EXPECT_FALSE(GetLockedState()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // Turn the screen on. |
| PressPowerButton(); |
| ReleasePowerButton(); |
| // Press power button when lock button is still being pressed. The pressing of |
| // power button will be ignored and continue to lock screen. |
| PressLockButton(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| ReleaseLockButton(); |
| EXPECT_TRUE(lock_state_test_api_->is_animating_lock()); |
| EXPECT_TRUE(GetLockedState()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests tapping power button when device is suspended without backlights forced |
| // off. |
| TEST_F(PowerButtonControllerTest, |
| TappingPowerButtonWhenSuspendedWithoutBacklightsForcedOff) { |
| EnableTabletMode(true); |
| power_manager_client()->SendSuspendImminent( |
| power_manager::SuspendImminent_Reason_OTHER); |
| SendBrightnessChange(0, kUserCause); |
| // There is a power button pressed here, but PowerButtonEvent is sent later. |
| power_manager_client()->SendSuspendDone(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| |
| // Send the power button event after a short delay and check that backlights |
| // are not forced off. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(500)); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Send the power button event after a longer delay and check that backlights |
| // are forced off. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(1600)); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests tapping power button when device is suspended with backlights forced |
| // off. |
| TEST_F(PowerButtonControllerTest, |
| TappingPowerButtonWhenSuspendedWithBacklightsForcedOff) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| power_manager_client()->SendSuspendImminent( |
| power_manager::SuspendImminent_Reason_OTHER); |
| // There is a power button pressed here, but PowerButtonEvent is sent later. |
| // Because of backlights forced off, resuming system will not restore |
| // brightness. |
| power_manager_client()->SendSuspendDone(); |
| |
| // Send the power button event after a short delay and check that backlights |
| // are not forced off. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(500)); |
| PressPowerButton(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Send the power button event after a longer delay and check that backlights |
| // are forced off. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(1600)); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // For convertible device working on tablet mode, keyboard/mouse event should |
| // not SetBacklightsForcedOff(false) when screen is off. |
| TEST_F(PowerButtonControllerTest, ConvertibleOnTabletMode) { |
| EnableTabletMode(true); |
| |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| PressKey(ui::VKEY_L); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| GenerateMouseMoveEvent(); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that a single set of power button pressed-and-released operation should |
| // cause only one SetBacklightsForcedOff call. |
| TEST_F(PowerButtonControllerTest, IgnorePowerOnKeyEvent) { |
| ui::KeyEvent power_key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_POWER, |
| ui::EF_NONE); |
| ui::KeyEvent power_key_released(ui::ET_KEY_RELEASED, ui::VKEY_POWER, |
| ui::EF_NONE); |
| |
| // There are two |power_key_pressed| events and |power_key_released| events |
| // generated for each pressing and releasing, and multiple repeating pressed |
| // events depending on holding. |
| ASSERT_EQ(0, power_manager_client()->num_set_backlights_forced_off_calls()); |
| EnableTabletMode(true); |
| power_button_test_api_->SendKeyEvent(&power_key_pressed); |
| power_button_test_api_->SendKeyEvent(&power_key_pressed); |
| PressPowerButton(); |
| power_button_test_api_->SendKeyEvent(&power_key_pressed); |
| power_button_test_api_->SendKeyEvent(&power_key_pressed); |
| power_button_test_api_->SendKeyEvent(&power_key_pressed); |
| ReleasePowerButton(); |
| power_button_test_api_->SendKeyEvent(&power_key_released); |
| power_button_test_api_->SendKeyEvent(&power_key_released); |
| EXPECT_EQ(1, power_manager_client()->num_set_backlights_forced_off_calls()); |
| } |
| |
| // Tests that when the power button is pressed/released in tablet mode, |
| // requesting/stopping backlights forced off should update the global |
| // touchscreen enabled status. |
| TEST_F(PowerButtonControllerTest, DisableTouchscreenWhileForcedOff) { |
| // Tests tablet power button. |
| EnableTabletMode(true); |
| ASSERT_TRUE(GetGlobalTouchscreenEnabled()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_FALSE(GetGlobalTouchscreenEnabled()); |
| |
| PressPowerButton(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| ReleasePowerButton(); |
| EXPECT_TRUE(GetGlobalTouchscreenEnabled()); |
| } |
| |
| // When the screen is turned off automatically, the touchscreen should also be |
| // disabled. |
| TEST_F(PowerButtonControllerTest, DisableTouchscreenForInactivity) { |
| ASSERT_TRUE(GetGlobalTouchscreenEnabled()); |
| |
| // Turn screen off for automated change (e.g. user is inactive). |
| SendBrightnessChange(0, kOtherCause); |
| EXPECT_FALSE(GetGlobalTouchscreenEnabled()); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_TRUE(GetGlobalTouchscreenEnabled()); |
| |
| // After decreasing the brightness to zero for a user request, the touchscreen |
| // should remain enabled. |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_TRUE(GetGlobalTouchscreenEnabled()); |
| } |
| |
| // When user switches convertible device between tablet mode and laptop mode, |
| // power button may be pressed and held, which may cause unwanted unclean |
| // shutdown. |
| TEST_F(PowerButtonControllerTest, LeaveTabletModeWhilePressingPowerButton) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EnableTabletMode(false); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(1500)); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests that repeated power button releases are ignored (crbug.com/675291). |
| TEST_F(PowerButtonControllerTest, IgnoreRepeatedPowerButtonReleases) { |
| // Set backlights forced off for starting point. |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // Test that a pressing-releasing operation after a short duration, backlights |
| // forced off is stopped since we don't drop request for power button pressed. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(200)); |
| PressPowerButton(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Test that after another short duration, backlights will not be forced off |
| // since this immediately following forcing off request needs to be dropped. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(200)); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Test that after another long duration, backlights should be forced off. |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(800)); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that repeated power button releases of clamshell should cancel the |
| // ongoing showing menu animation. |
| TEST_F(PowerButtonControllerTest, |
| ClamshellRepeatedPowerButtonReleasesCancelledAnimation) { |
| InitPowerButtonControllerMembers(PowerManagerClient::TabletMode::UNSUPPORTED); |
| EnableTabletMode(false); |
| |
| // Enable animations so that we can make sure that they occur. |
| ui::ScopedAnimationDurationScaleMode regular_animations( |
| ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| tick_clock_.Advance(base::TimeDelta::FromMilliseconds(200)); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| // Showing menu animation should be cancelled and menu is not shown. |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| EXPECT_FALSE(power_button_test_api_->PreShutdownTimerIsRunning()); |
| } |
| |
| // Tests that lid closed events stop forcing off backlights. |
| TEST_F(PowerButtonControllerTest, LidEventsStopForcingOff) { |
| // Pressing/releasing power button to set backlights forced off. |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // A lid closed event is received, we should stop forcing off backlights. |
| power_manager_client()->SetLidState(PowerManagerClient::LidState::CLOSED, |
| tick_clock_.NowTicks()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that tablet mode events from powerd stop forcing off backlights. |
| TEST_F(PowerButtonControllerTest, TabletModeEventsStopForcingOff) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| power_manager_client()->SetTabletMode(PowerManagerClient::TabletMode::OFF, |
| tick_clock_.NowTicks()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| power_manager_client()->SetTabletMode(PowerManagerClient::TabletMode::ON, |
| tick_clock_.NowTicks()); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that with system reboot, the global touchscreen enabled status should |
| // be synced with new backlights forced off state from powerd. |
| TEST_F(PowerButtonControllerTest, SyncTouchscreenEnabled) { |
| Shell::Get()->touch_devices_controller()->SetTouchscreenEnabled( |
| false, TouchDeviceEnabledSource::GLOBAL); |
| ASSERT_FALSE(GetGlobalTouchscreenEnabled()); |
| |
| // Simulate system reboot by resetting backlights forced off state in powerd |
| // and PowerButtonController. |
| power_manager_client()->SetBacklightsForcedOff(false); |
| ResetPowerButtonController(); |
| SetTabletModeSwitchState(PowerManagerClient::TabletMode::ON); |
| |
| // Run the event loop for PowerButtonDisplayController to get backlight state |
| // and check that the global touchscreen status is correct. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| EXPECT_TRUE(GetGlobalTouchscreenEnabled()); |
| } |
| |
| // Tests that when backlights get forced off due to tablet power button, media |
| // sessions should be suspended. |
| TEST_F(PowerButtonControllerTest, SuspendMediaSessions) { |
| TestMediaClient client; |
| Shell::Get()->media_controller()->SetClient(&client); |
| ASSERT_FALSE(client.media_sessions_suspended()); |
| |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| // Run the event loop for PowerButtonDisplayController to get backlight state. |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| EXPECT_TRUE(client.media_sessions_suspended()); |
| } |
| |
| // Tests that when system is suspended with backlights forced off, and then |
| // system resumes due to power button pressed without power button event fired |
| // (crbug.com/735291), that we stop forcing off backlights. |
| TEST_F(PowerButtonControllerTest, SuspendDoneStopsForcingOff) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // Simulate an edge case that system resumes because of tablet power button |
| // pressed, but power button event is not delivered. |
| power_manager_client()->SendSuspendDone(); |
| |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that during the interval that the display is turning on, tablet power |
| // button should not set display off (crbug.com/735225). |
| TEST_F(PowerButtonControllerTest, IgnoreForcingOffWhenDisplayIsTurningOn) { |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| ASSERT_TRUE(power_manager_client()->backlights_forced_off()); |
| |
| // Simiulate the backlight no longer being forced off due to a key event |
| // (which we need to briefly leave tablet mode to receive). Chrome will |
| // receive a brightness changed signal, but the display may still be off. |
| EnableTabletMode(false); |
| PressKey(ui::VKEY_L); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // Since display could still be off, ignore additional button presses. |
| tick_clock_.Advance(PowerButtonController::kScreenStateChangeDelay - |
| base::TimeDelta::FromMilliseconds(1)); |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_manager_client()->backlights_forced_off()); |
| |
| // After waiting long enough, we should be able to force the display off. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| // Tests that a11y alert is sent on tablet power button induced screen state |
| // change. |
| TEST_F(PowerButtonControllerTest, A11yAlert) { |
| TestAccessibilityControllerClient a11y_client; |
| |
| EnableTabletMode(true); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_EQ(AccessibilityAlert::SCREEN_OFF, a11y_client.last_a11y_alert()); |
| |
| PressPowerButton(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_EQ(AccessibilityAlert::SCREEN_ON, a11y_client.last_a11y_alert()); |
| ReleasePowerButton(); |
| } |
| |
| // Tests that tap outside of the menu bounds should dismiss the menu. |
| TEST_F(PowerButtonControllerTest, TapToDismissMenu) { |
| OpenPowerButtonMenu(); |
| TapToDismissPowerButtonMenu(); |
| } |
| |
| // Test that mouse click outside of the menu bounds should dismiss the menu. |
| TEST_F(PowerButtonControllerTest, MouseClickToDismissMenu) { |
| OpenPowerButtonMenu(); |
| gfx::Rect menu_bounds = power_button_test_api_->GetMenuBoundsInScreen(); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->MoveMouseTo(gfx::Point(menu_bounds.x() - 5, menu_bounds.y() - 5)); |
| generator->ClickLeftButton(); |
| generator->ReleaseLeftButton(); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests the menu items according to the login and screen locked status. |
| TEST_F(PowerButtonControllerTest, MenuItemsToLoginAndLockedStatus) { |
| // No sign out, lock screen and feedback items if user is not logged in. |
| ClearLogin(); |
| Shell::Get()->UpdateAfterLoginStatusChange(LoginStatus::NOT_LOGGED_IN); |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(power_button_test_api_->MenuHasSignOutItem()); |
| EXPECT_FALSE(power_button_test_api_->MenuHasLockScreenItem()); |
| EXPECT_FALSE(power_button_test_api_->MenuHasFeedbackItem()); |
| TapToDismissPowerButtonMenu(); |
| |
| // Should have sign out and feedback items if in guest mode (or, generally, |
| // if screen locking is disabled). |
| ClearLogin(); |
| Initialize(ButtonType::NORMAL, LoginStatus::GUEST); |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(GetLockedState()); |
| EXPECT_TRUE(power_button_test_api_->MenuHasSignOutItem()); |
| EXPECT_FALSE(power_button_test_api_->MenuHasLockScreenItem()); |
| EXPECT_TRUE(power_button_test_api_->MenuHasFeedbackItem()); |
| TapToDismissPowerButtonMenu(); |
| |
| // Should have sign out, lock screen and feedback items if user is logged in |
| // and screen is unlocked. |
| ClearLogin(); |
| CreateUserSessions(1); |
| Shell::Get()->UpdateAfterLoginStatusChange(LoginStatus::USER); |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(GetLockedState()); |
| EXPECT_TRUE(power_button_test_api_->MenuHasSignOutItem()); |
| EXPECT_TRUE(power_button_test_api_->MenuHasLockScreenItem()); |
| EXPECT_TRUE(power_button_test_api_->MenuHasFeedbackItem()); |
| TapToDismissPowerButtonMenu(); |
| |
| // Should have sign out but not lock screen and feedback items if user is |
| // logged in but screen is locked. |
| LockScreen(); |
| EXPECT_TRUE(GetLockedState()); |
| OpenPowerButtonMenu(); |
| EXPECT_TRUE(power_button_test_api_->MenuHasSignOutItem()); |
| EXPECT_FALSE(power_button_test_api_->MenuHasLockScreenItem()); |
| EXPECT_FALSE(power_button_test_api_->MenuHasFeedbackItem()); |
| } |
| |
| // Tests long-pressing the power button when the menu is open. |
| TEST_F(PowerButtonControllerTest, LongPressButtonWhenMenuIsOpened) { |
| OpenPowerButtonMenu(); |
| AdvanceClockToAvoidIgnoring(); |
| |
| // Long pressing the power button when menu is opened should not dismiss the |
| // menu but trigger the pre-shutdown animation instead. Menu should stay |
| // opened if releasing the button can cancel the animation. |
| PressPowerButton(); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running()); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| // Change focus to 'sign out' |
| PressKey(ui::VKEY_TAB); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->sign_out_item_for_test() |
| ->HasFocus()); |
| |
| // Long press when menu is opened with focus on 'sign out' item will change |
| // the focus to 'power off' after starting the pre-shutdown animation. |
| PressPowerButton(); |
| ASSERT_TRUE(power_button_test_api_->TriggerPreShutdownTimeout()); |
| EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running()); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->power_off_item_for_test() |
| ->HasFocus()); |
| ReleasePowerButton(); |
| } |
| |
| // Tests that switches between laptop mode and tablet mode should dismiss the |
| // opened menu. |
| TEST_F(PowerButtonControllerTest, EnterOrLeaveTabletModeDismissMenu) { |
| OpenPowerButtonMenu(); |
| EnableTabletMode(true); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| OpenPowerButtonMenu(); |
| EnableTabletMode(false); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests that screen changes to idle off will dismiss the opened menu. |
| TEST_F(PowerButtonControllerTest, DismissMenuWhenScreenIsIdleOff) { |
| OpenPowerButtonMenu(); |
| // Mock screen idle off. |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests that tapping the power button should dimiss the opened menu. |
| TEST_F(PowerButtonControllerTest, TappingPowerButtonWhenMenuIsOpened) { |
| EnableTabletMode(true); |
| OpenPowerButtonMenu(); |
| |
| // Tapping the power button when menu is opened will dismiss the menu. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| |
| // Long press the power button when backlights are off will show the menu. |
| PressPowerButton(); |
| SendBrightnessChange(kNonZeroBrightness, kUserCause); |
| EXPECT_TRUE(power_button_test_api_->TriggerPowerButtonMenuTimeout()); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| // Tapping the power button will dismiss the menu. |
| AdvanceClockToAvoidIgnoring(); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| SendBrightnessChange(0, kUserCause); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests that suspend will dismiss the opened menu. |
| TEST_F(PowerButtonControllerTest, SuspendWithMenuOn) { |
| OpenPowerButtonMenu(); |
| power_manager_client()->SendSuspendImminent( |
| power_manager::SuspendImminent_Reason_OTHER); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| power_manager_client()->SendSuspendDone(); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests the formerly-active window state in showing power menu. |
| TEST_F(PowerButtonControllerTest, FormerlyActiveWindowInShowingMenu) { |
| std::unique_ptr<views::Widget> widget = CreateTestWidget(); |
| ASSERT_TRUE(widget->IsActive()); |
| |
| OpenPowerButtonMenu(); |
| // The active window becomes inactive after menu is shown but it is still |
| // painted as active to avoid frame color change. |
| EXPECT_FALSE(widget->IsActive()); |
| EXPECT_TRUE(widget->ShouldPaintAsActive()); |
| EXPECT_TRUE(widget->non_client_view()->frame_view()->ShouldPaintAsActive()); |
| EXPECT_TRUE( |
| wm::IsActiveWindow(power_button_test_api_->GetPowerButtonMenuView() |
| ->GetWidget() |
| ->GetNativeWindow())); |
| // Should reset the previous painting as active setting of the active window |
| // if dismissing the menu. |
| TapToDismissPowerButtonMenu(); |
| |
| // Focus may fall to the widget if it's the only remaining widget on the |
| // screen. Deactivate it to verify that it's no longer being forced to render |
| // as active. |
| widget->Deactivate(); |
| EXPECT_FALSE(widget->ShouldPaintAsActive()); |
| |
| // A widget which is not the active widget is not affected by opening the |
| // power button menu. |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(widget->ShouldPaintAsActive()); |
| TapToDismissPowerButtonMenu(); |
| |
| // If focus didn't fall to the widget after the menu was closed, focus it. |
| widget->Activate(); |
| |
| // Dismiss menu should work well after the active window is closed between |
| // showing and dismissing menu. |
| OpenPowerButtonMenu(); |
| widget->Close(); |
| TapToDismissPowerButtonMenu(); |
| } |
| |
| // Tests that cursor is hidden after show the menu and should reappear if mouse |
| // moves. |
| TEST_F(PowerButtonControllerTest, HideCursorAfterShowMenu) { |
| // Cursor is hidden after show the menu. |
| ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager(); |
| EXPECT_TRUE(cursor_manager->IsCursorVisible()); |
| PressPowerButton(); |
| ReleasePowerButton(); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| EXPECT_FALSE(cursor_manager->IsCursorVisible()); |
| |
| // Cursor reappears if mouse moves. |
| GenerateMouseMoveEvent(); |
| EXPECT_TRUE(cursor_manager->IsCursorVisible()); |
| } |
| |
| // Tests that press VKEY_ESCAPE should dismiss the opened menu. |
| TEST_F(PowerButtonControllerTest, ESCDismissMenu) { |
| OpenPowerButtonMenu(); |
| |
| PressKey(ui::VKEY_VOLUME_DOWN); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| PressKey(ui::VKEY_BRIGHTNESS_UP); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| PressKey(ui::VKEY_BROWSER_SEARCH); |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| |
| PressKey(ui::VKEY_ESCAPE); |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| } |
| |
| // Tests the navigation of the menu. |
| TEST_F(PowerButtonControllerTest, MenuNavigation) { |
| OpenPowerButtonMenu(); |
| ASSERT_TRUE(power_button_test_api_->MenuHasSignOutItem()); |
| ASSERT_TRUE(power_button_test_api_->MenuHasLockScreenItem()); |
| ASSERT_TRUE(power_button_test_api_->MenuHasFeedbackItem()); |
| PressKey(ui::VKEY_TAB); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->power_off_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_RIGHT); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->sign_out_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_DOWN); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->lock_screen_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_TAB); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->feedback_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_TAB); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->power_off_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_UP); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->feedback_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_UP); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->lock_screen_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_LEFT); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->sign_out_item_for_test() |
| ->HasFocus()); |
| |
| PressKey(ui::VKEY_UP); |
| EXPECT_TRUE(power_button_test_api_->GetPowerButtonMenuView() |
| ->power_off_item_for_test() |
| ->HasFocus()); |
| } |
| |
| // Tests that the partially shown menu will be dismissed by power button up in |
| // tablet mode, and screen will be turned off at the same time. |
| TEST_F(PowerButtonControllerTest, PartiallyShownMenuInTabletMode) { |
| EnableTabletMode(true); |
| |
| // Enable animations so that we can make sure that they occur. |
| ui::ScopedAnimationDurationScaleMode regular_animations( |
| ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| |
| PressPowerButton(); |
| EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| EXPECT_TRUE(power_button_test_api_->TriggerPowerButtonMenuTimeout()); |
| EXPECT_FALSE(power_button_test_api_->PowerButtonMenuTimerIsRunning()); |
| // Power menu is in the partially shown state. |
| EXPECT_TRUE(power_button_test_api_->IsMenuOpened()); |
| EXPECT_FALSE(power_button_test_api_->ShowMenuAnimationDone()); |
| ReleasePowerButton(); |
| EXPECT_FALSE(power_button_test_api_->ShowMenuAnimationDone()); |
| // The partially shown menu should be dismissed by power button up. |
| EXPECT_FALSE(power_button_test_api_->IsMenuOpened()); |
| EXPECT_TRUE(power_manager_client()->backlights_forced_off()); |
| } |
| |
| class PowerButtonControllerWithPositionTest |
| : public PowerButtonControllerTest, |
| public testing::WithParamInterface<PowerButtonPosition> { |
| public: |
| PowerButtonControllerWithPositionTest() : power_button_position_(GetParam()) { |
| base::DictionaryValue position_info; |
| switch (power_button_position_) { |
| case PowerButtonPosition::LEFT: |
| position_info.SetString(PowerButtonController::kEdgeField, |
| PowerButtonController::kLeftEdge); |
| break; |
| case PowerButtonPosition::RIGHT: |
| position_info.SetString(PowerButtonController::kEdgeField, |
| PowerButtonController::kRightEdge); |
| break; |
| case PowerButtonPosition::TOP: |
| position_info.SetString(PowerButtonController::kEdgeField, |
| PowerButtonController::kTopEdge); |
| break; |
| case PowerButtonPosition::BOTTOM: |
| position_info.SetString(PowerButtonController::kEdgeField, |
| PowerButtonController::kBottomEdge); |
| break; |
| default: |
| return; |
| } |
| position_info.SetDouble(PowerButtonController::kPositionField, |
| kPowerButtonPercentage); |
| |
| std::string json_position_info; |
| base::JSONWriter::Write(position_info, &json_position_info); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| switches::kAshPowerButtonPosition, json_position_info); |
| } |
| |
| bool IsLeftOrRightPosition() const { |
| return power_button_position_ == PowerButtonPosition::LEFT || |
| power_button_position_ == PowerButtonPosition::RIGHT; |
| } |
| |
| // Returns true if it is in tablet mode. |
| bool IsTabletMode() const { |
| return Shell::Get()->tablet_mode_controller()->InTabletMode(); |
| } |
| |
| // Returns true if the menu is at the center of the display. |
| bool IsMenuCentered() const { |
| return power_button_test_api_->GetMenuBoundsInScreen().CenterPoint() == |
| display::Screen::GetScreen() |
| ->GetPrimaryDisplay() |
| .bounds() |
| .CenterPoint(); |
| } |
| |
| PowerButtonPosition power_button_position() const { |
| return power_button_position_; |
| } |
| |
| private: |
| PowerButtonPosition power_button_position_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PowerButtonControllerWithPositionTest); |
| }; |
| |
| // TODO(crbug.com/1010194). |
| TEST_P(PowerButtonControllerWithPositionTest, |
| DISABLED_MenuNextToPowerButtonInTabletMode) { |
| std::string display = |
| std::to_string(kDisplayWidth) + "x" + std::to_string(kDisplayHeight); |
| UpdateDisplay(display); |
| display::test::ScopedSetInternalDisplayId set_internal( |
| display_manager(), GetPrimaryDisplay().id()); |
| |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| // Set the screen orientation to LANDSCAPE_PRIMARY. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| // Menu is set at the center of the display if it is not in tablet mode. |
| OpenPowerButtonMenu(); |
| ASSERT_FALSE(IsTabletMode()); |
| EXPECT_TRUE(IsMenuCentered()); |
| TapToDismissPowerButtonMenu(); |
| |
| int animation_transform = PowerButtonMenuView::kMenuViewTransformDistanceDp; |
| EnableTabletMode(true); |
| EXPECT_TRUE(IsTabletMode()); |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(IsMenuCentered()); |
| if (power_button_position() == PowerButtonPosition::LEFT) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().x()); |
| } else if (power_button_position() == PowerButtonPosition::RIGHT) { |
| EXPECT_EQ(animation_transform, |
| kDisplayWidth - |
| power_button_test_api_->GetMenuBoundsInScreen().right()); |
| } else if (power_button_position() == PowerButtonPosition::TOP) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().y()); |
| } else if (power_button_position() == PowerButtonPosition::BOTTOM) { |
| EXPECT_EQ(animation_transform, |
| kDisplayHeight - |
| power_button_test_api_->GetMenuBoundsInScreen().bottom()); |
| } |
| |
| // Rotate the screen by 270 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| EXPECT_FALSE(IsMenuCentered()); |
| if (power_button_position() == PowerButtonPosition::LEFT) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().y()); |
| } else if (power_button_position() == PowerButtonPosition::RIGHT) { |
| EXPECT_EQ(animation_transform, |
| kDisplayWidth - |
| power_button_test_api_->GetMenuBoundsInScreen().bottom()); |
| } else if (power_button_position() == PowerButtonPosition::TOP) { |
| EXPECT_EQ(animation_transform, |
| kDisplayHeight - |
| power_button_test_api_->GetMenuBoundsInScreen().right()); |
| } else if (power_button_position() == PowerButtonPosition::BOTTOM) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().x()); |
| } |
| |
| // Rotate the screen by 180 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_180, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapeSecondary); |
| EXPECT_FALSE(IsMenuCentered()); |
| if (power_button_position() == PowerButtonPosition::LEFT) { |
| EXPECT_EQ(animation_transform, |
| kDisplayWidth - |
| power_button_test_api_->GetMenuBoundsInScreen().right()); |
| } else if (power_button_position() == PowerButtonPosition::RIGHT) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().x()); |
| } else if (power_button_position() == PowerButtonPosition::TOP) { |
| EXPECT_EQ(animation_transform, |
| kDisplayHeight - |
| power_button_test_api_->GetMenuBoundsInScreen().bottom()); |
| } else if (power_button_position() == PowerButtonPosition::BOTTOM) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().y()); |
| } |
| |
| // Rotate the screen by 90 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitSecondary); |
| EXPECT_FALSE(IsMenuCentered()); |
| if (power_button_position() == PowerButtonPosition::LEFT) { |
| EXPECT_EQ(animation_transform, |
| kDisplayWidth - |
| power_button_test_api_->GetMenuBoundsInScreen().bottom()); |
| } else if (power_button_position() == PowerButtonPosition::RIGHT) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().y()); |
| } else if (power_button_position() == PowerButtonPosition::TOP) { |
| EXPECT_EQ(animation_transform, |
| power_button_test_api_->GetMenuBoundsInScreen().x()); |
| } else if (power_button_position() == PowerButtonPosition::BOTTOM) { |
| EXPECT_EQ(animation_transform, |
| kDisplayHeight - |
| power_button_test_api_->GetMenuBoundsInScreen().right()); |
| } |
| } |
| |
| // Tests that the menu is always shown at the percentage of position when |
| // display has different scale factors. |
| TEST_P(PowerButtonControllerWithPositionTest, MenuShownAtPercentageOfPosition) { |
| const int scale_factor = 2; |
| std::string display = "8000x2400*" + std::to_string(scale_factor); |
| UpdateDisplay(display); |
| int64_t primary_id = GetPrimaryDisplay().id(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager(), |
| primary_id); |
| ASSERT_EQ(scale_factor, GetPrimaryDisplay().device_scale_factor()); |
| |
| EnableTabletMode(true); |
| OpenPowerButtonMenu(); |
| EXPECT_FALSE(IsMenuCentered()); |
| gfx::Point menu_center_point = |
| power_button_test_api_->GetMenuBoundsInScreen().CenterPoint(); |
| gfx::Rect display_bounds = GetPrimaryDisplay().bounds(); |
| int original_width = display_bounds.width(); |
| int original_height = display_bounds.height(); |
| if (IsLeftOrRightPosition()) { |
| EXPECT_EQ(menu_center_point.y(), static_cast<int>(display_bounds.height() * |
| kPowerButtonPercentage)); |
| } else { |
| EXPECT_EQ(menu_center_point.x(), static_cast<int>(display_bounds.width() * |
| kPowerButtonPercentage)); |
| } |
| TapToDismissPowerButtonMenu(); |
| |
| display_manager()->UpdateZoomFactor(primary_id, 1.f / scale_factor); |
| ASSERT_EQ(1.0f, GetPrimaryDisplay().device_scale_factor()); |
| display_bounds = GetPrimaryDisplay().bounds(); |
| int scale_up_width = display_bounds.width(); |
| int scale_up_height = display_bounds.height(); |
| EXPECT_EQ(scale_up_width, original_width * scale_factor); |
| EXPECT_EQ(scale_up_height, original_height * scale_factor); |
| OpenPowerButtonMenu(); |
| menu_center_point = |
| power_button_test_api_->GetMenuBoundsInScreen().CenterPoint(); |
| // Menu is still at the kPowerButtonPercentage position after scale up screen. |
| if (IsLeftOrRightPosition()) { |
| EXPECT_EQ(menu_center_point.y(), static_cast<int>(display_bounds.height() * |
| kPowerButtonPercentage)); |
| } else { |
| EXPECT_EQ(menu_center_point.x(), static_cast<int>(display_bounds.width() * |
| kPowerButtonPercentage)); |
| } |
| } |
| |
| TEST_P(PowerButtonControllerWithPositionTest, AdjustMenuShownForDisplaySize) { |
| OpenPowerButtonMenu(); |
| gfx::Rect menu_bounds = power_button_test_api_->GetMenuBoundsInScreen(); |
| TapToDismissPowerButtonMenu(); |
| |
| // (1 - kPowerButtonPercentage) * display_height < 0.5 * menu_height makes |
| // sure menu will be cut off by display when button is on LEFT/RIGHT, and (1 - |
| // kPowerButtonPercentage) * display_width < 0.5 * menu_width makes sure menu |
| // will be cut off by display when button is on TOP/BOTTOM. |
| int display_width = |
| 0.5 / (1.0f - kPowerButtonPercentage) * menu_bounds.width() - 5; |
| int display_height = |
| 0.5 / (1.0f - kPowerButtonPercentage) * menu_bounds.height() - 5; |
| std::string display = |
| std::to_string(display_width) + "x" + std::to_string(display_height); |
| UpdateDisplay(display); |
| display::test::ScopedSetInternalDisplayId set_internal( |
| display_manager(), GetPrimaryDisplay().id()); |
| |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| // Set the screen orientation to LANDSCAPE_PRIMARY. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| EnableTabletMode(true); |
| OpenPowerButtonMenu(); |
| // Menu's bounds is always inside the display. |
| EXPECT_TRUE(GetPrimaryDisplay().bounds().Contains( |
| power_button_test_api_->GetMenuBoundsInScreen())); |
| |
| // Rotate the screen by 270 degrees. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| EXPECT_TRUE(GetPrimaryDisplay().bounds().Contains( |
| power_button_test_api_->GetMenuBoundsInScreen())); |
| |
| // Rotate the screen by 180 degrees. |
| test_api.SetDisplayRotation(display::Display::ROTATE_180, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapeSecondary); |
| EXPECT_TRUE(GetPrimaryDisplay().bounds().Contains( |
| power_button_test_api_->GetMenuBoundsInScreen())); |
| |
| // Rotate the screen by 90 degrees. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitSecondary); |
| EXPECT_TRUE(GetPrimaryDisplay().bounds().Contains( |
| power_button_test_api_->GetMenuBoundsInScreen())); |
| } |
| |
| // Tests that a power button press before the menu is fully shown will not |
| // create a new menu. |
| TEST_F(PowerButtonControllerTest, LegacyPowerButtonIgnoreExtraPress) { |
| Initialize(ButtonType::LEGACY, LoginStatus::USER); |
| |
| // Enable animations so that we can make sure that they occur. |
| ui::ScopedAnimationDurationScaleMode regular_animations( |
| ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| PressPowerButton(); |
| // Power menu is in the partially shown state. |
| ASSERT_TRUE(power_button_test_api_->IsMenuOpened()); |
| ASSERT_FALSE(power_button_test_api_->ShowMenuAnimationDone()); |
| PowerButtonMenuView* menu_view_before = |
| power_button_test_api_->GetPowerButtonMenuView(); |
| // Press power button again and make sure new PowerButtonMenuView is not |
| // created. This makes sure that we do not create a new menu while we are in |
| // the process of creating one for an old power button press. |
| PressPowerButton(); |
| EXPECT_EQ(menu_view_before, power_button_test_api_->GetPowerButtonMenuView()); |
| // This is needed to simulate the shutdown sound having been played, |
| // which blocks the shutdown timer. |
| // Make sure that the second press did not trigger a shutdown. |
| EXPECT_FALSE(lock_state_test_api_->real_shutdown_timer_is_running()); |
| // Make sure that power menu is still in partially shown state. |
| ASSERT_TRUE(power_button_test_api_->IsMenuOpened()); |
| ASSERT_FALSE(power_button_test_api_->ShowMenuAnimationDone()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AshPowerButtonPosition, |
| PowerButtonControllerWithPositionTest, |
| testing::Values(PowerButtonPosition::LEFT, |
| PowerButtonPosition::RIGHT, |
| PowerButtonPosition::TOP, |
| PowerButtonPosition::BOTTOM)); |
| |
| } // namespace ash |