0

Reland "desks: Add tests for RootWindowDeskSwitchAnimator."

This is a reland of 4c93f79bd6. The
original change patch is in ps1.

The reason for the revert was an error was something with managing
GPU textures (mailbox name not found). Tried copying some GPU test
code [1] but still got the same error and was building up a lot of
GPU dependencies.

Instead of creating fake output, this cl wraps the existing
TakeScreenshot calls and puts a waiter in.

[1] https://source.chromium.org/chromium/chromium/src/+/master:components/viz/test/fake_skia_output_surface.cc;drc=361ef8a161b516ba106966bb0740314c41dc2678;l=272

Original change's description:
> desks: Add tests for RootWindowDeskSwitchAnimator.
>
> Test: added
> Bug: 1111445
> Change-Id: I0f79be1c09d4ef0eeff276e3c50f491efbe56244
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380517
> Reviewed-by: Khushal <khushalsagar@chromium.org>
> Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
> Commit-Queue: Sammie Quon <sammiequon@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#809854}

Bug: 1111445
Change-Id: Ib4e50c22fb063b82f50c3f4f79bb026e648a6157
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2429363
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810754}
This commit is contained in:
Sammie Quon
2020-09-25 19:09:02 +00:00
committed by Commit Bot
parent 7a32afe137
commit 341bf50b0b
6 changed files with 444 additions and 10 deletions

@@ -2174,6 +2174,7 @@ test("ash_unittests") {
"wm/default_window_resizer_unittest.cc",
"wm/desks/autotest_desks_api_unittests.cc",
"wm/desks/desks_unittests.cc",
"wm/desks/root_window_desk_switch_animator_unittest.cc",
"wm/drag_window_resizer_unittest.cc",
"wm/fullscreen_window_finder_unittest.cc",
"wm/gestures/back_gesture/back_gesture_affordance_unittest.cc",
@@ -2543,6 +2544,8 @@ static_library("test_support") {
"wm/cursor_manager_test_api.h",
"wm/desks/desks_test_util.cc",
"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.h",
"wm/lock_state_controller_test_api.cc",

@@ -29,9 +29,6 @@ namespace ash {
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
// starting or the ending desks. After this maximum number is reached, we ignore
// a failed screenshot request and proceed with next phases.
@@ -50,12 +47,6 @@ constexpr base::TimeDelta kAnimationDuration =
// desk change.
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,
// is translated vertically during the for-remove desk switch animation.
constexpr int kRemovedDeskWindowYTranslation = 20;

@@ -7,6 +7,7 @@
#include <memory>
#include "ash/ash_export.h"
#include "base/memory/weak_ptr.h"
#include "ui/compositor/layer_animation_observer.h"
@@ -157,7 +158,8 @@ namespace ash {
// slightly more to accommodate the continuous desk animations feature. The base
// algorithm is still valid, but once the features are near completion these
// need to be updated.
class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
class ASH_EXPORT RootWindowDeskSwitchAnimator
: public ui::ImplicitAnimationObserver {
public:
class Delegate {
public:
@@ -179,6 +181,15 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
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,
int starting_desk_index,
int ending_desk_index,
@@ -235,6 +246,8 @@ class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver {
void OnImplicitAnimationsCompleted() override;
private:
friend class RootWindowDeskSwitchAnimatorTestApi;
// 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
// animation layer, which will be setup with its initial transform according

@@ -0,0 +1,43 @@
// 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 "ash/wm/desks/root_window_desk_switch_animator.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_tree_owner.h"
namespace ash {
RootWindowDeskSwitchAnimatorTestApi::RootWindowDeskSwitchAnimatorTestApi(
RootWindowDeskSwitchAnimator* animator)
: animator_(animator) {
DCHECK(animator_);
}
RootWindowDeskSwitchAnimatorTestApi::~RootWindowDeskSwitchAnimatorTestApi() =
default;
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

@@ -0,0 +1,40 @@
// 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();
// 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_

@@ -0,0 +1,344 @@
// 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/callback_forward.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());
}
// Wrappers for Take{Starting|Ending}DeskScreenshot that wait for the async
// operation to finish.
void TakeStartingDeskScreenshotAndWait() {
base::RunLoop run_loop;
run_loop_quit_closure_ = run_loop.QuitClosure();
animator_->TakeStartingDeskScreenshot();
run_loop.Run();
}
void TakeEndingDeskScreenshotAndWait() {
base::RunLoop run_loop;
run_loop_quit_closure_ = run_loop.QuitClosure();
animator_->TakeEndingDeskScreenshot();
run_loop.Run();
}
// 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_;
DCHECK(!run_loop_quit_closure_.is_null());
std::move(run_loop_quit_closure_).Run();
}
void OnEndingDeskScreenshotTaken() override {
++ending_desk_screenshot_taken_count_;
DCHECK(!run_loop_quit_closure_.is_null());
std::move(run_loop_quit_closure_).Run();
}
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_;
// Run loop quit closure for waiting for both starting and ending screenshots.
base::OnceClosure run_loop_quit_closure_;
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);
TakeStartingDeskScreenshotAndWait();
TakeEndingDeskScreenshotAndWait();
// 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);
TakeStartingDeskScreenshotAndWait();
TakeEndingDeskScreenshotAndWait();
// 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);
TakeStartingDeskScreenshotAndWait();
TakeEndingDeskScreenshotAndWait();
// 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.
TakeEndingDeskScreenshotAndWait();
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);
TakeStartingDeskScreenshotAndWait();
TakeEndingDeskScreenshotAndWait();
// 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.
TakeEndingDeskScreenshotAndWait();
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);
TakeStartingDeskScreenshotAndWait();
TakeEndingDeskScreenshotAndWait();
// 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))
TakeEndingDeskScreenshotAndWait();
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