// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/lock_screen_action/lock_screen_action_background_controller_impl.h"
#include <memory>
#include <string>
#include <vector>
#include "ash/lock_screen_action/lock_screen_action_background_controller_impl_test_api.h"
#include "ash/lock_screen_action/lock_screen_action_background_observer.h"
#include "ash/lock_screen_action/lock_screen_action_background_state.h"
#include "ash/lock_screen_action/lock_screen_action_background_view.h"
#include "ash/lock_screen_action/lock_screen_action_background_view_test_api.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/run_loop.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
aura::Window* GetContainer(ShellWindowId container_id) {
return Shell::GetPrimaryRootWindowController()->GetContainer(container_id);
}
class TestActionBackgroundObserver : public LockScreenActionBackgroundObserver {
public:
TestActionBackgroundObserver() = default;
TestActionBackgroundObserver(const TestActionBackgroundObserver&) = delete;
TestActionBackgroundObserver& operator=(const TestActionBackgroundObserver&) =
delete;
~TestActionBackgroundObserver() override = default;
// LockScreenActionBackgroundObserver:
void OnLockScreenActionBackgroundStateChanged(
LockScreenActionBackgroundState state) override {
state_changes_.push_back(state);
}
const std::vector<LockScreenActionBackgroundState> state_changes() const {
return state_changes_;
}
void ClearStateChanges() { state_changes_.clear(); }
private:
std::vector<LockScreenActionBackgroundState> state_changes_;
};
} // namespace
class LockScreenActionBackgroundControllerImplTest : public AshTestBase {
public:
LockScreenActionBackgroundControllerImplTest() = default;
LockScreenActionBackgroundControllerImplTest(
const LockScreenActionBackgroundControllerImplTest&) = delete;
LockScreenActionBackgroundControllerImplTest& operator=(
const LockScreenActionBackgroundControllerImplTest&) = delete;
~LockScreenActionBackgroundControllerImplTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
test_animation_duration_ =
std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
controller_.AddObserver(&observer_);
}
void TearDown() override {
controller_.RemoveObserver(&observer_);
test_animation_duration_.reset();
AshTestBase::TearDown();
}
void CompleteBackgroundAnimations() {
LockScreenActionBackgroundView* contents_view =
LockScreenActionBackgroundControllerImplTestApi(controller())
.GetContentsView();
views::View* background =
LockScreenActionBackgroundViewTestApi(contents_view).GetBackground();
// The ink drop's layer is a sibling of the background's layer, so call this
// from the background's layer's parent.
background->layer()->parent()->CompleteAllAnimations();
}
views::Widget* GetBackgroundWidget() {
return LockScreenActionBackgroundControllerImplTestApi(controller())
.GetWidget();
}
aura::Window* GetBackgroundWindow() {
views::Widget* widget = GetBackgroundWidget();
if (!widget)
return nullptr;
return widget->GetNativeWindow();
}
bool BackgroundWindowShown() {
aura::Window* window = GetBackgroundWindow();
if (!window)
return false;
return window->IsVisible();
}
void ExpectObservedStatesMatch(
const std::vector<LockScreenActionBackgroundState>& expectation,
const std::string& error_message) {
EXPECT_EQ(expectation, observer_.state_changes()) << error_message;
observer_.ClearStateChanges();
}
// Initializes the controller instance to the desired state.
bool InitializeControllerToState(LockScreenActionBackgroundState state) {
controller_.SetParentWindow(
GetContainer(kShellWindowId_LockScreenContainer));
if (state == LockScreenActionBackgroundState::kHidden)
return controller_.state() == state;
controller_.ShowBackground();
observer_.ClearStateChanges();
if (state == LockScreenActionBackgroundState::kShowing)
return controller_.state() == state;
CompleteBackgroundAnimations();
observer_.ClearStateChanges();
if (state == LockScreenActionBackgroundState::kShown)
return controller_.state() == state;
controller_.HideBackground();
observer_.ClearStateChanges();
return controller_.state() == state;
}
LockScreenActionBackgroundControllerImpl* controller() {
return &controller_;
}
private:
LockScreenActionBackgroundControllerImpl controller_;
TestActionBackgroundObserver observer_;
std::unique_ptr<ui::ScopedAnimationDurationScaleMode>
test_animation_duration_;
};
TEST_F(LockScreenActionBackgroundControllerImplTest, NormalFlow) {
// Show/Hide before parent window is set should fail.
EXPECT_FALSE(controller()->ShowBackground());
EXPECT_FALSE(controller()->HideBackgroundImmediately());
EXPECT_FALSE(controller()->HideBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
controller()->SetParentWindow(
GetContainer(kShellWindowId_LockScreenContainer));
// With parent window set, show request should make the window visible and
// move the controller to showing state.
ASSERT_TRUE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShowing, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kShowing},
"Show requested");
EXPECT_TRUE(BackgroundWindowShown());
EXPECT_FALSE(GetBackgroundWindow()->HasFocus());
// Repeated request to show a background window should fail.
EXPECT_FALSE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShowing, controller()->state());
// When the background window animations are complete, the controller should
// report kShown state.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kShown, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kShown},
"Show animation complete");
EXPECT_TRUE(BackgroundWindowShown());
EXPECT_FALSE(GetBackgroundWindow()->HasFocus());
// Request to show a background window in shown state should fail.
EXPECT_FALSE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShown, controller()->state());
// Request to hide the window should start hiding the background window
// (the window should still be visible at this point).
ASSERT_TRUE(controller()->HideBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kHiding, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHiding},
"Hide requested");
EXPECT_TRUE(BackgroundWindowShown());
EXPECT_FALSE(GetBackgroundWindow()->HasFocus());
// Repeated reqiest to hide the background window should fail.
EXPECT_FALSE(controller()->HideBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kHiding, controller()->state());
// The window should be hidden when the background window animations are
// completed.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHidden},
"Hide animation complete");
EXPECT_FALSE(BackgroundWindowShown());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
// Request to hide the background window should fail if the window is already
// hidden.
EXPECT_FALSE(controller()->HideBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
}
TEST_F(LockScreenActionBackgroundControllerImplTest, HideWhileShowing) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kShowing));
// Request to hide background while show is in progress should initiate the
// background window hide.
ASSERT_TRUE(controller()->HideBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kHiding, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHiding},
"Hide requested");
EXPECT_TRUE(BackgroundWindowShown());
// Upon animation completion, the background window should be hidden.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHidden},
"Hide animation complete");
EXPECT_FALSE(BackgroundWindowShown());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
HideImmediatelyWhileShowing) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kShowing));
// Request to hide background while show is in progress should hide the
// background window.
ASSERT_TRUE(controller()->HideBackgroundImmediately());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHidden},
"Hide immediately requested");
EXPECT_FALSE(BackgroundWindowShown());
// Run any pending animations, to confirm that the controller state does not
// change.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch(std::vector<LockScreenActionBackgroundState>(),
"Running animation on hidden window");
EXPECT_FALSE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest, HideImmediatelyWhenShown) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kShown));
// Request to hide background immediately - the window should get hidden.
ASSERT_TRUE(controller()->HideBackgroundImmediately());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHidden},
"Hide immediately requested");
EXPECT_FALSE(BackgroundWindowShown());
// Run any pending animations, to confirm that the controller state does not
// change.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch(std::vector<LockScreenActionBackgroundState>(),
"Running animation on hidden window");
EXPECT_FALSE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
HideImmediatelyWhileHiding) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kHiding));
// Request to hide background immediately while hide is in progress should
// hide the background window.
ASSERT_TRUE(controller()->HideBackgroundImmediately());
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kHidden},
"Hide immediately requested");
EXPECT_FALSE(BackgroundWindowShown());
// Run any pending animations, to confirm that the controller state does not
// change.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
ExpectObservedStatesMatch(std::vector<LockScreenActionBackgroundState>(),
"Running animation on hidden window");
EXPECT_FALSE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest, ShowWhileHiding) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kHiding));
// Request to show the background window while hide ainmation is in progress
// should be respected.
ASSERT_TRUE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShowing, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kShowing},
"Show requested");
EXPECT_TRUE(BackgroundWindowShown());
// When the animations are completed, the controller should be in kShown
// state.
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kShown, controller()->state());
ExpectObservedStatesMatch({LockScreenActionBackgroundState::kShown},
"Complete show animation");
EXPECT_TRUE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
BackgroundWindowClosureWhenShown) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kShown));
// Close the background window while show is in progress - the controller
// should move to kHidden state.
ASSERT_TRUE(BackgroundWindowShown());
GetBackgroundWidget()->Close();
// Run the loop so the close request propagates.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
EXPECT_FALSE(BackgroundWindowShown());
// Background should be showable again, if requsted.
ASSERT_TRUE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShowing, controller()->state());
EXPECT_TRUE(BackgroundWindowShown());
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kShown, controller()->state());
EXPECT_TRUE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
BackgroundWindowClosureWhenHidden) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kHiding));
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
// Close the background window while show is in progress - the controller
// should move to kHidden state.
ASSERT_TRUE(GetBackgroundWidget());
GetBackgroundWidget()->Close();
// Run the loop so the close request propagates.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
EXPECT_FALSE(BackgroundWindowShown());
// Background should be showable again, if requsted.
ASSERT_TRUE(controller()->ShowBackground());
EXPECT_EQ(LockScreenActionBackgroundState::kShowing, controller()->state());
EXPECT_TRUE(BackgroundWindowShown());
CompleteBackgroundAnimations();
EXPECT_EQ(LockScreenActionBackgroundState::kShown, controller()->state());
EXPECT_TRUE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
BackgroundWindowClosureWhileShowing) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kShowing));
// Close the background window while show is in progress - the controller
// should move to kHidden state.
ASSERT_TRUE(BackgroundWindowShown());
GetBackgroundWidget()->Close();
// Run the loop so the close request propagates.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
EXPECT_FALSE(BackgroundWindowShown());
}
TEST_F(LockScreenActionBackgroundControllerImplTest,
BackgroundWindowClosureWhileHiding) {
ASSERT_TRUE(
InitializeControllerToState(LockScreenActionBackgroundState::kHiding));
// Close the background window while hiding is in progress - the controller
// should move to kHidden state.
ASSERT_TRUE(BackgroundWindowShown());
GetBackgroundWidget()->Close();
// Run the loop so the close request propagates.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(LockScreenActionBackgroundState::kHidden, controller()->state());
EXPECT_FALSE(BackgroundWindowShown());
}
} // namespace ash