blob: 59abb2730df34a2820c3020dab93aea4f905c3b5 [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/system/power/power_event_observer.h"
#include <memory>
#include "ash/root_window_controller.h"
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "ash/system/power/power_event_observer_test_api.h"
#include "ash/test/ash_test_base.h"
#include "ash/wallpaper/wallpaper_widget_controller.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/lock_state_controller_test_api.h"
#include "ash/wm/test_session_state_animator.h"
#include "base/time/time.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "chromeos/dbus/power_manager_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
namespace ash {
class PowerEventObserverTest : public AshTestBase {
public:
PowerEventObserverTest() = default;
~PowerEventObserverTest() override = default;
// AshTestBase:
void SetUp() override {
chromeos::DBusThreadManager::Initialize();
AshTestBase::SetUp();
observer_.reset(new PowerEventObserver());
}
void TearDown() override {
observer_.reset();
AshTestBase::TearDown();
chromeos::DBusThreadManager::Shutdown();
}
protected:
int GetNumVisibleCompositors() {
int result = 0;
for (auto* window : Shell::GetAllRootWindows()) {
if (window->GetHost()->compositor()->IsVisible())
++result;
}
return result;
}
bool GetLockedState() {
// LockScreen is an async mojo call.
SessionController* const session_controller =
Shell::Get()->session_controller();
session_controller->FlushMojoForTest();
return session_controller->IsScreenLocked();
}
std::unique_ptr<PowerEventObserver> observer_;
private:
DISALLOW_COPY_AND_ASSIGN(PowerEventObserverTest);
};
TEST_F(PowerEventObserverTest, LockBeforeSuspend) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
// Check that the observer requests a suspend-readiness callback when it hears
// that the system is about to suspend.
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
// It should run the callback when it hears that the screen is locked and the
// lock screen animations have completed.
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
PowerEventObserverTestApi test_api(observer_.get());
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
test_api.CompositingDidCommit(compositor);
observer_->OnLockAnimationsComplete();
// Verify that CompositingStarted and CompositingEnded observed before
// CompositingDidCommit are ignored.
test_api.CompositingStarted(compositor);
test_api.CompositingEnded(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
// Suspend should remain delayed after first compositing cycle ends.
test_api.CompositeFrame(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositingDidCommit(compositor);
test_api.CompositingStarted(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositingEnded(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
// If the system is already locked, no callback should be requested.
observer_->SuspendDone(base::TimeDelta());
EXPECT_EQ(1, GetNumVisibleCompositors());
UnblockUserSession();
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
// Notify that lock animation is complete.
observer_->OnLockAnimationsComplete();
// Wait for a compositing after lock animation completes before suspending.
// In this case compositors should be made invisible immediately
test_api.CompositeFrame(compositor);
test_api.CompositeFrame(compositor);
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
// It also shouldn't request a callback if it isn't instructed to lock the
// screen.
observer_->SuspendDone(base::TimeDelta());
UnblockUserSession();
SetShouldLockScreenAutomatically(false);
EXPECT_EQ(1, GetNumVisibleCompositors());
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
TEST_F(PowerEventObserverTest, SetInvisibleBeforeSuspend) {
// Tests that all the Compositors are marked invisible before a suspend
// request when the screen is not supposed to be locked before a suspend.
EXPECT_EQ(1, GetNumVisibleCompositors());
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(0, GetNumVisibleCompositors());
observer_->SuspendDone(base::TimeDelta());
// Tests that all the Compositors are marked invisible _after_ the screen lock
// animations have completed.
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, GetNumVisibleCompositors());
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_EQ(1, GetNumVisibleCompositors());
observer_->OnLockAnimationsComplete();
EXPECT_EQ(1, GetNumVisibleCompositors());
ASSERT_TRUE(PowerEventObserverTestApi(observer_.get())
.SimulateCompositorsReadyForSuspend());
EXPECT_EQ(0, GetNumVisibleCompositors());
observer_->SuspendDone(base::TimeDelta());
EXPECT_EQ(1, GetNumVisibleCompositors());
}
TEST_F(PowerEventObserverTest, CanceledSuspend) {
// Tests that the Compositors are not marked invisible if a suspend is
// canceled or the system resumes before the lock screen is ready.
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, GetNumVisibleCompositors());
observer_->SuspendDone(base::TimeDelta());
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
observer_->OnLockAnimationsComplete();
EXPECT_EQ(1, GetNumVisibleCompositors());
}
TEST_F(PowerEventObserverTest, DelayResuspendForLockAnimations) {
// Tests that the following order of events is handled correctly:
//
// - A suspend request is started.
// - The screen is locked.
// - The suspend request is canceled.
// - Another suspend request is started.
// - The screen lock animations complete.
// - The screen lock UI changes get composited
//
// In this case, the observer should block the second suspend request until
// the screen lock compositing is done.
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
observer_->SuspendDone(base::TimeDelta());
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
// The expected number of suspend readiness callbacks is 2 because the
// observer has not run the callback that it got from the first suspend
// request. The real PowerManagerClient would reset its internal counter in
// this situation but the stub client is not that smart.
EXPECT_EQ(2, client->GetNumPendingSuspendReadinessCallbacks());
observer_->OnLockAnimationsComplete();
EXPECT_EQ(2, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
ASSERT_TRUE(PowerEventObserverTestApi(observer_.get())
.SimulateCompositorsReadyForSuspend());
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that device suspend is delayed for screen lock until the screen lock
// changes are composited for all root windows.
TEST_F(PowerEventObserverTest, DelaySuspendForCompositing_MultiDisplay) {
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
UpdateDisplay("100x100,200x200");
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
aura::Window::Windows windows = Shell::GetAllRootWindows();
ASSERT_EQ(2u, windows.size());
ui::Compositor* primary_compositor = windows[0]->GetHost()->compositor();
ui::Compositor* secondary_compositor = windows[1]->GetHost()->compositor();
ASSERT_EQ(2, GetNumVisibleCompositors());
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
PowerEventObserverTestApi test_api(observer_.get());
// Simulate a commit before lock animations complete, and verify associated
// compositing ends are ignored.
test_api.CompositingDidCommit(secondary_compositor);
observer_->OnLockAnimationsComplete();
test_api.CompositingStarted(secondary_compositor);
test_api.CompositingEnded(secondary_compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(2, GetNumVisibleCompositors());
test_api.CompositeFrame(primary_compositor);
test_api.CompositeFrame(primary_compositor);
test_api.CompositeFrame(secondary_compositor);
// Even though compositing for one display is done, changes to compositor
// visibility, and suspend readines state should be delayed until compositing
// for the other display finishes.
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(2, GetNumVisibleCompositors());
test_api.CompositeFrame(secondary_compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
TEST_F(PowerEventObserverTest,
DelaySuspendForCompositing_PendingDisplayRemoved) {
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
UpdateDisplay("100x100,200x200");
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
aura::Window::Windows windows = Shell::GetAllRootWindows();
ASSERT_EQ(2u, windows.size());
ui::Compositor* primary_compositor = windows[0]->GetHost()->compositor();
ASSERT_EQ(2, GetNumVisibleCompositors());
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
observer_->OnLockAnimationsComplete();
PowerEventObserverTestApi test_api(observer_.get());
test_api.CompositeFrame(primary_compositor);
test_api.CompositeFrame(primary_compositor);
// Even though compositing for one display is done, changes to compositor
// visibility, and suspend readines state should be delayed until compositing
// for the other display finishes.
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(2, GetNumVisibleCompositors());
// Remove the second display, and verify the remaining compositor is hidden
// at this point.
UpdateDisplay("100x100");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
TEST_F(PowerEventObserverTest, CompositorNotVisibleAtLockAnimationsComplete) {
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
Shell::GetPrimaryRootWindow()->GetHost()->compositor()->SetVisible(false);
observer_->OnLockAnimationsComplete();
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that for suspend imminent induced locking screen, locking animations
// are immediate.
TEST_F(PowerEventObserverTest, ImmediateLockAnimations) {
TestSessionStateAnimator* test_animator = new TestSessionStateAnimator;
LockStateController* lock_state_controller =
Shell::Get()->lock_state_controller();
lock_state_controller->set_animator_for_test(test_animator);
LockStateControllerTestApi lock_state_test_api(lock_state_controller);
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
ASSERT_FALSE(GetLockedState());
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
// Tests that locking animation starts.
EXPECT_TRUE(lock_state_test_api.is_animating_lock());
// Tests that we have two active animation containers for pre-lock animation,
// which are non lock screen containers and shelf container.
EXPECT_EQ(2u, test_animator->GetAnimationCount());
test_animator->AreContainersAnimated(
LockStateController::kPreLockContainersMask,
SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY);
// Tests that after finishing immediate animation, we have no active
// animations left.
test_animator->Advance(test_animator->GetDuration(
SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE));
EXPECT_EQ(0u, test_animator->GetAnimationCount());
// Flushes locking screen async request to start post-lock animation.
EXPECT_TRUE(GetLockedState());
EXPECT_TRUE(lock_state_test_api.is_animating_lock());
// Tests that we have two active animation container for post-lock animation,
// which are lock screen containers and shelf container.
EXPECT_EQ(2u, test_animator->GetAnimationCount());
test_animator->AreContainersAnimated(
SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN);
test_animator->AreContainersAnimated(SessionStateAnimator::SHELF,
SessionStateAnimator::ANIMATION_FADE_IN);
// Tests that after finishing immediate animation, we have no active
// animations left. Also checks that animation ends.
test_animator->Advance(test_animator->GetDuration(
SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE));
EXPECT_EQ(0u, test_animator->GetAnimationCount());
EXPECT_FALSE(lock_state_test_api.is_animating_lock());
}
// Tests that displays will not be considered ready to suspend until the
// animated wallpaper change finishes (if the wallpaper is being animated to
// another wallpaper after the screen is locked).
TEST_F(PowerEventObserverTest,
DisplaysNotReadyForSuspendUntilWallpaperAnimationEnds) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
// Set up animation state so wallpaper widget animations are not ended on
// their creation.
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Lock screen - this is expected to start wallpaper change (e.g. to a
// widget with a blurred wallpaper).
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
observer_->OnLockAnimationsComplete();
WallpaperWidgetController* wallpaper_widget_controller =
Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
// Assert that the wallpaper is being animated here - otherwise the test will
// not work.
ASSERT_TRUE(wallpaper_widget_controller->IsAnimating());
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
PowerEventObserverTestApi test_api(observer_.get());
// Simulate a single frame getting composited before the wallpaper animation
// is done - this frame is expected to be ignored by power event observer's
// compositing state observer.
test_api.CompositeFrame(compositor);
// Simulate wallpaper animation finishing - for the purpose of this test,
// before suspend begins.
wallpaper_widget_controller->EndPendingAnimation();
// Expect that two compositing cycles are completed before suspend continues,
// and displays get suspended.
test_api.CompositeFrame(compositor);
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositeFrame(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that animated wallpaper changes will be finished immediately when
// suspend starts (if the screen was locked when suspend started).
TEST_F(PowerEventObserverTest, EndWallpaperAnimationOnSuspendWhileLocked) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
// Set up animation state so wallpaper widget animations are not ended on
// their creation.
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Lock screen - this is expected to start wallpaper change (e.g. to a
// widget with a blurred wallpaper).
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
observer_->OnLockAnimationsComplete();
// Wallpaper animation should be stopped immediately on suspend.
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
WallpaperWidgetController* wallpaper_widget_controller =
Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
PowerEventObserverTestApi test_api(observer_.get());
// Expect that two compositing cycles are completed before suspend continues,
// and displays get suspended.
test_api.CompositeFrame(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositeFrame(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that animated wallpaper changes will be finished immediately when
// suspend starts (if the screen lock started before suspend).
TEST_F(PowerEventObserverTest, EndWallpaperAnimationOnSuspendWhileLocking) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
// Set up animation state so wallpaper widget animations are not ended on
// their creation.
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Lock screen - this is expected to start wallpaper change (e.g. to a
// widget with a blurred wallpaper).
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
// If suspend starts, wallpaper animation should be stopped after screen lock
// completes.
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
observer_->OnLockAnimationsComplete();
WallpaperWidgetController* wallpaper_widget_controller =
Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
PowerEventObserverTestApi test_api(observer_.get());
// Expect that two compositing cycles are completed before suspend continues,
// and displays get suspended.
test_api.CompositeFrame(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositeFrame(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that animated wallpaper changes will be finished immediately when
// suspend starts and causes a screen lock.
TEST_F(PowerEventObserverTest, EndWallpaperAnimationAfterLockDueToSuspend) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
// Set up animation state so wallpaper widget animations are not ended on
// their creation.
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Start suspend (which should start screen lock) - verify that wallpaper is
// not animating after the screen lock animations are reported as complete.
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
observer_->OnLockAnimationsComplete();
WallpaperWidgetController* wallpaper_widget_controller =
Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
PowerEventObserverTestApi test_api(observer_.get());
// Expect that two compositing cycles are completed before suspend continues,
// and displays get suspended.
test_api.CompositeFrame(compositor);
EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(1, GetNumVisibleCompositors());
test_api.CompositeFrame(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
// Tests that removing a display while power event observer is waiting for the
// wallpaper animation does not cause suspend to hang.
TEST_F(PowerEventObserverTest, DisplayRemovedDuringWallpaperAnimation) {
chromeos::PowerManagerClient* client =
chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
ASSERT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
SetCanLockScreen(true);
SetShouldLockScreenAutomatically(true);
UpdateDisplay("100x100,200x200");
// Set up animation state so wallpaper widget animations are not ended on
// their creation.
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Lock screen - this is expected to start wallpaper change (e.g. to a
// widget with a blurred wallpaper).
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
observer_->OnLockAnimationsComplete();
// Remove a display before wallpaper animation ends.
UpdateDisplay("100x100");
base::RunLoop().RunUntilIdle();
// Start suspend and verify the suspend proceeds when the primary window's
// compositors go throug two compositing cycles.
observer_->SuspendImminent(power_manager::SuspendImminent_Reason_OTHER);
ui::Compositor* compositor =
Shell::GetPrimaryRootWindow()->GetHost()->compositor();
PowerEventObserverTestApi test_api(observer_.get());
// Expect that two compositing cycles are completed before suspend continues,
// and displays get suspended.
test_api.CompositeFrame(compositor);
test_api.CompositeFrame(compositor);
EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
EXPECT_EQ(0, GetNumVisibleCompositors());
}
} // namespace ash