blob: f24ec3287c351528ba97066726911193a3f166e8 [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/total_animation_throughput_reporter.h"
#include <memory>
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/metrics/frame_sequence_metrics.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/animation_throughput_reporter_test_base.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
namespace {
class TestReporter : public TotalAnimationThroughputReporter {
public:
explicit TestReporter(AnimationThroughputReporterTestBase* test_base)
: ui::TotalAnimationThroughputReporter(
test_base->compositor(),
base::BindRepeating(&TestReporter::Reported,
base::Unretained(this))),
test_base_(test_base) {}
TestReporter(AnimationThroughputReporterTestBase* test_base,
bool should_delete)
: ui::TotalAnimationThroughputReporter(
test_base->compositor(),
base::BindOnce(&TestReporter::Reported, base::Unretained(this)),
should_delete),
test_base_(test_base) {}
TestReporter(const TestReporter&) = delete;
TestReporter& operator=(const TestReporter&) = delete;
~TestReporter() override = default;
void AdvanceUntilReported(const base::TimeDelta& delta) {
DCHECK(!reported_);
test_base_->Advance(delta);
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Non ash-chrome platform uses native event loop which doesn't work well
// with mock time, so we still need to run the event loop.
if (!reported_) {
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
}
#endif
}
bool reported() const { return reported_; }
void reset() { reported_ = false; }
private:
void Reported(const cc::FrameSequenceMetrics::CustomReportData&) {
reported_ = true;
#if !BUILDFLAG(IS_CHROMEOS_ASH)
if (run_loop_)
run_loop_->Quit();
#endif
}
AnimationThroughputReporterTestBase* test_base_;
bool reported_ = false;
#if !BUILDFLAG(IS_CHROMEOS_ASH)
std::unique_ptr<base::RunLoop> run_loop_;
#endif
};
} // namespace
using TotalAnimationThroughputReporterTest =
AnimationThroughputReporterTestBase;
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_SingleAnimation DISABLED_SingleAnimation
#else
#define MAYBE_SingleAnimation SingleAnimation
#endif
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_SingleAnimation) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
TestReporter reporter(this);
{
LayerAnimator* animator = layer.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(32));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_StopAnimation DISABLED_StopAnimation
#else
#define MAYBE_StopAnimation StopAnimation
#endif
// Tests the stopping last animation will trigger the animation.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_StopAnimation) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
TestReporter reporter(this);
{
LayerAnimator* animator = layer.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(64));
layer.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
layer.GetAnimator()->StopAnimating();
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(32));
EXPECT_TRUE(reporter.reported());
}
// Tests the longest animation will trigger the report.
TEST_F(TotalAnimationThroughputReporterTest, MultipleAnimations) {
Layer layer1;
layer1.SetOpacity(0.5f);
root_layer()->Add(&layer1);
TestReporter reporter(this);
{
LayerAnimator* animator = layer1.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer1.SetOpacity(1.0f);
}
Layer layer2;
layer2.SetOpacity(0.5f);
root_layer()->Add(&layer2);
{
LayerAnimator* animator = layer2.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(96));
layer2.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(200));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_MultipleAnimationsOnSingleLayer \
DISABLED_MultipleAnimationsOnSingleLayer
#else
#define MAYBE_MultipleAnimationsOnSingleLayer MultipleAnimationsOnSingleLayer
#endif
// Tests the longest animation on a single layer will triger the report.
TEST_F(TotalAnimationThroughputReporterTest,
MAYBE_MultipleAnimationsOnSingleLayer) {
Layer layer;
layer.SetOpacity(0.5f);
layer.SetLayerBrightness(0.5f);
root_layer()->Add(&layer);
TestReporter reporter(this);
{
LayerAnimator* animator = layer.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer.SetOpacity(1.0f);
}
{
LayerAnimator* animator = layer.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(96));
layer.SetLayerBrightness(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(64));
EXPECT_FALSE(reporter.reported());
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(48));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_AddAnimationWhileAnimating DISABLED_AddAnimationWhileAnimating
#else
#define MAYBE_AddAnimationWhileAnimating AddAnimationWhileAnimating
#endif
// Tests adding new animation will extends the duration.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_AddAnimationWhileAnimating) {
Layer layer1;
layer1.SetOpacity(0.5f);
root_layer()->Add(&layer1);
TestReporter reporter(this);
{
LayerAnimator* animator = layer1.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer1.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
// Add new animation while animating.
Layer layer2;
layer2.SetOpacity(0.5f);
root_layer()->Add(&layer2);
{
LayerAnimator* animator = layer2.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer2.SetOpacity(1.0f);
}
// The animation time is extended.
Advance(base::TimeDelta::FromMilliseconds(32));
EXPECT_FALSE(reporter.reported());
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(32));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_RemoveWhileAnimating DISABLED_RemoveWhileAnimating
#else
#define MAYBE_RemoveWhileAnimating RemoveWhileAnimating
#endif
// Tests removing last animation will call report callback.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_RemoveWhileAnimating) {
auto layer1 = std::make_unique<Layer>();
layer1->SetOpacity(0.5f);
root_layer()->Add(layer1.get());
TestReporter reporter(this);
{
LayerAnimator* animator = layer1->GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(100));
layer1->SetOpacity(1.0f);
}
Layer layer2;
layer2.SetOpacity(0.5f);
root_layer()->Add(&layer2);
{
LayerAnimator* animator = layer2.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(48));
layer2.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(48));
EXPECT_FALSE(reporter.reported());
layer1.reset();
// Aborting will be processed in next frame.
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(16));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_StartWhileAnimating DISABLED_StartWhileAnimating
#else
#define MAYBE_StartWhileAnimating StartWhileAnimating
#endif
// Make sure the reporter can start measuring even if the animation
// has started.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_StartWhileAnimating) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
{
LayerAnimator* animator = layer.GetAnimator();
ScopedLayerAnimationSettings settings(animator);
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(96));
layer.SetOpacity(1.0f);
}
Advance(base::TimeDelta::FromMilliseconds(32));
TestReporter reporter(this);
EXPECT_TRUE(reporter.IsMeasuringForTesting());
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_PersistedAnimation DISABLED_PersistedAnimation
#else
#define MAYBE_PersistedAnimation PersistedAnimation
#endif
// Tests the reporter is called multiple times for persistent animation.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_PersistedAnimation) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
// Set a persisted animator to |layer|.
LayerAnimator* animator =
new LayerAnimator(base::TimeDelta::FromMilliseconds(48));
layer.SetAnimator(animator);
// |reporter| keeps reporting as long as it is alive.
TestReporter reporter(this);
// Report data for animation of opacity goes to 1.
layer.SetOpacity(1.0f);
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(reporter.reported());
// Report data for animation of opacity goes to 0.5.
reporter.reset();
layer.SetOpacity(0.5f);
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_OnceReporter DISABLED_OnceReporter
#else
#define MAYBE_OnceReporter OnceReporter
#endif
// Make sure the once reporter is called only once.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_OnceReporter) {
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
// Set a persisted animator to |layer|.
LayerAnimator* animator =
new LayerAnimator(base::TimeDelta::FromMilliseconds(32));
layer.SetAnimator(animator);
TestReporter reporter(this, /*should_delete=*/false);
// Report data for animation of opacity goes to 1.
layer.SetOpacity(1.0f);
reporter.AdvanceUntilReported(base::TimeDelta::FromMilliseconds(100));
EXPECT_TRUE(reporter.reported());
// Report data for animation of opacity goes to 0.5.
reporter.reset();
layer.SetOpacity(1.0f);
Advance(base::TimeDelta::FromMilliseconds(100));
EXPECT_FALSE(reporter.reported());
}
// Flaky on ChromeOS: crbug.com/1157649
#if defined(OS_CHROMEOS)
#define MAYBE_OnceReporterShouldDelete DISABLED_OnceReporterShouldDelete
#else
#define MAYBE_OnceReporterShouldDelete OnceReporterShouldDelete
#endif
// One reporter marked as "should_delete" should be deleted when
// reported.
TEST_F(TotalAnimationThroughputReporterTest, MAYBE_OnceReporterShouldDelete) {
class DeleteTestReporter : public TotalAnimationThroughputReporter {
public:
DeleteTestReporter(Compositor* compositor,
ReportOnceCallback callback,
bool* deleted)
: TotalAnimationThroughputReporter(compositor,
std::move(callback),
true),
deleted_(deleted) {}
~DeleteTestReporter() override { *deleted_ = true; }
private:
bool* deleted_;
};
Layer layer;
layer.SetOpacity(0.5f);
root_layer()->Add(&layer);
// Set a persisted animator to |layer|.
LayerAnimator* animator =
new LayerAnimator(base::TimeDelta::FromMilliseconds(32));
layer.SetAnimator(animator);
// |reporter| keeps reporting as long as it is alive.
base::RunLoop run_loop;
bool deleted = false;
new DeleteTestReporter(
compositor(),
base::BindLambdaForTesting(
[&](const cc::FrameSequenceMetrics::CustomReportData&) {
run_loop.Quit();
}),
&deleted);
// Report data for animation of opacity goes to 1.
layer.SetOpacity(1.0f);
#if BUILDFLAG(IS_CHROMEOS_ASH)
Advance(base::TimeDelta::FromMilliseconds(48));
EXPECT_FALSE(run_loop.running());
#else
// Non ash-chrome platform uses native event loop which doesn't work
// with mock time, so we need to run more the event loop.
run_loop.Run();
#endif
EXPECT_TRUE(deleted);
}
} // namespace ui