blob: 6ae558ee88302c811869ee38f00da51975c6587c [file] [log] [blame]
// Copyright 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/wm/lock_state_controller.h"
#include <memory>
#include <utility>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/cpp/config.h"
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "ash/shutdown_controller.h"
#include "ash/shutdown_reason.h"
#include "ash/system/power/power_button_controller.h"
#include "ash/system/power/power_button_test_base.h"
#include "ash/touch/touch_devices_controller.h"
#include "ash/wm/lock_state_controller_test_api.h"
#include "ash/wm/session_state_animator.h"
#include "ash/wm/test_session_state_animator.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "chromeos/dbus/fake_power_manager_client.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "ui/display/manager/chromeos/display_configurator.h"
#include "ui/display/manager/fake_display_snapshot.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/geometry/size.h"
namespace ash {
namespace {
bool cursor_visible() {
return Shell::Get()->cursor_manager()->IsCursorVisible();
}
void CheckCalledCallback(bool* flag) {
if (flag)
(*flag) = true;
}
// ShutdownController that tracks how many shutdown requests have been made.
class TestShutdownController : public ShutdownController {
public:
TestShutdownController() = default;
~TestShutdownController() override = default;
int num_shutdown_requests() const { return num_shutdown_requests_; }
private:
// ShutdownController:
void ShutDownOrReboot(ShutdownReason reason) override {
num_shutdown_requests_++;
}
int num_shutdown_requests_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestShutdownController);
};
} // namespace
class LockStateControllerTest : public PowerButtonTestBase {
public:
LockStateControllerTest() = default;
~LockStateControllerTest() override = default;
void SetUp() override {
PowerButtonTestBase::SetUp();
InitPowerButtonControllerMembers(false /* SendAccelerometerUpdate */);
test_animator_ = new TestSessionStateAnimator;
lock_state_controller_->set_animator_for_test(test_animator_);
lock_state_test_api_->set_shutdown_controller(&test_shutdown_controller_);
a11y_controller_ = Shell::Get()->accessibility_controller();
a11y_controller_->SetClient(a11y_client_.CreateInterfacePtrAndBind());
}
protected:
int NumShutdownRequests() {
return test_shutdown_controller_.num_shutdown_requests();
}
void Advance(SessionStateAnimator::AnimationSpeed speed) {
test_animator_->Advance(test_animator_->GetDuration(speed));
}
void AdvancePartially(SessionStateAnimator::AnimationSpeed speed,
float factor) {
test_animator_->Advance(test_animator_->GetDuration(speed) * factor);
}
void ExpectPreLockAnimationStarted() {
SCOPED_TRACE("Failure in ExpectPreLockAnimationStarted");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_LIFT));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_OUT));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY));
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
}
void ExpectPreLockAnimationRunning() {
SCOPED_TRACE("Failure in ExpectPreLockAnimationRunning");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_LIFT));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_OUT));
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
}
void ExpectPreLockAnimationCancel() {
SCOPED_TRACE("Failure in ExpectPreLockAnimationCancel");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_UNDO_LIFT));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectPreLockAnimationFinished() {
SCOPED_TRACE("Failure in ExpectPreLockAnimationFinished");
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_LIFT));
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_OUT));
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY));
}
void ExpectPostLockAnimationStarted() {
SCOPED_TRACE("Failure in ExpectPostLockAnimationStarted");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectPostLockAnimationFinished() {
SCOPED_TRACE("Failure in ExpectPostLockAnimationFinished");
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN));
}
void ExpectUnlockBeforeUIDestroyedAnimationStarted() {
SCOPED_TRACE("Failure in ExpectUnlockBeforeUIDestroyedAnimationStarted");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_LIFT));
}
void ExpectUnlockBeforeUIDestroyedAnimationFinished() {
SCOPED_TRACE("Failure in ExpectUnlockBeforeUIDestroyedAnimationFinished");
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_LIFT));
}
void ExpectUnlockAfterUIDestroyedAnimationStarted() {
SCOPED_TRACE("Failure in ExpectUnlockAfterUIDestroyedAnimationStarted");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_DROP));
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectUnlockAfterUIDestroyedAnimationFinished() {
SCOPED_TRACE("Failure in ExpectUnlockAfterUIDestroyedAnimationFinished");
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_DROP));
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::SHELF, SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectShutdownAnimationStarted() {
SCOPED_TRACE("Failure in ExpectShutdownAnimationStarted");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::ROOT_CONTAINER,
SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS));
}
void ExpectShutdownAnimationFinished() {
SCOPED_TRACE("Failure in ExpectShutdownAnimationFinished");
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
EXPECT_FALSE(test_animator_->AreContainersAnimated(
SessionStateAnimator::ROOT_CONTAINER,
SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS));
}
void ExpectShutdownAnimationCancel() {
SCOPED_TRACE("Failure in ExpectShutdownAnimationCancel");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::ROOT_CONTAINER,
SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS));
}
void ExpectWallpaperIsShowing() {
SCOPED_TRACE("Failure in ExpectWallpaperIsShowing");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::WALLPAPER,
SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectWallpaperIsHiding() {
SCOPED_TRACE("Failure in ExpectWallpaperIsHiding");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::WALLPAPER,
SessionStateAnimator::ANIMATION_FADE_OUT));
}
void ExpectRestoringWallpaperVisibility() {
SCOPED_TRACE("Failure in ExpectRestoringWallpaperVisibility");
EXPECT_LT(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::WALLPAPER,
SessionStateAnimator::ANIMATION_FADE_IN));
}
void ExpectUnlockedState() {
SCOPED_TRACE("Failure in ExpectUnlockedState");
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
EXPECT_FALSE(Shell::Get()->session_controller()->IsScreenLocked());
}
void ExpectLockedState() {
SCOPED_TRACE("Failure in ExpectLockedState");
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
EXPECT_TRUE(Shell::Get()->session_controller()->IsScreenLocked());
}
void HideWallpaper() { test_animator_->HideWallpaper(); }
void PressLockButton() {
power_button_controller_->OnLockButtonEvent(true, base::TimeTicks::Now());
}
void ReleaseLockButton() {
power_button_controller_->OnLockButtonEvent(false, base::TimeTicks::Now());
}
void SuccessfulAuthentication(bool* call_flag) {
base::Closure closure = base::Bind(&CheckCalledCallback, call_flag);
lock_state_controller_->OnLockScreenHide(closure);
}
// Simulate that shutdown sound duration callback is done.
void ShutdownSoundPlayed() { a11y_controller_->FlushMojoForTest(); }
TestShutdownController test_shutdown_controller_;
TestSessionStateAnimator* test_animator_ = nullptr; // not owned
AccessibilityController* a11y_controller_;
TestAccessibilityControllerClient a11y_client_;
private:
DISALLOW_COPY_AND_ASSIGN(LockStateControllerTest);
};
// Test the lock-to-shutdown flow for non-Chrome-OS hardware that doesn't
// correctly report power button releases. We should lock immediately the first
// time the button is pressed and shut down when it's pressed from the locked
// state.
TEST_F(LockStateControllerTest, LegacyLockAndShutDown) {
Initialize(ButtonType::LEGACY, LoginStatus::USER);
ExpectUnlockedState();
// We should request that the screen be locked immediately after seeing the
// power button get pressed.
PressPowerButton();
EXPECT_FALSE(lock_state_test_api_->is_lock_cancellable());
ExpectPreLockAnimationStarted();
test_animator_->CompleteAllAnimations(true);
ExpectPreLockAnimationFinished();
EXPECT_EQ(1, session_manager_client_->request_lock_screen_call_count());
// Notify that we locked successfully.
lock_state_controller_->OnStartingLock();
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
LockScreen();
ExpectPostLockAnimationStarted();
test_animator_->CompleteAllAnimations(true);
ExpectPostLockAnimationFinished();
// We shouldn't progress towards the shutdown state, however.
EXPECT_FALSE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running());
ReleasePowerButton();
// Hold the button again and check that we start shutting down.
PressPowerButton();
ExpectShutdownAnimationStarted();
EXPECT_EQ(0, NumShutdownRequests());
// Make sure a mouse move event won't show the cursor.
GenerateMouseMoveEvent();
// TODO: CursorManager not created in mash. http://crbug.com/631103.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_FALSE(cursor_visible());
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
lock_state_test_api_->trigger_real_shutdown_timeout();
EXPECT_EQ(1, NumShutdownRequests());
}
// Test that we start shutting down immediately if the power button is pressed
// while we're not logged in on an unofficial system.
TEST_F(LockStateControllerTest, LegacyNotLoggedIn) {
Initialize(ButtonType::LEGACY, LoginStatus::NOT_LOGGED_IN);
PressPowerButton();
ExpectShutdownAnimationStarted();
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
}
// Test that we start shutting down immediately if the power button is pressed
// while we're logged in as a guest on an unofficial system.
TEST_F(LockStateControllerTest, LegacyGuest) {
Initialize(ButtonType::LEGACY, LoginStatus::GUEST);
PressPowerButton();
ExpectShutdownAnimationStarted();
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
}
// When we hold the power button while the user isn't logged in, we should shut
// down the machine directly.
TEST_F(LockStateControllerTest, ShutdownWhenNotLoggedIn) {
Initialize(ButtonType::NORMAL, LoginStatus::NOT_LOGGED_IN);
// Press the power button and check that we start the shutdown timer.
PressPowerButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running());
ExpectShutdownAnimationStarted();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN, 0.5f);
// Release the power button before the shutdown timer fires.
ReleasePowerButton();
EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running());
ExpectShutdownAnimationCancel();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_REVERT, 0.5f);
// Press the button again and make the shutdown timeout fire this time.
// Check that we start the timer for actually requesting the shutdown.
PressPowerButton();
EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running());
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
ExpectShutdownAnimationFinished();
lock_state_test_api_->trigger_shutdown_timeout();
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
EXPECT_EQ(0, NumShutdownRequests());
// When the timout fires, we should request a shutdown.
lock_state_test_api_->trigger_real_shutdown_timeout();
EXPECT_EQ(1, NumShutdownRequests());
}
// Test that we lock the screen and deal with unlocking correctly.
TEST_F(LockStateControllerTest, LockAndUnlock) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
ExpectUnlockedState();
// Press the power button and check that the lock timer is started and that we
// start lifting the non-screen-locker containers.
PressPowerButton();
ExpectPreLockAnimationStarted();
EXPECT_TRUE(lock_state_test_api_->is_lock_cancellable());
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
test_animator_->CompleteAllAnimations(true);
ExpectPreLockAnimationFinished();
EXPECT_EQ(1, session_manager_client_->request_lock_screen_call_count());
// Notify that we locked successfully.
lock_state_controller_->OnStartingLock();
// We had that animation already.
EXPECT_EQ(0u, test_animator_->GetAnimationCount());
LockScreen();
ExpectPostLockAnimationStarted();
test_animator_->CompleteAllAnimations(true);
ExpectPostLockAnimationFinished();
// When we release the power button, the lock-to-shutdown timer should be
// stopped.
ExpectLockedState();
EXPECT_TRUE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
ReleasePowerButton();
ExpectLockedState();
EXPECT_FALSE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
// The backlights shouldn't be forced off when clamshell power button behavior
// isn't explicitly requested.
EXPECT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
// Notify that the screen has been unlocked. We should show the
// non-screen-locker windows.
bool called = false;
SuccessfulAuthentication(&called);
ExpectUnlockBeforeUIDestroyedAnimationStarted();
EXPECT_FALSE(called);
test_animator_->CompleteAllAnimations(true);
ExpectUnlockBeforeUIDestroyedAnimationFinished();
EXPECT_TRUE(called);
UnlockScreen();
ExpectUnlockAfterUIDestroyedAnimationStarted();
test_animator_->CompleteAllAnimations(true);
ExpectUnlockAfterUIDestroyedAnimationFinished();
ExpectUnlockedState();
}
// Test that we deal with cancelling lock correctly.
TEST_F(LockStateControllerTest, LockAndCancel) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
ExpectUnlockedState();
// Press the power button and check that the lock timer is started and that we
// start lifting the non-screen-locker containers.
PressPowerButton();
ExpectPreLockAnimationStarted();
EXPECT_TRUE(lock_state_test_api_->is_lock_cancellable());
// forward only half way through
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.5f);
// Release the button before the lock timer fires.
ReleasePowerButton();
ExpectPreLockAnimationCancel();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectUnlockedState();
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
}
// Test that we deal with cancelling lock correctly.
TEST_F(LockStateControllerTest, LockAndCancelAndLockAgain) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
ExpectUnlockedState();
// Press the power button and check that the lock timer is started and that we
// start lifting the non-screen-locker containers.
PressPowerButton();
ExpectPreLockAnimationStarted();
EXPECT_TRUE(lock_state_test_api_->is_lock_cancellable());
// forward only half way through
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.5f);
// Release the button before the lock timer fires.
ReleasePowerButton();
ExpectPreLockAnimationCancel();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
0.5f);
PressPowerButton();
ExpectPreLockAnimationStarted();
EXPECT_TRUE(lock_state_test_api_->is_lock_cancellable());
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.6f);
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.6f);
ExpectPreLockAnimationFinished();
EXPECT_EQ(1, session_manager_client_->request_lock_screen_call_count());
}
// Hold the power button down from the unlocked state to eventual shutdown.
TEST_F(LockStateControllerTest, LockToShutdown) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// Hold the power button and lock the screen.
PressPowerButton();
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
LockScreen();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
// When the lock-to-shutdown timeout fires, we should start the shutdown
// timer.
EXPECT_TRUE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
lock_state_test_api_->trigger_lock_to_shutdown_timeout();
ExpectShutdownAnimationStarted();
EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running());
// Fire the shutdown timeout and check that we request shutdown.
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
ExpectShutdownAnimationFinished();
lock_state_test_api_->trigger_shutdown_timeout();
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
EXPECT_EQ(0, NumShutdownRequests());
lock_state_test_api_->trigger_real_shutdown_timeout();
EXPECT_EQ(1, NumShutdownRequests());
}
// Hold the power button down from the unlocked state to eventual shutdown,
// then release the button while system does locking.
TEST_F(LockStateControllerTest, CancelLockToShutdown) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
PressPowerButton();
// Hold the power button and lock the screen.
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
LockScreen();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS, 0.5f);
// Power button is released while system attempts to lock.
ReleasePowerButton();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
EXPECT_FALSE(lock_state_controller_->ShutdownRequested());
EXPECT_FALSE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running());
}
// Test that we handle the case where lock requests are ignored.
TEST_F(LockStateControllerTest, Lock) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// Hold the power button and lock the screen.
PressPowerButton();
ExpectPreLockAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
EXPECT_EQ(1, session_manager_client_->request_lock_screen_call_count());
EXPECT_TRUE(lock_state_test_api_->lock_fail_timer_is_running());
// We shouldn't start the lock-to-shutdown timer until the screen has actually
// been locked and this was animated.
EXPECT_FALSE(lock_state_test_api_->lock_to_shutdown_timer_is_running());
// Act as if the request timed out.
EXPECT_DEATH(lock_state_test_api_->trigger_lock_fail_timeout(), "");
}
// Test the basic operation of the lock button (not logged in).
TEST_F(LockStateControllerTest, LockButtonBasicNotLoggedIn) {
// The lock button shouldn't do anything if we aren't logged in.
Initialize(ButtonType::NORMAL, LoginStatus::NOT_LOGGED_IN);
PressLockButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
ReleaseLockButton();
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
}
// Test the basic operation of the lock button (guest).
TEST_F(LockStateControllerTest, LockButtonBasicGuest) {
// The lock button shouldn't do anything when we're logged in as a guest.
Initialize(ButtonType::NORMAL, LoginStatus::GUEST);
PressLockButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
ReleaseLockButton();
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
}
// Test the basic operation of the lock button.
TEST_F(LockStateControllerTest, LockButtonBasic) {
// If we're logged in as a regular user, we should start the lock timer and
// the pre-lock animation.
Initialize(ButtonType::NORMAL, LoginStatus::USER);
PressLockButton();
ExpectPreLockAnimationStarted();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.5f);
// If the button is released immediately, we shouldn't lock the screen.
ReleaseLockButton();
ExpectPreLockAnimationCancel();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectUnlockedState();
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
// Press the button again and let the lock timeout fire. We should request
// that the screen be locked.
PressLockButton();
ExpectPreLockAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
EXPECT_EQ(1, session_manager_client_->request_lock_screen_call_count());
// Pressing the lock button while we have a pending lock request shouldn't do
// anything.
ReleaseLockButton();
PressLockButton();
ExpectPreLockAnimationFinished();
ReleaseLockButton();
// Pressing the button also shouldn't do anything after the screen is locked.
LockScreen();
ExpectPostLockAnimationStarted();
PressLockButton();
ReleaseLockButton();
ExpectPostLockAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectPostLockAnimationFinished();
PressLockButton();
ReleaseLockButton();
ExpectPostLockAnimationFinished();
}
// Test that the power button takes priority over the lock button.
TEST_F(LockStateControllerTest, PowerButtonPreemptsLockButton) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// While the lock button is down, hold the power button.
PressLockButton();
ExpectPreLockAnimationStarted();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.1f);
ExpectPreLockAnimationRunning();
PressPowerButton();
ExpectPreLockAnimationRunning();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.1f);
ExpectPreLockAnimationRunning();
// The lock timer shouldn't be stopped when the lock button is released.
ReleaseLockButton();
ExpectPreLockAnimationRunning();
ReleasePowerButton();
ExpectPreLockAnimationCancel();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectUnlockedState();
// Now press the power button first and then the lock button.
PressPowerButton();
ExpectPreLockAnimationStarted();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.1f);
PressLockButton();
ExpectPreLockAnimationRunning();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.1f);
// Releasing the power button should stop the lock timer.
ReleasePowerButton();
ExpectPreLockAnimationCancel();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.1f);
ReleaseLockButton();
ExpectPreLockAnimationCancel();
}
// When the screen is locked without going through the usual power-button
// slow-close path (e.g. via the wrench menu), test that we still show the
// fast-close animation.
TEST_F(LockStateControllerTest, LockWithoutButton) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
lock_state_controller_->OnStartingLock();
ExpectPreLockAnimationStarted();
EXPECT_FALSE(lock_state_test_api_->is_lock_cancellable());
EXPECT_LT(0u, test_animator_->GetAnimationCount());
test_animator_->CompleteAllAnimations(true);
EXPECT_EQ(0, session_manager_client_->request_lock_screen_call_count());
}
// When we hear that the process is exiting but we haven't had a chance to
// display an animation, we should just blank the screen.
TEST_F(LockStateControllerTest, ShutdownWithoutButton) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
lock_state_controller_->OnChromeTerminating();
EXPECT_TRUE(test_animator_->AreContainersAnimated(
SessionStateAnimator::kAllNonRootContainersMask,
SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY));
GenerateMouseMoveEvent();
// TODO: CursorManager not created in mash. http://crbug.com/631103.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_FALSE(cursor_visible());
}
// Test that we display the fast-close animation and shut down when we get an
// outside request to shut down (e.g. from the login or lock screen).
TEST_F(LockStateControllerTest, RequestShutdownFromLoginScreen) {
Initialize(ButtonType::NORMAL, LoginStatus::NOT_LOGGED_IN);
lock_state_controller_->RequestShutdown(
ShutdownReason::LOGIN_SHUT_DOWN_BUTTON);
ExpectShutdownAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
GenerateMouseMoveEvent();
// TODO: CursorManager not created in mash. http://crbug.com/631103.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_FALSE(cursor_visible());
EXPECT_EQ(0, NumShutdownRequests());
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
lock_state_test_api_->trigger_real_shutdown_timeout();
EXPECT_EQ(1, NumShutdownRequests());
}
TEST_F(LockStateControllerTest, RequestShutdownFromLockScreen) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
LockScreen();
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
ExpectPostLockAnimationFinished();
lock_state_controller_->RequestShutdown(
ShutdownReason::LOGIN_SHUT_DOWN_BUTTON);
ExpectShutdownAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
GenerateMouseMoveEvent();
// TODO: CursorManager not created in mash. http://crbug.com/631103.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_FALSE(cursor_visible());
EXPECT_EQ(0, NumShutdownRequests());
ShutdownSoundPlayed();
EXPECT_TRUE(lock_state_test_api_->real_shutdown_timer_is_running());
lock_state_test_api_->trigger_real_shutdown_timeout();
EXPECT_EQ(1, NumShutdownRequests());
}
TEST_F(LockStateControllerTest, RequestAndCancelShutdownFromLockScreen) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
LockScreen();
Advance(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
ExpectLockedState();
// Press the power button and check that we start the shutdown timer.
PressPowerButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running());
ExpectShutdownAnimationStarted();
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN, 0.5f);
// Release the power button before the shutdown timer fires.
ReleasePowerButton();
EXPECT_FALSE(lock_state_test_api_->shutdown_timer_is_running());
ExpectShutdownAnimationCancel();
Advance(SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN);
ExpectLockedState();
}
// Test that we ignore power button presses when the screen is turned off.
TEST_F(LockStateControllerTest, IgnorePowerButtonIfScreenIsOff) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// When the screen brightness is at 0%, we shouldn't do anything in response
// to power button presses.
power_manager_client_->SendBrightnessChanged(0, true);
PressPowerButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
ReleasePowerButton();
// After increasing the brightness to 10%, we should start the timer like
// usual.
power_manager_client_->SendBrightnessChanged(10, true);
PressPowerButton();
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
ReleasePowerButton();
}
TEST_F(LockStateControllerTest, HonorPowerButtonInDockedMode) {
// Create two outputs, the first internal and the second external.
display::DisplayConfigurator::DisplayStateList outputs;
std::unique_ptr<display::DisplaySnapshot> internal_display =
display::FakeDisplaySnapshot::Builder()
.SetId(123)
.SetNativeMode(gfx::Size(1, 1))
.SetType(display::DISPLAY_CONNECTION_TYPE_INTERNAL)
.Build();
outputs.push_back(internal_display.get());
std::unique_ptr<display::DisplaySnapshot> external_display =
display::FakeDisplaySnapshot::Builder()
.SetId(456)
.SetNativeMode(gfx::Size(1, 1))
.SetType(display::DISPLAY_CONNECTION_TYPE_HDMI)
.Build();
outputs.push_back(external_display.get());
// When all of the displays are turned off (e.g. due to user inactivity), the
// power button should be ignored.
power_manager_client_->SendBrightnessChanged(0, true);
internal_display->set_current_mode(nullptr);
external_display->set_current_mode(nullptr);
power_button_controller_->OnDisplayModeChanged(outputs);
PressPowerButton();
EXPECT_FALSE(lock_state_test_api_->is_animating_lock());
ReleasePowerButton();
// When the screen brightness is 0% but the external display is still turned
// on (indicating either docked mode or the user having manually decreased the
// brightness to 0%), the power button should still be handled.
external_display->set_current_mode(external_display->modes().back().get());
power_button_controller_->OnDisplayModeChanged(outputs);
PressPowerButton();
EXPECT_TRUE(lock_state_test_api_->is_animating_lock());
ReleasePowerButton();
}
// Test that hidden wallpaper appears and revers correctly on lock/cancel.
TEST_F(LockStateControllerTest, TestHiddenWallpaperLockCancel) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
HideWallpaper();
ExpectUnlockedState();
PressPowerButton();
ExpectPreLockAnimationStarted();
ExpectWallpaperIsShowing();
// Forward only half way through.
AdvancePartially(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, 0.5f);
// Release the button before the lock timer fires.
ReleasePowerButton();
ExpectPreLockAnimationCancel();
ExpectWallpaperIsHiding();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS);
// When the CancelPrelockAnimation sequence finishes it queues up a
// restore wallpaper visibility sequence when the wallpaper is hidden.
ExpectRestoringWallpaperVisibility();
Advance(SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
ExpectUnlockedState();
}
// Test that hidden wallpaper appears and revers correctly on lock/unlock.
TEST_F(LockStateControllerTest, TestHiddenWallpaperLockUnlock) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
HideWallpaper();
ExpectUnlockedState();
// Press the power button and check that the lock timer is started and that we
// start lifting the non-screen-locker containers.
PressPowerButton();
ExpectPreLockAnimationStarted();
ExpectWallpaperIsShowing();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
ExpectPreLockAnimationFinished();
LockScreen();
ReleasePowerButton();
ExpectPostLockAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectPostLockAnimationFinished();
ExpectLockedState();
SuccessfulAuthentication(NULL);
ExpectUnlockBeforeUIDestroyedAnimationStarted();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
ExpectUnlockBeforeUIDestroyedAnimationFinished();
UnlockScreen();
ExpectUnlockAfterUIDestroyedAnimationStarted();
ExpectWallpaperIsHiding();
Advance(SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
// When the StartUnlockAnimationAfterUIDestroyed sequence finishes it queues
// up a restore wallpaper visibility sequence when the wallpaper is hidden.
ExpectRestoringWallpaperVisibility();
Advance(SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
ExpectUnlockAfterUIDestroyedAnimationFinished();
ExpectUnlockedState();
}
// Test that backlights are forced off shortly after the screen is locked when
// clamshell-style power button behavior is forced.
TEST_F(LockStateControllerTest, ClamshellDisplayOffAfterLock) {
ForceClamshellPowerButton();
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// If the power button isn't held long enough for the screen to be locked, the
// backlights shouldn't be forced off.
PressPowerButton();
ReleasePowerButton();
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
EXPECT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
// Now hold the power button long enough to lock the screen.
PressPowerButton();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
LockScreen();
EXPECT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
// After releasing the power button, the display should still be on, but it
// should be forced off after the display-off timer fires.
ReleasePowerButton();
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
EXPECT_TRUE(power_button_controller_->TriggerDisplayOffTimerForTesting());
EXPECT_TRUE(power_manager_client_->backlights_forced_off());
// Pressing the power button should turn the display back on.
PressPowerButton();
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
ReleasePowerButton();
// Now that the screen is already locked, the timer shouldn't be started if
// the power button is pressed and released again.
PressPowerButton();
ReleasePowerButton();
ASSERT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
ASSERT_FALSE(power_manager_client_->backlights_forced_off());
}
// Test that user activity prevents backlights from being forced off after the
// screen is locked.
TEST_F(LockStateControllerTest, CancelClamshellDisplayOffAfterLock) {
ForceClamshellPowerButton();
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// If a key is pressed shortly after locking, the display-off timer should be
// stopped.
PressPowerButton();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
LockScreen();
ReleasePowerButton();
PressKey(ui::VKEY_A);
ReleaseKey(ui::VKEY_A);
EXPECT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
UnlockScreen();
// Mouse events should also stop the timer.
PressPowerButton();
Advance(SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
LockScreen();
ReleasePowerButton();
GenerateMouseMoveEvent();
EXPECT_FALSE(power_button_controller_->TriggerDisplayOffTimerForTesting());
EXPECT_FALSE(power_manager_client_->backlights_forced_off());
UnlockScreen();
}
// Tests the default behavior of disabling the touchscreen when the screen is
// turned off due to user inactivity.
TEST_F(LockStateControllerTest, DisableTouchscreenForScreenOff) {
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// Run the event loop so PowerButtonDisplayController will get the initial
// backlights-forced-off state from chromeos::PowerManagerClient.
base::RunLoop().RunUntilIdle();
// Manually turn the screen off and check that the touchscreen is enabled.
power_manager_client_->SendBrightnessChanged(0, true /* user_initiated */);
EXPECT_TRUE(Shell::Get()->touch_devices_controller()->GetTouchscreenEnabled(
TouchscreenEnabledSource::GLOBAL));
// It should be disabled if the screen is turned off due to user inactivity.
power_manager_client_->SendBrightnessChanged(100, true /* user_initiated */);
power_manager_client_->SendBrightnessChanged(0, false /* user_initiated */);
EXPECT_FALSE(Shell::Get()->touch_devices_controller()->GetTouchscreenEnabled(
TouchscreenEnabledSource::GLOBAL));
}
// Tests that the kTouchscreenUsableWhileScreenOff switch keeps the touchscreen
// enabled when the screen is turned off due to user inactivity.
TEST_F(LockStateControllerTest, TouchscreenUnableWhileScreenOff) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kTouchscreenUsableWhileScreenOff);
ResetPowerButtonController();
Initialize(ButtonType::NORMAL, LoginStatus::USER);
// Run the event loop so PowerButtonDisplayController will get the initial
// backlights-forced-off state from chromeos::PowerManagerClient.
base::RunLoop().RunUntilIdle();
// The touchscreen should remain enabled.
power_manager_client_->SendBrightnessChanged(0, false /* user_initiated */);
EXPECT_TRUE(Shell::Get()->touch_devices_controller()->GetTouchscreenEnabled(
TouchscreenEnabledSource::GLOBAL));
}
} // namespace ash