#include "ui/views/animation/animation_builder.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.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/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/gfx/geometry/transform.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.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_;
};
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;
}
}
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();
layer_owners_.clear();
animator_controllers_.clear();
AnimationBuilder::SetObserverDeletedCallbackForTesting(
base::NullCallback());
if (expected_observers_deleted_) {
EXPECT_EQ(expected_observers_deleted_.value(), deleted_observers_);
}
}
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_;
std::optional<int> expected_observers_deleted_;
int deleted_observers_ = 0;
};
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);
}
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);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
}
TEST_F(AnimationBuilderTest, ModifiedSlowAnimationDuration) {
gfx::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
gfx::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 * gfx::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * gfx::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * 2 * gfx::ScopedAnimationDurationScaleMode::SLOW_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
TEST_F(AnimationBuilderTest, ModifiedFastAnimationDuration) {
gfx::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
gfx::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 * gfx::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(first_delegate->GetOpacityForAnimation(), 0.4f);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
EXPECT_NE(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * gfx::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.9f);
Step(kDelay * 2 * gfx::ScopedAnimationDurationScaleMode::FAST_DURATION);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
TEST_F(AnimationBuilderTest, ModifiedZeroAnimationDuration) {
gfx::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
gfx::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);
EXPECT_FLOAT_EQ(first_delegate->GetRoundedCornersForAnimation().upper_left(),
12.0f);
EXPECT_FLOAT_EQ(second_delegate->GetOpacityForAnimation(), 0.4f);
}
TEST_F(AnimationBuilderTest, ModifiedZeroAnimationDurationWithOnEndedCallback) {
gfx::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
gfx::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);
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);
first_animating_view->layer()->GetAnimator()->AbortAllAnimations();
EXPECT_NEAR(gfx::Tween::CalculateValue(tween_type, 0.5) * opacity_end_val,
first_animating_view->delegate()->GetOpacityForAnimation(),
0.001f);
}
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();
}
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);
EXPECT_FALSE(first_animating_view->layer());
EXPECT_FALSE(second_animating_view->layer());
}
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());
first_animating_view->layer()->GetAnimator()->AbortAllAnimations();
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);
}
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);
}
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);
}
TEST_F(AnimationBuilderTest, DelayedStart) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDelay = base::Seconds(1);
constexpr auto kDuration = base::Seconds(1);
{
AnimationBuilder()
.Once()
.At(kDelay)
.SetDuration(kDuration)
.SetOpacity(view, 0.4f);
}
Step(kDelay);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
}
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);
}
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);
}
TEST_F(AnimationBuilderTest, TwoPropertiesOfDifferentDuration) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
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);
}
TEST_F(AnimationBuilderTest, TwoPropertiesOfDifferentStartTime) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
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);
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);
}
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);
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);
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);
}
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);
}
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);
}
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);
}
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);
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);
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);
}
TEST_F(AnimationBuilderTest, OffsetCanRewindTime) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
gfx::RoundedCornersF rounded_corners(12.0f, 12.0f, 12.0f, 12.0f);
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);
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);
}
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);
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);
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);
}
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);
delegate->SetBrightnessFromAnimation(
1.0f, ui::PropertyChangeReason::NOT_FROM_ANIMATION);
{
AnimationBuilder().Once().SetDuration(kDuration).SetBrightness(view, 0.0f);
}
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_SET_NEW_TARGET)
.Once()
.SetDuration(
kStepSize)
.SetBrightness(view, 1.0f);
}
Step(kStepSize);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
EXPECT_FALSE(view->layer()->GetAnimator()->is_animating());
{
AnimationBuilder().Once().SetDuration(kDuration).SetBrightness(view, 0.0f);
}
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);
}
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
Step(kStepSize);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 1.0f);
Step(kStepSize);
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);
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);
}
{
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_DCHECK_DEATH(abort_handle.reset());
}
}
{
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(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);
}
abort_handle.reset();
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetBrightnessForAnimation(), 0.8f);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.8f);
}
}
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);
{
AnimationBuilder builder = BuildLayerOpacityAnimationAndReturnBuilder(
first_animating_view, kDuration);
builder.GetCurrentSequence().SetOpacity(second_animating_view, 0.f);
}
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(second_animating_view->layer()->GetAnimator()->is_animating());
Step(kDuration);
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());
}
TEST_F(AnimationBuilderTest,
DisallowMultipleSameBlockSameTargetTransformPropertyAnimations) {
TestAnimatibleLayerOwner* target = CreateTestLayerOwner();
EXPECT_DCHECK_DEATH_WITH(
AnimationBuilder()
.Once()
.SetInterpolatedTransform(
target, std::make_unique<ui::InterpolatedMatrixTransform>(
gfx::Transform(),
gfx::Transform()))
.SetTransform(target, gfx::Transform()),
"Animate \\(target, property\\) at most once per block.");
}
TEST_F(AnimationBuilderTest, SetInterpolatedTransform) {
auto transform = std::make_unique<ui::InterpolatedScale>(
0.f, 1.f, -1.f,
2.f);
transform->SetChild(std::make_unique<ui::InterpolatedRotation>(
0.f, 360.f, 0.5f,
0.75f));
const gfx::Transform expected_start_transform = transform->Interpolate(0.f);
const gfx::Transform expected_mid_transform = transform->Interpolate(0.5f);
const gfx::Transform expected_end_transform = transform->Interpolate(1.f);
TestAnimatibleLayerOwner* target = CreateTestLayerOwner();
EXPECT_EQ(target->delegate()->GetTransformForAnimation(), gfx::Transform());
constexpr auto kDuration = base::Seconds(2);
AnimationBuilder().Once().SetDuration(kDuration).SetInterpolatedTransform(
target, std::move(transform));
EXPECT_TRUE(target->layer()->GetAnimator()->is_animating());
EXPECT_EQ(target->delegate()->GetTransformForAnimation(),
expected_start_transform);
Step(kDuration / 2);
EXPECT_TRUE(target->layer()->GetAnimator()->is_animating());
EXPECT_EQ(target->delegate()->GetTransformForAnimation(),
expected_mid_transform);
Step(kDuration / 2);
EXPECT_FALSE(target->layer()->GetAnimator()->is_animating());
EXPECT_EQ(target->delegate()->GetTransformForAnimation(),
expected_end_transform);
}
}