Commit 4c93f79b authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

desks: Add tests for RootWindowDeskSwitchAnimator.

Test: added
Bug: 1111445
Change-Id: I0f79be1c09d4ef0eeff276e3c50f491efbe56244
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380517Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809854}
parent 08252de5
...@@ -2171,6 +2171,7 @@ test("ash_unittests") { ...@@ -2171,6 +2171,7 @@ test("ash_unittests") {
"wm/default_window_resizer_unittest.cc", "wm/default_window_resizer_unittest.cc",
"wm/desks/autotest_desks_api_unittests.cc", "wm/desks/autotest_desks_api_unittests.cc",
"wm/desks/desks_unittests.cc", "wm/desks/desks_unittests.cc",
"wm/desks/root_window_desk_switch_animator_unittest.cc",
"wm/drag_window_resizer_unittest.cc", "wm/drag_window_resizer_unittest.cc",
"wm/fullscreen_window_finder_unittest.cc", "wm/fullscreen_window_finder_unittest.cc",
"wm/gestures/back_gesture/back_gesture_affordance_unittest.cc", "wm/gestures/back_gesture/back_gesture_affordance_unittest.cc",
...@@ -2540,6 +2541,8 @@ static_library("test_support") { ...@@ -2540,6 +2541,8 @@ static_library("test_support") {
"wm/cursor_manager_test_api.h", "wm/cursor_manager_test_api.h",
"wm/desks/desks_test_util.cc", "wm/desks/desks_test_util.cc",
"wm/desks/desks_test_util.h", "wm/desks/desks_test_util.h",
"wm/desks/root_window_desk_switch_animator_test_api.cc",
"wm/desks/root_window_desk_switch_animator_test_api.h",
"wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.cc", "wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.cc",
"wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.h", "wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.h",
"wm/lock_state_controller_test_api.cc", "wm/lock_state_controller_test_api.cc",
......
specific_include_rules = {
"root_window_desk_switch_animator_test_api.cc": [
# These tests create a fake viz::CopyOutputTextureResult to avoid the async
# way of waiting for a layer output request. So this needs to explicity
# depend on gpu/command_buffer/common.
"+gpu/command_buffer/common",
],
}
...@@ -29,9 +29,6 @@ namespace ash { ...@@ -29,9 +29,6 @@ namespace ash {
namespace { namespace {
// The space between the starting and ending desks screenshots in dips.
constexpr int kDesksSpacing = 50;
// The maximum number of times to retry taking a screenshot for either the // The maximum number of times to retry taking a screenshot for either the
// starting or the ending desks. After this maximum number is reached, we ignore // starting or the ending desks. After this maximum number is reached, we ignore
// a failed screenshot request and proceed with next phases. // a failed screenshot request and proceed with next phases.
...@@ -50,12 +47,6 @@ constexpr base::TimeDelta kAnimationDuration = ...@@ -50,12 +47,6 @@ constexpr base::TimeDelta kAnimationDuration =
// desk change. // desk change.
constexpr int kTouchpadSwipeLengthForDeskChange = 100; constexpr int kTouchpadSwipeLengthForDeskChange = 100;
// The animation layer has extra padding at its two edges. The width in dips is
// a ratio of the root window width. This padding is to notify users there are
// no more desks on that side by showing a black region as we swipe
// continuously.
constexpr float kEdgePaddingRatio = 0.15f;
// The amount, by which the detached old layers of the removed desk's windows, // The amount, by which the detached old layers of the removed desk's windows,
// is translated vertically during the for-remove desk switch animation. // is translated vertically during the for-remove desk switch animation.
constexpr int kRemovedDeskWindowYTranslation = 20; constexpr int kRemovedDeskWindowYTranslation = 20;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "ash/ash_export.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_observer.h"
...@@ -157,7 +158,8 @@ namespace ash { ...@@ -157,7 +158,8 @@ namespace ash {
// slightly more to accommodate the continuous desk animations feature. The base // slightly more to accommodate the continuous desk animations feature. The base
// algorithm is still valid, but once the features are near completion these // algorithm is still valid, but once the features are near completion these
// need to be updated. // need to be updated.
class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { class ASH_EXPORT RootWindowDeskSwitchAnimator
: public ui::ImplicitAnimationObserver {
public: public:
class Delegate { class Delegate {
public: public:
...@@ -179,6 +181,15 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -179,6 +181,15 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
virtual ~Delegate() = default; virtual ~Delegate() = default;
}; };
// The space between the starting and ending desks screenshots in dips.
static constexpr int kDesksSpacing = 50;
// The animation layer has extra padding at its two edges. The width in dips
// is a ratio of the root window width. This padding is to notify users there
// are no more desks on that side by showing a black region as we swipe
// continuously.
static constexpr float kEdgePaddingRatio = 0.15f;
RootWindowDeskSwitchAnimator(aura::Window* root, RootWindowDeskSwitchAnimator(aura::Window* root,
int starting_desk_index, int starting_desk_index,
int ending_desk_index, int ending_desk_index,
...@@ -235,6 +246,8 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { ...@@ -235,6 +246,8 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
void OnImplicitAnimationsCompleted() override; void OnImplicitAnimationsCompleted() override;
private: private:
friend class RootWindowDeskSwitchAnimatorTestApi;
// Completes the first phase of the animation using the given |layer| as the // Completes the first phase of the animation using the given |layer| as the
// screenshot layer of the starting desk. This layer will be parented to the // screenshot layer of the starting desk. This layer will be parented to the
// animation layer, which will be setup with its initial transform according // animation layer, which will be setup with its initial transform according
......
// Copyright 2020 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/desks/root_window_desk_switch_animator_test_api.h"
#include <memory>
#include "ash/wm/desks/root_window_desk_switch_animator.h"
#include "base/strings/string_number_conversions.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/gfx/color_space.h"
namespace ash {
namespace {
int g_mailbox_id = 0;
// Creates a blank copy output result the size of |root_window|.
std::unique_ptr<viz::CopyOutputResult> CreateCopyOutputResult(
aura::Window* root_window) {
std::string mailbox_name =
"mailboxname" + base::NumberToString(g_mailbox_id++);
gpu::Mailbox mailbox;
mailbox.SetName(reinterpret_cast<const int8_t*>(mailbox_name.c_str()));
std::unique_ptr<viz::CopyOutputResult> copy_result =
std::make_unique<viz::CopyOutputTextureResult>(
root_window->bounds(), mailbox, gpu::SyncToken(),
gfx::ColorSpace::CreateSRGB(),
viz::SingleReleaseCallback::Create(base::DoNothing()));
DCHECK(!copy_result->IsEmpty());
return copy_result;
}
} // namespace
RootWindowDeskSwitchAnimatorTestApi::RootWindowDeskSwitchAnimatorTestApi(
RootWindowDeskSwitchAnimator* animator)
: animator_(animator) {
DCHECK(animator_);
}
RootWindowDeskSwitchAnimatorTestApi::~RootWindowDeskSwitchAnimatorTestApi() =
default;
void RootWindowDeskSwitchAnimatorTestApi::OnStartingDeskScreenshotTaken() {
animator_->OnStartingDeskScreenshotTaken(
CreateCopyOutputResult(animator_->root_window_));
}
void RootWindowDeskSwitchAnimatorTestApi::OnEndingDeskScreenshotTaken() {
animator_->OnEndingDeskScreenshotTaken(
CreateCopyOutputResult(animator_->root_window_));
}
ui::Layer* RootWindowDeskSwitchAnimatorTestApi::GetAnimationLayer() {
return animator_->animation_layer_owner_->root();
}
ui::Layer*
RootWindowDeskSwitchAnimatorTestApi::GetScreenshotLayerOfDeskWithIndex(
int desk_index) {
auto screenshot_layers = animator_->screenshot_layers_;
DCHECK_GE(desk_index, 0);
DCHECK_LT(desk_index, int{screenshot_layers.size()});
ui::Layer* layer = screenshot_layers[desk_index];
DCHECK(layer);
return layer;
}
int RootWindowDeskSwitchAnimatorTestApi::GetEndingDeskIndex() const {
return animator_->ending_desk_index_;
}
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_TEST_API_H_
#define ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_TEST_API_H_
namespace ui {
class Layer;
}
namespace ash {
class RootWindowDeskSwitchAnimator;
// Use the api in this class to test the internals of
// RootWindowDeskSwitchAnimator.
class RootWindowDeskSwitchAnimatorTestApi {
public:
explicit RootWindowDeskSwitchAnimatorTestApi(
RootWindowDeskSwitchAnimator* animator);
RootWindowDeskSwitchAnimatorTestApi(
const RootWindowDeskSwitchAnimatorTestApi&) = delete;
RootWindowDeskSwitchAnimatorTestApi& operator=(
const RootWindowDeskSwitchAnimatorTestApi&) = delete;
~RootWindowDeskSwitchAnimatorTestApi();
// Triggers the path taken when a screenshot is taken. Uses a blank
// viz::CopyOutputResult and is synchronous.
void OnStartingDeskScreenshotTaken();
void OnEndingDeskScreenshotTaken();
// Getters for the layers associated with the animation.
ui::Layer* GetAnimationLayer();
ui::Layer* GetScreenshotLayerOfDeskWithIndex(int desk_index);
int GetEndingDeskIndex() const;
private:
RootWindowDeskSwitchAnimator* const animator_;
};
} // namespace ash
#endif // ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_TEST_API_H_
// Copyright 2020 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/desks/root_window_desk_switch_animator.h"
#include <memory>
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/desks/root_window_desk_switch_animator_test_api.h"
#include "base/test/scoped_feature_list.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
namespace ash {
namespace {
// Computes the animation layer expected size. Each screenshot is the size of
// the primary root window, and there is spacing between each screenshot, which
// are children of the animation layer. The animation layer has padding on each
// end.
gfx::Size ComputeAnimationLayerExpectedSize(int expected_screenshots) {
const gfx::Size root_window_size =
Shell::GetPrimaryRootWindow()->bounds().size();
const int edge_padding =
std::round(RootWindowDeskSwitchAnimator::kEdgePaddingRatio *
root_window_size.width());
gfx::Size expected_size = root_window_size;
expected_size.set_width(root_window_size.width() * expected_screenshots +
RootWindowDeskSwitchAnimator::kDesksSpacing *
(expected_screenshots - 1) +
2 * edge_padding);
return expected_size;
}
// Computes where |child_layer| is shown in root window coordinates. This is
// done by applying its parent's transform to it. |child_layer| itself is not
// expected to have a transform and its grandparent is the root window. If
// |use_target_transform| is false apply the parent's current transform,
// otherwise apply the parent's target transform (the expected transform at the
// end of an ongoing animation).
gfx::Rect GetVisibleBounds(ui::Layer* child_layer,
ui::Layer* animating_layer,
bool use_target_transform = false) {
DCHECK_EQ(animating_layer, child_layer->parent());
DCHECK(child_layer->transform().IsIdentity());
DCHECK_EQ(Shell::GetPrimaryRootWindow()->layer(), animating_layer->parent());
const gfx::Transform animating_layer_transform =
use_target_transform ? animating_layer->GetTargetTransform()
: animating_layer->transform();
DCHECK(animating_layer_transform.IsIdentityOr2DTranslation());
gfx::RectF bounds(child_layer->bounds());
animating_layer_transform.TransformRect(&bounds);
return gfx::ToRoundedRect(bounds);
}
gfx::Rect GetTargetVisibleBounds(ui::Layer* child_layer,
ui::Layer* animating_layer) {
return GetVisibleBounds(child_layer, animating_layer,
/*use_target_transform=*/true);
}
} // namespace
class RootWindowDeskSwitchAnimatorTest
: public AshTestBase,
public RootWindowDeskSwitchAnimator::Delegate {
public:
RootWindowDeskSwitchAnimatorTest() = default;
RootWindowDeskSwitchAnimatorTest(const RootWindowDeskSwitchAnimatorTest&) =
delete;
RootWindowDeskSwitchAnimatorTest& operator=(
const RootWindowDeskSwitchAnimatorTest&) = delete;
~RootWindowDeskSwitchAnimatorTest() override = default;
RootWindowDeskSwitchAnimatorTestApi* test_api() { return test_api_.get(); }
RootWindowDeskSwitchAnimator* animator() { return animator_.get(); }
int starting_desk_screenshot_taken_count() const {
return starting_desk_screenshot_taken_count_;
}
int ending_desk_screenshot_taken_count() const {
return ending_desk_screenshot_taken_count_;
}
// Creates an animator from the given indices on the primary root window.
// Creates a test api for the animator as well.
void InitAnimator(int starting_desk_index, int ending_desk_index) {
animator_ = std::make_unique<RootWindowDeskSwitchAnimator>(
Shell::GetPrimaryRootWindow(), starting_desk_index, ending_desk_index,
this, /*for_remove=*/false);
test_api_ =
std::make_unique<RootWindowDeskSwitchAnimatorTestApi>(animator_.get());
}
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
features::kEnhancedDeskAnimations);
AshTestBase::SetUp();
}
// RootWindowDeskSwitchAnimator::Delegate:
void OnStartingDeskScreenshotTaken(int ending_desk_index) override {
++starting_desk_screenshot_taken_count_;
}
void OnEndingDeskScreenshotTaken() override {
++ending_desk_screenshot_taken_count_;
}
void OnDeskSwitchAnimationFinished() override {}
private:
base::test::ScopedFeatureList scoped_feature_list_;
// The RootWindowDeskSwitchAnimator we are testing.
std::unique_ptr<RootWindowDeskSwitchAnimator> animator_;
// The support test api associated with |animator_|.
std::unique_ptr<RootWindowDeskSwitchAnimatorTestApi> test_api_;
int starting_desk_screenshot_taken_count_ = 0;
int ending_desk_screenshot_taken_count_ = 0;
};
// Tests a simple animation from one desk to another.
TEST_F(RootWindowDeskSwitchAnimatorTest, SimpleAnimation) {
InitAnimator(1, 2);
test_api()->OnStartingDeskScreenshotTaken();
test_api()->OnEndingDeskScreenshotTaken();
// Tests that a simple animation has 2 screenshots, one for each desk.
EXPECT_EQ(1, starting_desk_screenshot_taken_count());
EXPECT_EQ(1, ending_desk_screenshot_taken_count());
// Tests that the animation layer is the expected size.
auto* animation_layer = test_api()->GetAnimationLayer();
EXPECT_EQ(2u, animation_layer->children().size());
EXPECT_EQ(ComputeAnimationLayerExpectedSize(2),
animation_layer->bounds().size());
// Tests that the screenshot associated with desk index 1 is the one that is
// shown at the beginning of the animation.
EXPECT_EQ(Shell::GetPrimaryRootWindow()->bounds(),
GetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(1),
animation_layer));
// Tests that the screenshot associated with desk index 2 is the one that is
// shown at the end of the animation.
animator()->StartAnimation();
EXPECT_EQ(Shell::GetPrimaryRootWindow()->bounds(),
GetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(2),
animation_layer));
EXPECT_EQ(2, test_api()->GetEndingDeskIndex());
}
// Tests a chained animation where the replaced animation already has a
// screenshot layer stored.
TEST_F(RootWindowDeskSwitchAnimatorTest, ChainedAnimationNoNewScreenshot) {
InitAnimator(1, 2);
test_api()->OnStartingDeskScreenshotTaken();
test_api()->OnEndingDeskScreenshotTaken();
// Replacing needs to be done while a current animation is underway, otherwise
// it will have no effect.
ui::ScopedAnimationDurationScaleMode non_zero(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
animator()->StartAnimation();
// Replacing with an animation going back to desk index 1. No new screenshot
// is needed.
bool needs_screenshot = animator()->ReplaceAnimation(1);
EXPECT_FALSE(needs_screenshot);
// Tests that no new screenshot was taken as it already existed.
auto* animation_layer = test_api()->GetAnimationLayer();
EXPECT_EQ(2u, animation_layer->children().size());
EXPECT_EQ(1, starting_desk_screenshot_taken_count());
EXPECT_EQ(1, ending_desk_screenshot_taken_count());
// Tests that the screenshot associated with desk index 1 is the one that is
// shown at the end of the animation.
animator()->StartAnimation();
EXPECT_EQ(
Shell::GetPrimaryRootWindow()->bounds(),
GetTargetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(1),
animation_layer));
}
// Tests a chained animation where we are adding an animation to the right of
// the current animating desks, causing the animation layer to shift left.
TEST_F(RootWindowDeskSwitchAnimatorTest, ChainedAnimationMovingLeft) {
InitAnimator(1, 2);
test_api()->OnStartingDeskScreenshotTaken();
test_api()->OnEndingDeskScreenshotTaken();
// Replacing needs to be done while a current animation is underway, otherwise
// it will have no effect.
ui::ScopedAnimationDurationScaleMode non_zero(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Tests that the animation layer originally has 2 children.
auto* animation_layer = test_api()->GetAnimationLayer();
EXPECT_EQ(2u, animation_layer->children().size());
animator()->StartAnimation();
// Replace the current animation to one that goes to desk index 3.
EXPECT_EQ(2, test_api()->GetEndingDeskIndex());
bool needs_screenshot = animator()->ReplaceAnimation(3);
ASSERT_TRUE(needs_screenshot);
EXPECT_EQ(3, test_api()->GetEndingDeskIndex());
// Take a screenshot at the new ending desk. Test that the animation layer now
// has 3 children.
test_api()->OnEndingDeskScreenshotTaken();
EXPECT_EQ(1, starting_desk_screenshot_taken_count());
EXPECT_EQ(2, ending_desk_screenshot_taken_count());
EXPECT_EQ(3u, animation_layer->children().size());
EXPECT_EQ(ComputeAnimationLayerExpectedSize(3),
animation_layer->bounds().size());
animator()->StartAnimation();
// Tests that the screenshot associated with desk index 3 is the one that is
// shown at the end of the animation.
EXPECT_EQ(
Shell::GetPrimaryRootWindow()->bounds(),
GetTargetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(3),
animation_layer));
}
// Tests a chained animation where we are adding an animation to the left of
// the current animating desks, causing the animation layer to shift right.
TEST_F(RootWindowDeskSwitchAnimatorTest, ChainedAnimationMovingRight) {
InitAnimator(3, 2);
test_api()->OnStartingDeskScreenshotTaken();
test_api()->OnEndingDeskScreenshotTaken();
// Replacing needs to be done while a current animation is underway, otherwise
// it will have no effect.
ui::ScopedAnimationDurationScaleMode non_zero(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
animator()->StartAnimation();
// Replace the current animation to one that goes to desk index 1.
EXPECT_EQ(2, test_api()->GetEndingDeskIndex());
bool needs_screenshot = animator()->ReplaceAnimation(1);
ASSERT_TRUE(needs_screenshot);
EXPECT_EQ(1, test_api()->GetEndingDeskIndex());
// Take a screenshot at the new ending desk. Test that the animation layer now
// has 3 children.
test_api()->OnEndingDeskScreenshotTaken();
EXPECT_EQ(1, starting_desk_screenshot_taken_count());
EXPECT_EQ(2, ending_desk_screenshot_taken_count());
auto* animation_layer = test_api()->GetAnimationLayer();
EXPECT_EQ(3u, animation_layer->children().size());
EXPECT_EQ(ComputeAnimationLayerExpectedSize(3),
animation_layer->bounds().size());
animator()->StartAnimation();
// Tests that the screenshot associated with desk index 1 is the one that is
// shown at the end of the animation.
EXPECT_EQ(
Shell::GetPrimaryRootWindow()->bounds(),
GetTargetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(1),
animation_layer));
}
// Tests a complex animation which multiple animations are started and replaced.
TEST_F(RootWindowDeskSwitchAnimatorTest, MultipleReplacements) {
InitAnimator(1, 2);
test_api()->OnStartingDeskScreenshotTaken();
test_api()->OnEndingDeskScreenshotTaken();
// Replacing needs to be done while a current animation is underway, otherwise
// it will have no effect.
ui::ScopedAnimationDurationScaleMode non_zero(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
animator()->StartAnimation();
// Replace all the indices in the list one at a time.
auto animation_indices = {1, 0, 1, 2, 1, 2, 3, 2, 1};
ui::Layer* animation_layer = test_api()->GetAnimationLayer();
for (int index : animation_indices) {
if (animator()->ReplaceAnimation(index))
test_api()->OnEndingDeskScreenshotTaken();
EXPECT_EQ(index, test_api()->GetEndingDeskIndex());
// Start the replacement animation. The new animation should have a target
// transform such that the desk at |index| is visible on animation end.
animator()->StartAnimation();
EXPECT_EQ(Shell::GetPrimaryRootWindow()->bounds(),
GetTargetVisibleBounds(
test_api()->GetScreenshotLayerOfDeskWithIndex(index),
animation_layer));
}
// Only 4 screenshots are taken as they are reused.
EXPECT_EQ(1, starting_desk_screenshot_taken_count());
EXPECT_EQ(3, ending_desk_screenshot_taken_count());
// Tests that the screenshot associated with desk index 1 is the one that is
// shown at the end of the animation.
EXPECT_EQ(
Shell::GetPrimaryRootWindow()->bounds(),
GetTargetVisibleBounds(test_api()->GetScreenshotLayerOfDeskWithIndex(1),
animation_layer));
}
} // namespace ash
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment