blob: d32fd21cdc22b129bc8bc068f1a2988506cd4df9 [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 "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/test/layer_animator_test_controller.h"
#include "ui/compositor/test/test_layer_animation_delegate.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
namespace views {
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_;
};
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;
}
private:
std::vector<std::unique_ptr<TestAnimatibleLayerOwner>> layer_owners_;
std::vector<std::unique_ptr<ui::LayerAnimatorTestController>>
animator_controllers_;
base::TimeDelta elapsed_;
};
// 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::TimeDelta::FromSeconds(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);
}
TEST_F(AnimationBuilderTest, CheckTweenType) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
gfx::Tween::Type tween_type = gfx::Tween::EASE_IN;
constexpr auto kDelay = base::TimeDelta::FromSeconds(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);
}
TEST_F(AnimationBuilderTest, CheckStartEndCallbacks) {
TestAnimatibleLayerOwner* first_animating_view = CreateTestLayerOwner();
TestAnimatibleLayerOwner* second_animating_view = CreateTestLayerOwner();
constexpr auto kDelay = base::TimeDelta::FromSeconds(3);
bool started = false;
bool ended = false;
{
AnimationBuilder()
.Once()
.OnStarted(
base::BindOnce([](bool* started) { *started = true; }, &started))
.OnEnded(base::BindOnce([](bool* ended) { *ended = true; }, &ended))
.SetDuration(kDelay)
.SetOpacity(first_animating_view, 0.4f)
.Offset(base::TimeDelta())
.SetDuration(kDelay * 2)
.SetOpacity(second_animating_view, 0.9f);
}
EXPECT_TRUE(first_animating_view->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(started);
Step(kDelay * 2);
EXPECT_TRUE(ended);
}
TEST_F(AnimationBuilderTest, DelayedStart) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDelay = base::TimeDelta::FromSeconds(1);
constexpr auto kDuration = base::TimeDelta::FromSeconds(1);
{
// clang-format off
AnimationBuilder()
.Once()
.At(kDelay)
.SetDuration(kDuration)
.SetOpacity(view, 0.4f);
// clang-format on
}
// Original value before the animation steps.
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
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);
}
TEST_F(AnimationBuilderTest, TwoKeyFrame) {
TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
ui::LayerAnimationDelegate* delegate = view->delegate();
constexpr auto kDuration = base::TimeDelta::FromSeconds(1);
{
AnimationBuilder()
.Once()
.SetDuration(kDuration)
.SetOpacity(view, 0.4f)
.Then()
.SetDuration(kDuration)
.SetOpacity(view, 0.9f);
}
// The animation on opacity is not yet started.
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
Step(kDuration);
EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
}
} // namespace views