blob: 446a57db6bfd1be4785754dbb3a85a22358af07d [file] [log] [blame]
// 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 "ui/compositor/animation_throughput_reporter.h"
#include <memory>
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "cc/metrics/frame_sequence_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/compositor/test/test_compositor_host.h"
#include "ui/compositor/test/test_context_factories.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
class AnimationThroughputReporterTest : public testing::Test {
public:
AnimationThroughputReporterTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {}
AnimationThroughputReporterTest(const AnimationThroughputReporterTest&) =
delete;
AnimationThroughputReporterTest& operator=(
const AnimationThroughputReporterTest&) = delete;
~AnimationThroughputReporterTest() override = default;
// testing::Test:
void SetUp() override {
context_factories_ = std::make_unique<TestContextFactories>(false);
const gfx::Rect bounds(100, 100);
host_.reset(TestCompositorHost::Create(
bounds, context_factories_->GetContextFactory()));
host_->Show();
compositor()->SetRootLayer(&root_);
frame_generation_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(50), this,
&AnimationThroughputReporterTest::GenerateOneFrame);
}
void TearDown() override {
frame_generation_timer_.Stop();
host_.reset();
context_factories_.reset();
}
void GenerateOneFrame() { compositor()->ScheduleFullRedraw(); }
Compositor* compositor() { return host_->GetCompositor(); }
Layer* root_layer() { return &root_; }
private:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<TestContextFactories> context_factories_;
std::unique_ptr<TestCompositorHost> host_;
Layer root_;
// A timer to generate continuous compositor frames to trigger throughput
// data being transferred back.
base::RepeatingTimer frame_generation_timer_;
};
// Tests animation throughput collection with implicit animation scenario.
TEST_F(AnimationThroughputReporterTest, ImplicitAnimation) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
base::RunLoop run_loop;
{
LayerAnimator* animator = layer.GetAnimator();
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
run_loop.Quit();
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer.SetOpacity(1.0f);
}
run_loop.Run();
}
// Tests animation throughput collection with implicit animation setup before
// Layer is attached to a compositor.
TEST_F(AnimationThroughputReporterTest, ImplicitAnimationLateAttach) {
Layer layer;
layer.SetOpacity(0.5f);
base::RunLoop run_loop;
{
LayerAnimator* animator = layer.GetAnimator();
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
run_loop.Quit();
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer.SetOpacity(1.0f);
}
// Attach to root after animation setup.
root_layer()->Add(&layer);
run_loop.Run();
}
// Tests animation throughput collection with explicitly created animation
// sequence scenario.
TEST_F(AnimationThroughputReporterTest, ExplicitAnimation) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
base::RunLoop run_loop;
{
LayerAnimator* animator = layer.GetAnimator();
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
run_loop.Quit();
}));
animator->ScheduleAnimation(
new LayerAnimationSequence(LayerAnimationElement::CreateOpacityElement(
1.0f, base::TimeDelta::FromMilliseconds(50))));
}
run_loop.Run();
}
// Tests animation throughput collection for a persisted animator of a Layer.
TEST_F(AnimationThroughputReporterTest, PersistedAnimation) {
auto layer = std::make_unique<Layer>();
layer->SetOpacity(0.5f);
root_layer()->Add(layer.get());
// Set a persisted animator to |layer|.
LayerAnimator* animator =
new LayerAnimator(base::TimeDelta::FromMilliseconds(50));
layer->SetAnimator(animator);
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
// |reporter| keeps reporting as long as it is alive.
AnimationThroughputReporter reporter(
animator,
base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) { run_loop->Quit(); }));
// Report data for animation of opacity goes to 1.
layer->SetOpacity(1.0f);
run_loop->Run();
// Report data for animation of opacity goes to 0.5.
run_loop = std::make_unique<base::RunLoop>();
layer->SetOpacity(0.5f);
run_loop->Run();
}
// Tests animation throughput not reported when animation is aborted.
TEST_F(AnimationThroughputReporterTest, AbortedAnimation) {
auto layer = std::make_unique<Layer>();
layer->SetOpacity(0.5f);
root_layer()->Add(layer.get());
{
LayerAnimator* animator = layer->GetAnimator();
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
ADD_FAILURE() << "No report for aborted animations.";
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer->SetOpacity(1.0f);
}
// Delete |layer| to abort on-going animations.
layer.reset();
// Wait a bit to ensure that report does not happen.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
run_loop.Run();
}
// Tests animation throughput not reported when detached from timeline.
TEST_F(AnimationThroughputReporterTest, NoReportOnDetach) {
auto layer = std::make_unique<Layer>();
layer->SetOpacity(0.5f);
root_layer()->Add(layer.get());
{
LayerAnimator* animator = layer->GetAnimator();
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
ADD_FAILURE() << "No report for aborted animations.";
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer->SetOpacity(1.0f);
}
// Detach from the root and attach to a root.
root_layer()->Remove(layer.get());
root_layer()->Add(layer.get());
// Wait a bit to ensure that report does not happen.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
run_loop.Run();
}
// Tests animation throughput not reported and no leak when animation is stopped
// without being attached to a root.
TEST_F(AnimationThroughputReporterTest, EndDetachedNoReportNoLeak) {
auto layer = std::make_unique<Layer>();
layer->SetOpacity(0.5f);
LayerAnimator* animator = layer->GetAnimator();
// Schedule an animation without being attached to a root.
{
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
ADD_FAILURE() << "No report for aborted animations.";
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer->SetOpacity(1.0f);
}
// End the animation without being attached to a root.
animator->StopAnimating();
// Wait a bit to ensure that report does not happen.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
run_loop.Run();
// AnimationTracker in |reporter| should not leak in asan.
}
// Tests animation throughput are reported if there was a previous animation
// preempted under IMMEDIATELY_ANIMATE_TO_NEW_TARGET strategy.
TEST_F(AnimationThroughputReporterTest, ReportForAnimateToNewTarget) {
auto layer = std::make_unique<Layer>();
layer->SetOpacity(0.f);
layer->SetBounds(gfx::Rect(0, 0, 1, 2));
root_layer()->Add(layer.get());
LayerAnimator* animator = layer->GetAnimator();
// Schedule an animation that will be preempted. No report should happen.
{
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
ADD_FAILURE() << "No report for aborted animations.";
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer->SetOpacity(0.5f);
layer->SetBounds(gfx::Rect(0, 0, 3, 4));
}
// Animate to new target. Report should happen.
base::RunLoop run_loop;
{
AnimationThroughputReporter reporter(
animator, base::BindLambdaForTesting(
[&](cc::FrameSequenceMetrics::ThroughputData) {
run_loop.Quit();
}));
ScopedLayerAnimationSettings settings(animator);
settings.SetPreemptionStrategy(
LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
layer->SetOpacity(1.0f);
layer->SetBounds(gfx::Rect(0, 0, 5, 6));
}
run_loop.Run();
}
} // namespace ui