[go: nahoru, domu]

blob: 4eae1d88052759ae874cfa6a4d24f328a5e54c9a [file] [log] [blame]
// Copyright 2021 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 "ui/views/animation/animation_builder.h"
#include "base/bind.h"
#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/layer_owner.h"
#include "ui/compositor/property_change_reason.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/layer_animator_test_controller.h"
#include "ui/compositor/test/test_layer_animation_delegate.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/animation/animation_abort_handle.h"
namespace views {
namespace {
class TestAnimatibleLayerOwner : public ui::LayerOwner {
public:
TestAnimatibleLayerOwner() : ui::LayerOwner(std::make_unique<ui::Layer>()) {
layer()->GetAnimator()->set_disable_timer_for_test(true);
layer()->GetAnimator()->SetDelegate(&delegate_);
}
ui::LayerAnimationDelegate* delegate() { return &delegate_; }
private:
ui::TestLayerAnimationDelegate delegate_;
};
// Configures the layer animation on `layer_owner` and returns the builder.
AnimationBuilder BuildLayerOpacityAnimationAndReturnBuilder(
ui::LayerOwner* layer_owner,
const base::TimeDelta& duration) {
EXPECT_NE(0.f, layer_owner->layer()->opacity());
AnimationBuilder builder;
builder.Once().SetDuration(duration).SetOpacity(layer_owner, 0.f);
return builder;
}
} // namespace
class AnimationBuilderTest : public testing::Test {
public:
AnimationBuilderTest() = default;
TestAnimatibleLayerOwner* CreateTestLayerOwner() {
layer_owners_.push_back(std::make_unique<TestAnimatibleLayerOwner>());
animator_controllers_.push_back(
std::make_unique<ui::LayerAnimatorTestController>(
layer_owners_.back()->layer()->GetAnimator()));
return layer_owners_.back().get();
}
void Step(const base::TimeDelta& duration) {
DCHECK_GT(duration, base::TimeDelta());
for (const auto& controller : animator_controllers_) {
controller->StartThreadedAnimationsIfNeeded(
controller->animator()->last_step_time());
controller->Step(duration);
}
elapsed_ += duration;
}
protected:
void SetUp() override {
testing::Test::SetUp();
AnimationBuilder::SetObserverDeletedCallbackForTesting(base::BindRepeating(
[](int* deleted_count) { ++(*deleted_count); }, &deleted_observers_));
}
void TearDown() override {
testing::Test::TearDown();
// Delete the layer owners and animator controllers here to ensure any
// lingering animations are aborted and all the observers are destroyed.
layer_owners_.clear();
animator_controllers_.clear();
AnimationBuilder::SetObserverDeletedCallbackForTesting(
base::NullCallback());
if (expected_observers_deleted_)
EXPECT_EQ(expected_observers_deleted_.value(), deleted_observers_);
}
// Call this function to also ensure any implicitly created observers have
// also been properly cleaned up. One observer is created per
// AnimationSequenceBlock which sets callbacks.
void set_expected_observers_deleted(int expected_observers_deleted) {
expected_observers_deleted_ = expected_observers_deleted;
}
private:
std::vector<std::unique_ptr<TestAnimatibleLayerOwner>> layer_owners_;
std::vector<std::unique_ptr<ui::LayerAnimatorTestController>>
animator_controllers_;
base::TimeDelta elapsed_;
absl::optional<int> expected_observers_deleted_;
int deleted_observers_ = 0;
};
// This test builds two animation sequences and checks that the properties are
// animated in the specified durations.
TEST_F(AnimationBuilderTest, SimpleAnimation) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* first_delegate = first_animating_view->delegate();
ui::LayerAnimationDelegate* second_delegate =
second_animating_view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
constexpr auto kDelay = base::Seconds(3);
{
AnimationBuilder()
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.SetRoundedCorners(first_animating_view, rounded_corners)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f);
}
// Original value before the animation steps.
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 1.0);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
0.0);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 1.0);
Step(kDelay);
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
// Sanity check one of the corners.
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
// This animation should not be finished yet.
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
}
// This test checks that after setting the animation duration scale to be larger
// than 1, animations behave as expected of that scale.
TEST_F(AnimationBuilderTest, ModifiedSlowAnimationDuration) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* first_delegate = first_animating_view->delegate();
ui::LayerAnimationDelegate* second_delegate =
second_animating_view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
constexpr auto kDelay = base::Seconds(3);
{
AnimationBuilder()
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.SetRoundedCorners(first_animating_view, rounded_corners)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f)
.Then()
.SetDuration(kDelay)
.Then()
.SetDuration(kDelay)
.SetOpacity(second_animating_view, 0.4f);
}
Step(kDelay * ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
// Sanity check one of the corners.
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
// This animation should not be finished yet.
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * 2 * ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
// This test checks that after setting the animation duration scale to be
// between 0 and 1, animations behave as expected of that scale.
TEST_F(AnimationBuilderTest, ModifiedFastAnimationDuration) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* first_delegate = first_animating_view->delegate();
ui::LayerAnimationDelegate* second_delegate =
second_animating_view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
constexpr auto kDelay = base::Seconds(3);
{
AnimationBuilder()
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.SetRoundedCorners(first_animating_view, rounded_corners)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f)
.Then()
.SetDuration(kDelay)
.Then()
.SetDuration(kDelay)
.SetOpacity(second_animating_view, 0.4f);
}
Step(kDelay * ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
// Sanity check one of the corners.
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
// This animation should not be finished yet.
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * 2 * ui::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
// This test checks that after setting the animation duration scale to be 0,
// animations behave as expected of that scale.
TEST_F(AnimationBuilderTest, ModifiedZeroAnimationDuration) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* first_delegate = first_animating_view->delegate();
ui::LayerAnimationDelegate* second_delegate =
second_animating_view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
constexpr auto kDelay = base::Seconds(3);
{
AnimationBuilder()
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.SetRoundedCorners(first_animating_view, rounded_corners)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f)
.Then()
.SetDuration(kDelay)
.Then()
.SetDuration(kDelay)
.SetOpacity(second_animating_view, 0.4f);
}
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
// Sanity check one of the corners.
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
// This test checks that the callback supplied to .OnEnded is not called before
// all sequences have finished running. This test will crash if .OnEnded is
// called prematurely.
TEST_F(AnimationBuilderTest, ModifiedZeroAnimationDurationWithOnEndedCallback) {
ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
auto first_animating_view = std::make_unique<TestAnimatibleLayerOwner>();
auto second_animating_view = std::make_unique<TestAnimatibleLayerOwner>();
views::AnimationBuilder b;
b.OnEnded(base::BindRepeating(
[](TestAnimatibleLayerOwner* layer_owner,
TestAnimatibleLayerOwner* second_layer_owner) {
delete layer_owner;
delete second_layer_owner;
},
first_animating_view.get(), second_animating_view.get()))
.Once()
.SetDuration(base::Seconds(3))
.SetOpacity(first_animating_view.get(), 0.4f)
.SetOpacity(second_animating_view.get(), 0.9f);
first_animating_view.release();
second_animating_view.release();
}
TEST_F(AnimationBuilderTest, ZeroDurationBlock) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* first_delegate = first_animating_view->delegate();
gfx::RoundedCornersF first_corners(6.0f, 6.0f, 6.0f, 6.0f);
gfx::RoundedCornersF second_corners(12.0f, 12.0f, 12.0f, 12.0f);
constexpr auto kDelay = base::Seconds(3);
{
AnimationBuilder()
.Once()
.SetDuration(base::TimeDelta())
.SetRoundedCorners(first_animating_view, first_corners)
.Then()
.SetDuration(kDelay)
.SetRoundedCorners(first_animating_view, second_corners)
.Then()
.SetDuration(base::TimeDelta())
.SetRoundedCorners(first_animating_view, first_corners);
}
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
6.0f);
Step(kDelay / 2);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
9.0f);
Step(kDelay / 2);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
6.0f);
}
TEST_F(AnimationBuilderTest, CheckTweenType) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
gfx::Tween::Type tween_type = gfx::Tween::EASE_IN;
constexpr auto kDelay = base::Seconds(4);
// Set initial opacity.
first_animating_view->delegate()->SetOpacityFromAnimation(
0.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
constexpr float opacity_end_val = 0.5f;
{
AnimationBuilder().Once().SetDuration(kDelay).SetOpacity(
first_animating_view, opacity_end_val, tween_type);
}
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
Step(kDelay / 2);
// Force an update to the delegate by aborting the animation.
first_animating_view->layer()->GetAnimator()->AbortAllAnimations();
// Values at intermediate steps may not be exact.
EXPECT_NEAR(gfx::Tween::CalculateValue(tween_type, 0.5) * opacity_end_val,
first_animating_view->delegate()->GetOpacityForAnimation(),
0.001f);
}
// Verify that destroying the layers tracked by the animation abort handle
// before the animation ends should not cause any crash.
TEST_F(AnimationBuilderTest, DestroyLayerBeforeAnimationEnd) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
std::unique_ptr<AnimationAbortHandle> abort_handle;
{
AnimationBuilder builder;
abort_handle = builder.GetAbortHandle();
builder.Once()
.SetDuration(base::Seconds(3))
.SetOpacity(first_animating_view, 0.5f)
.SetOpacity(second_animating_view, 0.5f);
}
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
first_animating_view->ReleaseLayer();
second_animating_view->ReleaseLayer();
}
// Verify that destroying layers tracked by the animation abort handle when
// the animation ends should not cause any crash.
TEST_F(AnimationBuilderTest, DestroyLayerWhenAnimationEnd) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
auto end_callback = [](TestAnimatibleLayerOwner* first_animating_view,
TestAnimatibleLayerOwner* second_animating_view) {
first_animating_view->ReleaseLayer();
second_animating_view->ReleaseLayer();
};
constexpr auto kDelay = base::Seconds(3);
std::unique_ptr<AnimationAbortHandle> abort_handle;
{
AnimationBuilder builder;
abort_handle = builder.GetAbortHandle();
builder
.OnEnded(base::BindOnce(end_callback, first_animating_view,
second_animating_view))
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.5f)
.SetOpacity(second_animating_view, 0.5f);
}
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
Step(kDelay * 2);
// Verify that layers are destroyed when the animation ends.
EXPECT_FALSE(first_animating_view->layer());
EXPECT_FALSE(second_animating_view->layer());
}
// Verify that destroying layers tracked by the animation abort handle when
// the animation is aborted should not cause any crash.
TEST_F(AnimationBuilderTest, DestroyLayerWhenAnimationAborted) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
auto abort_callback = [](TestAnimatibleLayerOwner* first_animating_view,
TestAnimatibleLayerOwner* second_animating_view) {
first_animating_view->ReleaseLayer();
second_animating_view->ReleaseLayer();
};
constexpr auto kDelay = base::Seconds(3);
std::unique_ptr<AnimationAbortHandle> abort_handle;
{
AnimationBuilder builder;
abort_handle = builder.GetAbortHandle();
builder
.OnAborted(base::BindOnce(abort_callback, first_animating_view,
second_animating_view))
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.5f)
.SetOpacity(second_animating_view, 0.5f);
}
Step(0.5 * kDelay);
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
// Abort the animation in the half way.
first_animating_view->layer()->GetAnimator()->AbortAllAnimations();
// Verify that layers are destroyed by the animation abortion callback.
EXPECT_FALSE(first_animating_view->layer());
EXPECT_FALSE(second_animating_view->layer());
}
TEST_F(AnimationBuilderTest, CheckStartEndCallbacks) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
constexpr auto kDelay = base::Seconds(3);
bool started = false;
bool ended = false;
{
AnimationBuilder()
.OnStarted(
base::BindOnce([](bool* started) { *started = true; }, &started))
.OnEnded(base::BindOnce([](bool* ended) { *ended = true; }, &ended))
.Once()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f)
.Then()
.SetDuration(kDelay)
.SetOpacity(second_animating_view, 0.4f);
}
// Only one Observer should have been created in the above block. Make sure
// it has been cleaned up.
set_expected_observers_deleted(1);
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(started);
Step(kDelay * 2);
EXPECT_FALSE(ended);
Step(kDelay);
EXPECT_TRUE(ended);
}
// This test checks that repeat callbacks are called after each sequence
// repetition and callbacks from one sequence do not affect calls from another
// sequence.
TEST_F(AnimationBuilderTest, CheckOnWillRepeatCallbacks) {
int first_repeat_count = 0;
int second_repeat_count = 0;
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
constexpr auto kDelay = base::Seconds(3);
gfx::RoundedCornersF first_rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
gfx::RoundedCornersF second_rounded_corners(5.0f, 5.0f, 5.0f, 5.0f);
{
AnimationBuilder b;
b.OnWillRepeat(base::BindRepeating([](int& repeat) { repeat = repeat + 1; },
std::ref(first_repeat_count)))
.Repeatedly()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.Then()
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.9f);
b.OnWillRepeat(base::BindRepeating([](int& repeat) { repeat = repeat + 1; },
std::ref(second_repeat_count)))
.Repeatedly()
.SetDuration(kDelay)
.SetRoundedCorners(first_animating_view, first_rounded_corners)
.Then()
.SetDuration(kDelay)
.SetRoundedCorners(first_animating_view, second_rounded_corners);
}
set_expected_observers_deleted(2);
Step(kDelay * 2);
EXPECT_EQ(first_repeat_count, 1);
EXPECT_EQ(second_repeat_count, 1);
Step(kDelay * 2);
EXPECT_EQ(first_repeat_count, 2);
EXPECT_EQ(second_repeat_count, 2);
}
// We use these notations to illustrate the tested timeline,
// Pause: ---|
// KeyFrame: -->|
// Repeat: [...]
//
// Opacity ---|-->|
TEST_F(AnimationBuilderTest, DelayedStart) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = base::Seconds(1);
{
// clang-format off
AnimationBuilder()
.Once()
.At(kDelay)
.SetDuration(kDuration)
.SetOpacity(view, 0.4f);
// clang-format on
}
Step(kDelay);
// The animation on opacity is not yet started.
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
}
// Opacity -->|-->|
TEST_F(AnimationBuilderTest, TwoKeyFrame) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f);
}
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
}
// Opacity -->|---|-->|
TEST_F(AnimationBuilderTest, PauseInTheMiddle) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.Offset(kDuration)
.SetDuration(kDuration)
.SetOpacity(view, 0.9f);
}
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
}
// Opacity -->|
// RoundedCorners ----->|
TEST_F(AnimationBuilderTest, TwoPropertiesOfDifferentDuration) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
// Make sure that the opacity keyframe finishes at the middle of the rounded
// corners keyframe.
constexpr auto kDurationShort = base::Seconds(1);
constexpr auto kDurationLong = kDurationShort * 2;
{
AnimationBuilder()
.Once()
.SetDuration(kDurationShort)
.SetOpacity(view, 0.4f)
.At(base::TimeDelta())
.SetDuration(kDurationLong)
.SetRoundedCorners(view, rounded_corners);
}
Step(kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 6.0f);
Step(kDurationLong - kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
}
// Opacity ----->|
// RoundedCorners ----->|
TEST_F(AnimationBuilderTest, TwoPropertiesOfDifferentStartTime) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
// Make sure that the opacity keyframe finishes at the middle of the rounded
// corners keyframe.
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = kDelay * 2;
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.At(kDelay)
.SetDuration(kDuration)
.SetRoundedCorners(view, rounded_corners);
}
Step(kDelay);
// Unfortunately, we can't test threaded animations in the midst of a frame
// because they don't update LayerAnimationDelegate in OnProgress().
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 0.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 6.0f);
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
}
// Opacity ----->|---|-->|
// RoundedCorners ----->|-->|
TEST_F(AnimationBuilderTest, ThenAddsImplicitPause) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners1(12.0f, 12.0f, 12.0f, 12.0f);
gfx::RoundedCornersF rounded_corners2(5.0f, 5.0f, 5.0f, 5.0f);
// Make sure that the first opacity keyframe finishes at the middle of the
// first rounded corners keyframe.
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = kDelay * 2;
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.At(kDelay)
.SetDuration(kDuration)
.SetRoundedCorners(view, rounded_corners1)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f)
.SetRoundedCorners(view, rounded_corners2);
}
Step(kDelay);
// Unfortunately, we can't test threaded animations in the midst of a frame
// because they don't update LayerAnimationDelegate in OnProgress().
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 0.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 6.0f);
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 5.0f);
}
// Opacity [-->|-->]
TEST_F(AnimationBuilderTest, Repeat) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Repeatedly()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f);
}
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
}
// Opacity [-->|-->| ]
TEST_F(AnimationBuilderTest, RepeatWithExplicitTrailingPause) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Repeatedly()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f)
.Then()
.SetDuration(kDuration);
}
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
}
// Opacity [-->|-->]
// RoundedCorners [-->|-->]
TEST_F(AnimationBuilderTest, RepeatTwoProperties) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners1(12.0f, 12.0f, 12.0f, 12.0f);
gfx::RoundedCornersF rounded_corners2(5.0f, 5.0f, 5.0f, 5.0f);
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Repeatedly()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.SetRoundedCorners(view, rounded_corners1)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f)
.SetRoundedCorners(view, rounded_corners2);
}
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 5.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 5.0);
}
// Opacity -->|-->|
// RoundedCorners -->|-->|
TEST_F(AnimationBuilderTest, AtCanSkipThenBlock) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners1(12.0f, 12.0f, 12.0f, 12.0f);
gfx::RoundedCornersF rounded_corners2(4.0f, 4.0f, 4.0f, 4.0f);
// Make sure that the first opacity keyframe finishes at the middle of the
// first rounded corners keyframe.
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = kDelay * 2;
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f)
.At(kDelay)
.SetDuration(kDuration)
.SetRoundedCorners(view, rounded_corners1)
.Then()
.SetDuration(kDuration)
.SetRoundedCorners(view, rounded_corners2);
}
Step(kDelay);
// Unfortunately, we can't test threaded animations in the midst of a frame
// because they don't update LayerAnimationDelegate in OnProgress().
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 0.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 6.0);
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 8.0);
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 4.0);
}
// Opacity -->|-->|
// RoundedCorners -->|
TEST_F(AnimationBuilderTest, OffsetCanRewindTime) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
// Make sure that the first opacity keyframe finishes at the middle of the
// first rounded corners keyframe.
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = kDelay * 2;
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f)
.Offset(kDelay - kDuration)
.SetDuration(kDuration)
.SetRoundedCorners(view, rounded_corners);
}
Step(kDelay);
// Unfortunately, we can't test threaded animations in the midst of a frame
// because they don't update LayerAnimationDelegate in OnProgress().
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 0.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 6.0);
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDuration - kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
}
// Opacity [-->|--> ]
// RoundedCorners [-->|---->]
TEST_F(AnimationBuilderTest, RepeatedlyImplicitlyAppendsTrailingPause) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners1(12.0f, 12.0f, 12.0f, 12.0f);
gfx::RoundedCornersF rounded_corners2(4.0f, 4.0f, 4.0f, 4.0f);
// Make sure that the second opacity keyframe finishes at the middle of the
// second rounded corners keyframe.
constexpr auto kDurationShort = base::Seconds(1);
constexpr auto kDurationLong = kDurationShort * 2;
{
AnimationBuilder()
.Repeatedly()
.SetDuration(kDurationShort)
.SetOpacity(view, 0.4f)
.SetRoundedCorners(view, rounded_corners1)
.Then()
.SetDuration(kDurationShort)
.SetOpacity(view, 0.9f)
.Offset(base::TimeDelta())
.SetDuration(kDurationLong)
.SetRoundedCorners(view, rounded_corners2);
}
Step(kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 8.0);
Step(kDurationLong - kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 4.0);
// Repeat
Step(kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 12.0);
Step(kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 8.0);
Step(kDurationLong - kDurationShort);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
EXPECT_FLOAT_EQ(delegate->GetRoundedCornersForAnimation().upper_left(), 4.0);
}
// Opacity -->|-->|--> with a loop for setting these blocks.
TEST_F(AnimationBuilderTest, RepeatedBlocks) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::Seconds(1);
constexpr float kOpacity[] = {0.4f, 0.9f, 0.6f};
{
AnimationBuilder builder;
builder.Repeatedly();
for (const auto& opacity : kOpacity) {
builder.GetCurrentSequence()
.SetDuration(kDuration)
.SetOpacity(view, opacity)
.Then();
}
}
for (const auto& opacity : kOpacity) {
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), opacity);
}
}
TEST_F(AnimationBuilderTest, PreemptionStrategyTest) {
using ps = ui::LayerAnimator::PreemptionStrategy;
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kStepSize = base::Seconds(1);
constexpr auto kDuration = base::Seconds(5);
// Set the initial value to animate.
delegate->SetBrightnessFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
{
AnimationBuilder().Once().SetDuration(kDuration).SetBrightness(view, 0.0f);
}
// The animation hasn't started.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
// Step the animation, but don't complete it.
Step(kStepSize);
// Make sure the animation is progressing.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
// Make sure we're still animatiing.
EXPECT_TRUE(view->layer()->GetAnimator()->is_animating());
// Now start a new animation to a different target.
{
AnimationBuilder()
.SetPreemptionStrategy(ps::IMMEDIATELY_SET_NEW_TARGET)
.Once()
.SetDuration(
kStepSize) // We only moved previous animation by kStepSize
.SetBrightness(view, 1.0f);
}
Step(kStepSize);
// The above animation should have been aborted, and set the brightness to the
// new target immediately.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
EXPECT_FALSE(view->layer()->GetAnimator()->is_animating());
// Start another animation which we'll preemtp to test another strategy.
{
AnimationBuilder().Once().SetDuration(kDuration).SetBrightness(view, 0.0f);
}
// This should start out like the one above.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
Step(kStepSize);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
EXPECT_TRUE(view->layer()->GetAnimator()->is_animating());
{
AnimationBuilder()
.SetPreemptionStrategy(ps::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
.Once()
.SetDuration(kStepSize)
.SetBrightness(view, 1.0f);
}
// The new animation should pick up where the last one left off.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
Step(kStepSize);
// The new animation is in force if it steps toward the new target.
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
// Make sure the animation is fully complete.
Step(kStepSize);
// The animation should be done now.
EXPECT_FALSE(view->layer()->GetAnimator()->is_animating());
}
TEST_F(AnimationBuilderTest, AbortHandle) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
std::unique_ptr<AnimationAbortHandle> abort_handle;
constexpr auto kStepSize = base::Seconds(1);
constexpr auto kDuration = kStepSize * 2;
{
delegate->SetBrightnessFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
delegate->SetOpacityFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
{
AnimationBuilder b;
abort_handle = b.GetAbortHandle();
b.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.At(base::TimeDelta())
.SetDuration(kDuration)
.SetBrightness(view, 0.4f);
}
Step(kStepSize);
// Destroy abort handle should stop all animations.
abort_handle.reset();
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.7f);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.7f);
Step(kStepSize);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.7f);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.7f);
}
// The builder crashes if the handle is destroyed before animation starts.
{
delegate->SetBrightnessFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
delegate->SetOpacityFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
{
AnimationBuilder b;
abort_handle = b.GetAbortHandle();
b.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.At(base::TimeDelta())
.SetDuration(kDuration)
.SetBrightness(view, 0.4f);
// Early destroy should crash the builder.
EXPECT_DCHECK_DEATH(abort_handle.reset());
}
}
// The handle shouldn't abort animations subsequent to the builder.
{
delegate->SetBrightnessFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
delegate->SetOpacityFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
{
AnimationBuilder b;
abort_handle = b.GetAbortHandle();
b.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.At(base::TimeDelta())
.SetDuration(kDuration)
.SetBrightness(view, 0.4f);
}
EXPECT_EQ(abort_handle->animation_state(),
AnimationAbortHandle::AnimationState::kRunning);
// Step to the end of the animation.
Step(kDuration);
EXPECT_EQ(abort_handle->animation_state(),
AnimationAbortHandle::AnimationState::kEnded);
{
AnimationBuilder b;
b.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.8f)
.At(base::TimeDelta())
.SetDuration(kDuration)
.SetBrightness(view, 0.8f);
}
// Destroy the handle on the finihsed animation shouldn't affect other
// unfinihsed animations.
abort_handle.reset();
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.8f);
}
}
// Verifies that configuring layer animations with an animation builder returned
// from a function works as expected.
TEST_F(AnimationBuilderTest, BuildAnimationWithBuilderFromScope) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
EXPECT_EQ(1.f, first_animating_view->layer()->opacity());
EXPECT_EQ(1.f, second_animating_view->layer()->opacity());
constexpr auto kDuration = base::Seconds(3);
{
// Build a layer animation on `second_animating_view` with a builder
// returned from a function.
AnimationBuilder builder = BuildLayerOpacityAnimationAndReturnBuilder(
first_animating_view, kDuration);
builder.GetCurrentSequence().SetOpacity(second_animating_view, 0.f);
}
// Verify that both views are under animation.
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
Step(kDuration);
// Verify that after `kDuration` time, both layer animations end. In addition,
// both layers are set with the target opacity.
EXPECT_FALSE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(second_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_EQ(0.f, first_animating_view->delegate()->GetOpacityForAnimation());
EXPECT_EQ(0.f, second_animating_view->delegate()->GetOpacityForAnimation());
}
} // namespace views