diff options
author | antrim@chromium.org <antrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-26 22:29:07 +0000 |
---|---|---|
committer | antrim@chromium.org <antrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-26 22:29:07 +0000 |
commit | 88223751105adcb476b69e1e0f9ce8eeb50b98fa (patch) | |
tree | 7b31da8da358d483128a45f2851086df58b8b658 /ash | |
parent | fca480623de212e5412708c459f123d175b754cc (diff) | |
download | chromium_src-88223751105adcb476b69e1e0f9ce8eeb50b98fa.zip chromium_src-88223751105adcb476b69e1e0f9ce8eeb50b98fa.tar.gz chromium_src-88223751105adcb476b69e1e0f9ce8eeb50b98fa.tar.bz2 |
Add tests for new lock/shutdown animations before flipping switch on the ToT.
Introduce test for new SessionStateController implementation (mostly the copy of the old PowerButtonController test).
Better tests will follow (test PowerButtonController and SessionStateController separately).
BUG=138171,139461
Review URL: https://chromiumcodereview.appspot.com/11348224
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169507 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 1 | ||||
-rw-r--r-- | ash/wm/session_state_animator.cc | 106 | ||||
-rw-r--r-- | ash/wm/session_state_animator.h | 4 | ||||
-rw-r--r-- | ash/wm/session_state_controller.h | 2 | ||||
-rw-r--r-- | ash/wm/session_state_controller_impl2.h | 4 | ||||
-rw-r--r-- | ash/wm/session_state_controller_impl2_unittest.cc | 617 |
6 files changed, 701 insertions, 33 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index a89f96b..53cdc29 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -561,6 +561,7 @@ 'wm/panel_layout_manager_unittest.cc', 'wm/power_button_controller_unittest.cc', 'wm/screen_dimmer_unittest.cc', + 'wm/session_state_controller_impl2_unittest.cc', 'wm/shelf_layout_manager_unittest.cc', 'wm/system_gesture_event_filter_unittest.cc', 'wm/system_modal_container_layout_manager_unittest.cc', diff --git a/ash/wm/session_state_animator.cc b/ash/wm/session_state_animator.cc index 12ddf33..b082c98 100644 --- a/ash/wm/session_state_animator.cc +++ b/ash/wm/session_state_animator.cc @@ -254,6 +254,72 @@ class CallbackAnimationObserver : public ui::LayerAnimationObserver { DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); }; + +bool IsLayerAnimated(ui::Layer* layer, + SessionStateAnimator::AnimationType type) { + switch (type) { + case SessionStateAnimator::ANIMATION_PARTIAL_CLOSE: + if (layer->GetTargetTransform() != GetSlowCloseTransform()) + return false; + break; + case SessionStateAnimator::ANIMATION_UNDO_PARTIAL_CLOSE: + if (layer->GetTargetTransform() != gfx::Transform()) + return false; + break; + case SessionStateAnimator::ANIMATION_FULL_CLOSE: + if (layer->GetTargetTransform() != GetFastCloseTransform() || + layer->GetTargetOpacity() > 0.0001) + return false; + break; + case SessionStateAnimator::ANIMATION_FADE_IN: + if (layer->GetTargetOpacity() < 0.9999) + return false; + break; + case SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY: + if (layer->GetTargetOpacity() > 0.0001) + return false; + break; + case SessionStateAnimator::ANIMATION_RESTORE: + if (layer->opacity() < 0.9999 || layer->transform() != gfx::Transform()) + return false; + break; + case SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS: + if ((layer->GetTargetBrightness() < 0.9999) || + (layer->GetTargetGrayscale() < 0.9999)) + return false; + break; + case SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS: + if ((layer->GetTargetBrightness() > 0.0001) || + (layer->GetTargetGrayscale() > 0.0001)) + return false; + break; + case SessionStateAnimator::ANIMATION_DROP: + //ToDo(antim) : check other effects + if (layer->GetTargetOpacity() < 0.9999) + return false; + break; + //ToDo(antim) : check other effects + case SessionStateAnimator::ANIMATION_LIFT: + if (layer->GetTargetOpacity() > 0.0001) + return false; + break; + case SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN: + //ToDo(antim) : check other effects + if (layer->GetTargetOpacity() < 0.9999) + return false; + break; + //ToDo(antim) : check other effects + case SessionStateAnimator::ANIMATION_LOWER_BELOW_SCREEN: + if (layer->GetTargetOpacity() > 0.0001) + return false; + break; + default: + NOTREACHED() << "Unhandled animation type " << type; + return false; + } + return true; +} + } // namespace bool SessionStateAnimator::TestApi::ContainersAreAnimated( @@ -264,41 +330,19 @@ bool SessionStateAnimator::TestApi::ContainersAreAnimated( it != containers.end(); ++it) { aura::Window* window = *it; ui::Layer* layer = window->layer(); - - switch (type) { - case ANIMATION_PARTIAL_CLOSE: - if (layer->GetTargetTransform() != GetSlowCloseTransform()) - return false; - break; - case ANIMATION_UNDO_PARTIAL_CLOSE: - if (layer->GetTargetTransform() != gfx::Transform()) - return false; - break; - case ANIMATION_FULL_CLOSE: - if (layer->GetTargetTransform() != GetFastCloseTransform() || - layer->GetTargetOpacity() > 0.0001) - return false; - break; - case ANIMATION_FADE_IN: - if (layer->GetTargetOpacity() < 0.9999) - return false; - break; - case ANIMATION_HIDE_IMMEDIATELY: - if (layer->GetTargetOpacity() > 0.0001) - return false; - break; - case ANIMATION_RESTORE: - if (layer->opacity() < 0.9999 || layer->transform() != gfx::Transform()) - return false; - break; - default: - NOTREACHED() << "Unhandled animation type " << type; - return false; - } + if (!IsLayerAnimated(layer, type)) + return false; } return true; } +bool SessionStateAnimator::TestApi::RootWindowIsAnimated(AnimationType type) + const { + aura::RootWindow* root_window = Shell::GetPrimaryRootWindow(); + ui::Layer* layer = root_window->layer(); + return IsLayerAnimated(layer, type); +} + const int SessionStateAnimator::kAllLockScreenContainersMask = SessionStateAnimator::LOCK_SCREEN_BACKGROUND | SessionStateAnimator::LOCK_SCREEN_CONTAINERS | diff --git a/ash/wm/session_state_animator.h b/ash/wm/session_state_animator.h index e46e2df..ed6ea45 100644 --- a/ash/wm/session_state_animator.h +++ b/ash/wm/session_state_animator.h @@ -110,6 +110,10 @@ class ASH_EXPORT SessionStateAnimator { // |container_mask| is a bitfield of a Container. bool ContainersAreAnimated(int container_mask, AnimationType type) const; + // Returns true if root window was last animated with |type| (probably; + // the analysis is fairly ad-hoc). + bool RootWindowIsAnimated(AnimationType type) const; + private: SessionStateAnimator* animator_; // not owned diff --git a/ash/wm/session_state_controller.h b/ash/wm/session_state_controller.h index 648e923..c457c70 100644 --- a/ash/wm/session_state_controller.h +++ b/ash/wm/session_state_controller.h @@ -29,6 +29,7 @@ namespace ash { namespace test { class PowerButtonControllerTest; +class SessionStateControllerImpl2Test; } // Performs system-related functions on behalf of SessionStateController. @@ -128,6 +129,7 @@ class ASH_EXPORT SessionStateController : public aura::RootWindowObserver, protected: friend class test::PowerButtonControllerTest; + friend class test::SessionStateControllerImpl2Test; scoped_ptr<internal::SessionStateAnimator> animator_; diff --git a/ash/wm/session_state_controller_impl2.h b/ash/wm/session_state_controller_impl2.h index f8535fb..d554679 100644 --- a/ash/wm/session_state_controller_impl2.h +++ b/ash/wm/session_state_controller_impl2.h @@ -27,7 +27,7 @@ class Layer; namespace ash { namespace test { -class PowerButtonControllerTest; +class SessionStateControllerImpl2Test; } // Displays onscreen animations and locks or suspends the system in response to @@ -118,7 +118,7 @@ class ASH_EXPORT SessionStateControllerImpl2 : public SessionStateController { virtual void SetLockScreenDisplayedCallback(base::Closure& callback) OVERRIDE; protected: - friend class test::PowerButtonControllerTest; + friend class test::SessionStateControllerImpl2Test; private: void RequestShutdownImpl(); diff --git a/ash/wm/session_state_controller_impl2_unittest.cc b/ash/wm/session_state_controller_impl2_unittest.cc new file mode 100644 index 0000000..9a2cd0d --- /dev/null +++ b/ash/wm/session_state_controller_impl2_unittest.cc @@ -0,0 +1,617 @@ +// Copyright (c) 2012 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/session_state_controller_impl2.h" + +#include "ash/ash_switches.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/test_shell_delegate.h" +#include "ash/wm/cursor_manager.h" +#include "ash/wm/power_button_controller.h" +#include "ash/wm/session_state_animator.h" +#include "ash/wm/session_state_controller.h" +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/time.h" +#include "ui/aura/env.h" +#include "ui/aura/root_window.h" +#include "ui/aura/test/event_generator.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" + +namespace ash { +namespace test { +namespace { +bool cursor_visible() { + return ash::Shell::GetInstance()->cursor_manager()->cursor_visible(); +} + +void CheckCalledCallback(bool* flag) { + (*flag) = true; +} +} + +// Fake implementation of PowerButtonControllerDelegate that just logs requests +// to lock the screen and shut down the device. +class TestSessionStateControllerDelegate : + public SessionStateControllerDelegate { + public: + TestSessionStateControllerDelegate() + : num_lock_requests_(0), + num_shutdown_requests_(0) {} + + int num_lock_requests() const { return num_lock_requests_; } + int num_shutdown_requests() const { return num_shutdown_requests_; } + + // SessionStateControllerDelegate implementation. + virtual void RequestLockScreen() OVERRIDE { + num_lock_requests_++; + } + virtual void RequestShutdown() OVERRIDE { + num_shutdown_requests_++; + } + + private: + int num_lock_requests_; + int num_shutdown_requests_; + + DISALLOW_COPY_AND_ASSIGN(TestSessionStateControllerDelegate); +}; + +class SessionStateControllerImpl2Test : public AshTestBase { + public: + SessionStateControllerImpl2Test() : controller_(NULL), delegate_(NULL) {} + virtual ~SessionStateControllerImpl2Test() {} + + void SetUp() OVERRIDE { + CommandLine::ForCurrentProcess()->AppendSwitch( + ash::switches::kAshNewLockAnimationsEnabled); + + AshTestBase::SetUp(); + delegate_ = new TestSessionStateControllerDelegate; + controller_ = Shell::GetInstance()->power_button_controller(); + state_controller_ = static_cast<SessionStateControllerImpl2*>( + Shell::GetInstance()->session_state_controller()); + state_controller_->SetDelegate(delegate_); // transfers ownership + test_api_.reset( + new SessionStateControllerImpl2::TestApi(state_controller_)); + animator_api_.reset( + new internal::SessionStateAnimator::TestApi(state_controller_-> + animator_.get())); + shell_delegate_ = reinterpret_cast<TestShellDelegate*>( + ash::Shell::GetInstance()->delegate()); + } + + protected: + void GenerateMouseMoveEvent() { + aura::test::EventGenerator generator( + Shell::GetPrimaryRootWindow()); + generator.MoveMouseTo(10, 10); + } + + int NumShutdownRequests() { + return delegate_->num_shutdown_requests() + + shell_delegate_->num_exit_requests(); + } + + PowerButtonController* controller_; // not owned + SessionStateControllerImpl2* state_controller_; // not owned + TestSessionStateControllerDelegate* delegate_; // not owned + TestShellDelegate* shell_delegate_; // not owned + + scoped_ptr<SessionStateControllerImpl2::TestApi> test_api_; + scoped_ptr<internal::SessionStateAnimator::TestApi> animator_api_; + + private: + DISALLOW_COPY_AND_ASSIGN(SessionStateControllerImpl2Test); +}; + +// 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(SessionStateControllerImpl2Test, LegacyLockAndShutDown) { + controller_->set_has_legacy_power_button_for_test(true); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // We should request that the screen be locked immediately after seeing the + // power button get pressed. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + + EXPECT_FALSE(test_api_->lock_timer_is_running()); + EXPECT_EQ(1, delegate_->num_lock_requests()); + + // Notify that we locked successfully. + state_controller_->OnStartingLock(); + + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY)); + + + // Notify that the lock window is visible. We should make it fade in. + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN)); + + // We shouldn't progress towards the shutdown state, however. + EXPECT_FALSE(test_api_->lock_to_shutdown_timer_is_running()); + EXPECT_FALSE(test_api_->shutdown_timer_is_running()); + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + + // Hold the button again and check that we start shutting down. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_EQ(0, NumShutdownRequests()); + + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + // Make sure a mouse move event won't show the cursor. + GenerateMouseMoveEvent(); + EXPECT_FALSE(cursor_visible()); + EXPECT_TRUE(test_api_->real_shutdown_timer_is_running()); + 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(SessionStateControllerImpl2Test, LegacyNotLoggedIn) { + controller_->set_has_legacy_power_button_for_test(true); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_NONE); + state_controller_->OnLockStateChanged(false); + SetUserLoggedIn(false); + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(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(SessionStateControllerImpl2Test, LegacyGuest) { + controller_->set_has_legacy_power_button_for_test(true); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_GUEST); + state_controller_->OnLockStateChanged(false); + SetCanLockScreen(false); + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(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(SessionStateControllerImpl2Test, ShutdownWhenNotLoggedIn) { + controller_->set_has_legacy_power_button_for_test(false); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_NONE); + state_controller_->OnLockStateChanged(false); + SetUserLoggedIn(false); + + // Press the power button and check that we start the shutdown timer. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + EXPECT_TRUE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + + // Release the power button before the shutdown timer fires. + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS)); + + // Press the button again and make the shutdown timeout fire this time. + // Check that we start the timer for actually requesting the shutdown. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->shutdown_timer_is_running()); + test_api_->trigger_shutdown_timeout(); + EXPECT_TRUE(test_api_->real_shutdown_timer_is_running()); + EXPECT_EQ(0, NumShutdownRequests()); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + + // When the timout fires, we should request a shutdown. + test_api_->trigger_real_shutdown_timeout(); + EXPECT_EQ(1, NumShutdownRequests()); +} + +// Test that we lock the screen and deal with unlocking correctly. +TEST_F(SessionStateControllerImpl2Test, LockAndUnlock) { + controller_->set_has_legacy_power_button_for_test(false); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // We should initially be showing the screen locker containers, since they + // also contain login-related windows that we want to show during the + // logging-in animation. + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_RESTORE)); + + // Press the power button and check that the lock timer is started and that we + // start lifting the non-screen-locker containers. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + EXPECT_FALSE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + + // Release the button before the lock timer fires. + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_DROP)); + + // Press the button and fire the lock timer. We should request that the + // screen be locked, but we should still be in the lift animation. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + EXPECT_EQ(0, delegate_->num_lock_requests()); + test_api_->trigger_lock_timeout(); + EXPECT_EQ(1, delegate_->num_lock_requests()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + + // Notify that we locked successfully. + state_controller_->OnStartingLock(); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY)); + + // Notify that the lock window is visible. We should make it raise in. + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN)); + + // When we release the power button, the lock-to-shutdown timer should be + // stopped. + EXPECT_TRUE(test_api_->lock_to_shutdown_timer_is_running()); + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_to_shutdown_timer_is_running()); + + // Notify that the screen has been unlocked. We should show the + // non-screen-locker windows. + state_controller_->OnLockStateChanged(false); + shell_delegate_->UnlockScreen(); + bool called = false; + base::Closure closure = base::Bind(&CheckCalledCallback, &called); + state_controller_->OnLockScreenHide(closure); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_LIFT)); + EXPECT_TRUE(called); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_DROP)); +} + +// Hold the power button down from the unlocked state to eventual shutdown. +TEST_F(SessionStateControllerImpl2Test, LockToShutdown) { + controller_->set_has_legacy_power_button_for_test(false); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // Hold the power button and lock the screen. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + test_api_->trigger_lock_timeout(); + state_controller_->OnStartingLock(); + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + + // When the lock-to-shutdown timeout fires, we should start the shutdown + // timer. + EXPECT_TRUE(test_api_->lock_to_shutdown_timer_is_running()); + test_api_->trigger_lock_to_shutdown_timeout(); + EXPECT_TRUE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, + internal::SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN)); + + // Fire the shutdown timeout and check that we request shutdown. + test_api_->trigger_shutdown_timeout(); + EXPECT_TRUE(test_api_->real_shutdown_timer_is_running()); + EXPECT_EQ(0, NumShutdownRequests()); + 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(SessionStateControllerImpl2Test, CancelLockToShutdown) { + controller_->set_has_legacy_power_button_for_test(false); + + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // Hold the power button and lock the screen. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + test_api_->trigger_lock_timeout(); + state_controller_->OnStartingLock(); + + // Power button is released while system attempts to lock. + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + + EXPECT_FALSE(state_controller_->ShutdownRequested()); + EXPECT_FALSE(test_api_->lock_to_shutdown_timer_is_running()); + EXPECT_FALSE(test_api_->shutdown_timer_is_running()); +} + +// Test that we handle the case where lock requests are ignored. +TEST_F(SessionStateControllerImpl2Test, Lock) { + // We require animations to have a duration for this test. + ui::LayerAnimator::set_disable_animations_for_test(false); + + controller_->set_has_legacy_power_button_for_test(false); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // Hold the power button and lock the screen. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + test_api_->trigger_lock_timeout(); + EXPECT_EQ(1, delegate_->num_lock_requests()); + EXPECT_TRUE(test_api_->lock_fail_timer_is_running()); + + // We shouldn't start the lock-to-shutdown timer until the screen has actually + // been locked. + EXPECT_FALSE(test_api_->lock_to_shutdown_timer_is_running()); + + // Act as if the request timed out. We should restore the windows. + test_api_->trigger_lock_fail_timeout(); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_DROP)); +} + +// Test the basic operation of the lock button. +TEST_F(SessionStateControllerImpl2Test, LockButtonBasic) { + controller_->set_has_legacy_power_button_for_test(false); + // The lock button shouldn't do anything if we aren't logged in. + state_controller_->OnLoginStateChanged(user::LOGGED_IN_NONE); + state_controller_->OnLockStateChanged(false); + SetUserLoggedIn(false); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + EXPECT_EQ(0, delegate_->num_lock_requests()); + + // Ditto for when we're logged in as a guest. + state_controller_->OnLoginStateChanged(user::LOGGED_IN_GUEST); + SetUserLoggedIn(true); + SetCanLockScreen(false); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + EXPECT_EQ(0, delegate_->num_lock_requests()); + + // If we're logged in as a regular user, we should start the lock timer and + // the pre-lock animation. + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + SetCanLockScreen(true); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); + + // If the button is released immediately, we shouldn't lock the screen. + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_DROP)); + EXPECT_EQ(0, delegate_->num_lock_requests()); + + // Press the button again and let the lock timeout fire. We should request + // that the screen be locked. + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + test_api_->trigger_lock_timeout(); + EXPECT_EQ(1, delegate_->num_lock_requests()); + + // Pressing the lock button while we have a pending lock request shouldn't do + // anything. + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + + // Pressing the button also shouldn't do anything after the screen is locked. + state_controller_->OnStartingLock(); + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); +} + +// Test that the power button takes priority over the lock button. +TEST_F(SessionStateControllerImpl2Test, PowerButtonPreemptsLockButton) { + controller_->set_has_legacy_power_button_for_test(false); + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(false); + + // While the lock button is down, hold the power button. + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + + // The lock timer shouldn't be stopped when the lock button is released. + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + + // Now press the power button first and then the lock button. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); + + // Releasing the power button should stop the lock timer. + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + controller_->OnLockButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); +} + +// 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(SessionStateControllerImpl2Test, LockWithoutButton) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnStartingLock(); + EXPECT_TRUE( + animator_api_->ContainersAreAnimated( + internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | + internal::SessionStateAnimator::LAUNCHER, + internal::SessionStateAnimator::ANIMATION_LIFT)); +} + +// 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(SessionStateControllerImpl2Test, ShutdownWithoutButton) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnAppTerminating(); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + GenerateMouseMoveEvent(); + 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(SessionStateControllerImpl2Test, RequestShutdownFromLoginScreen) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_NONE); + state_controller_->OnLockStateChanged(false); + SetUserLoggedIn(false); + state_controller_->RequestShutdown(); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + GenerateMouseMoveEvent(); + EXPECT_FALSE(cursor_visible()); + + EXPECT_EQ(0, NumShutdownRequests()); + EXPECT_TRUE(test_api_->real_shutdown_timer_is_running()); + test_api_->trigger_real_shutdown_timeout(); + EXPECT_EQ(1, NumShutdownRequests()); +} + +TEST_F(SessionStateControllerImpl2Test, RequestShutdownFromLockScreen) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + state_controller_->RequestShutdown(); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + GenerateMouseMoveEvent(); + EXPECT_FALSE(cursor_visible()); + + EXPECT_EQ(0, NumShutdownRequests()); + EXPECT_TRUE(test_api_->real_shutdown_timer_is_running()); + test_api_->trigger_real_shutdown_timeout(); + EXPECT_EQ(1, NumShutdownRequests()); +} + +TEST_F(SessionStateControllerImpl2Test, + RequestAndCancelShutdownFromLockScreen) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + state_controller_->OnLockStateChanged(true); + shell_delegate_->LockScreen(); + + // Press the power button and check that we start the shutdown timer. + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + EXPECT_TRUE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS)); + + // Release the power button before the shutdown timer fires. + controller_->OnPowerButtonEvent(false, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->shutdown_timer_is_running()); + EXPECT_TRUE( + animator_api_->RootWindowIsAnimated( + internal::SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS)); +} + +// Test that we ignore power button presses when the screen is turned off. +TEST_F(SessionStateControllerImpl2Test, IgnorePowerButtonIfScreenIsOff) { + state_controller_->OnLoginStateChanged(user::LOGGED_IN_USER); + + // When the screen brightness is at 0%, we shouldn't do anything in response + // to power button presses. + controller_->OnScreenBrightnessChanged(0.0); + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_FALSE(test_api_->lock_timer_is_running()); + + // After increasing the brightness to 10%, we should start the timer like + // usual. + controller_->OnScreenBrightnessChanged(10.0); + controller_->OnPowerButtonEvent(true, base::TimeTicks::Now()); + EXPECT_TRUE(test_api_->lock_timer_is_running()); +} + +} // namespace test +} // namespace ash |