#include "cc/trees/image_animation_controller.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
class FakeAnimationDriver : public ImageAnimationController::AnimationDriver {
public:
FakeAnimationDriver() = default;
~FakeAnimationDriver() override = default;
void set_should_animate(bool should_animate) {
should_animate_ = should_animate;
}
bool ShouldAnimate(PaintImage::Id paint_image_id) const override {
return should_animate_;
}
private:
bool should_animate_ = true;
};
class DelayTrackingTaskRunner : public base::SingleThreadTaskRunner {
public:
explicit DelayTrackingTaskRunner(base::SingleThreadTaskRunner* task_runner)
: task_runner_(task_runner) {}
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
last_delay_.emplace(delay);
return task_runner_->PostTask(from_here, std::move(task));
}
bool RunsTasksInCurrentSequence() const override {
return task_runner_->RunsTasksInCurrentSequence();
}
bool PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
last_delay_.emplace(delay);
return task_runner_->PostTask(from_here, std::move(task));
}
void VerifyDelay(base::TimeDelta expected) {
DCHECK(last_delay_.has_value());
EXPECT_EQ(last_delay_.value(), expected);
last_delay_.reset();
}
bool has_delay() const { return last_delay_.has_value(); }
private:
~DelayTrackingTaskRunner() override = default;
std::optional<base::TimeDelta> last_delay_;
raw_ptr<base::SingleThreadTaskRunner> task_runner_;
};
class ImageAnimationControllerTest : public testing::Test,
public ImageAnimationController::Client {
public:
void SetUp() override {
task_runner_ = new DelayTrackingTaskRunner(
base::SingleThreadTaskRunner::GetCurrentDefault().get());
controller_ = std::make_unique<ImageAnimationController>(
task_runner_.get(), this, GetEnableImageAnimationResync());
controller_->set_now_callback_for_testing(base::BindRepeating(
&ImageAnimationControllerTest::Now, base::Unretained(this)));
now_ += base::Seconds(10);
}
void TearDown() override { controller_.reset(); }
void LoopOnceNoDelay(PaintImage::Id paint_image_id,
const std::vector<FrameMetadata>& frames,
size_t num_of_frames_to_loop,
int repetitions_completed,
std::vector<base::TimeDelta> expected_delays = {},
bool restarting = false) {
DCHECK_LE(num_of_frames_to_loop, frames.size());
if (expected_delays.empty()) {
expected_delays.resize(frames.size());
for (size_t i = 0; i < frames.size(); ++i)
expected_delays[i] = frames[i].duration;
}
invalidation_count_ = 0;
for (size_t i = 0; i < num_of_frames_to_loop; ++i) {
SCOPED_TRACE(i);
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(
controller_->GetLastNumOfFramesSkippedForTesting(paint_image_id), 0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id,
WhichTree::PENDING_TREE),
i);
if (i == 0u && !restarting) {
size_t active_index = 0u;
if (repetitions_completed != 0)
active_index = frames.size() - 1;
EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id,
WhichTree::ACTIVE_TREE),
active_index);
} else if (i != 0u) {
EXPECT_EQ(controller_->GetFrameIndexForImage(paint_image_id,
WhichTree::ACTIVE_TREE),
i - 1);
}
if (i == 0u && repetitions_completed == 0 && !restarting) {
EXPECT_EQ(animated_images.size(), 0u);
} else {
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(paint_image_id), 1u);
}
if (i != num_of_frames_to_loop - 1)
task_runner_->VerifyDelay(expected_delays.at(i));
controller_->DidActivate();
AdvanceNow(expected_delays.at(i));
}
}
protected:
void RequestBeginFrameForAnimatedImages() override { begin_frame_count_++; }
void RequestInvalidationForAnimatedImages() override {
invalidation_count_++;
}
base::TimeTicks Now() { return now_; }
void AdvanceNow(base::TimeDelta delta) { now_ += delta; }
viz::BeginFrameArgs BeginFrameArgs(base::TimeTicks now = base::TimeTicks()) {
if (now == base::TimeTicks())
now = now_;
return viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, 1 , 1 ,
now , now + interval_ ,
interval_ ,
viz::BeginFrameArgs::BeginFrameArgsType::NORMAL);
}
void RunFrameRequestAndInvalidation() {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 1);
EXPECT_EQ(invalidation_count_, 0);
begin_frame_count_ = 0;
controller_->WillBeginImplFrame(BeginFrameArgs());
EXPECT_EQ(begin_frame_count_, 0);
EXPECT_EQ(invalidation_count_, 1);
invalidation_count_ = 0;
}
virtual bool GetEnableImageAnimationResync() const { return true; }
base::TimeTicks now_;
int invalidation_count_ = 0;
int begin_frame_count_ = 0;
std::unique_ptr<ImageAnimationController> controller_;
scoped_refptr<DelayTrackingTaskRunner> task_runner_;
base::TimeDelta interval_ = base::Milliseconds(1);
};
TEST_F(ImageAnimationControllerTest, AnimationWithDelays) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(5)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
base::TimeDelta additional_delay = base::Milliseconds(1);
AdvanceNow(data.frames[0].duration + additional_delay);
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
EXPECT_EQ(
controller_->GetLastNumOfFramesSkippedForTesting(data.paint_image_id),
1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
3u);
task_runner_->VerifyDelay(frames[1].duration - additional_delay);
controller_->DidActivate();
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
1u);
AdvanceNow(data.frames[1].duration + data.frames[2].duration +
data.frames[3].duration);
RunFrameRequestAndInvalidation();
animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
EXPECT_EQ(
controller_->GetLastNumOfFramesSkippedForTesting(data.paint_image_id),
2u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
1u);
task_runner_->VerifyDelay(frames[0].duration - additional_delay);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, DriversControlAnimationTicking) {
std::vector<FrameMetadata> first_image_frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata first_data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone,
first_image_frames, kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(first_data);
FakeAnimationDriver first_driver;
controller_->RegisterAnimationDriver(first_data.paint_image_id,
&first_driver);
std::vector<FrameMetadata> second_image_frames = {
FrameMetadata(true, base::Milliseconds(5)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata second_data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone,
second_image_frames, kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(second_data);
FakeAnimationDriver second_driver;
controller_->RegisterAnimationDriver(second_data.paint_image_id,
&second_driver);
first_driver.set_should_animate(false);
second_driver.set_should_animate(false);
controller_->UpdateStateFromDrivers();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 0);
first_driver.set_should_animate(true);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 0u);
task_runner_->VerifyDelay(first_image_frames[0].duration);
second_driver.set_should_animate(true);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
first_driver.set_should_animate(false);
second_driver.set_should_animate(false);
controller_->UpdateStateFromDrivers();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 0);
controller_->UnregisterAnimationDriver(first_data.paint_image_id,
&first_driver);
controller_->UnregisterAnimationDriver(second_data.paint_image_id,
&second_driver);
}
TEST_F(ImageAnimationControllerTest, RepetitionsRequested) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
data.paint_image_id = PaintImage::GetNextId();
data.repetition_count = 5;
controller_->UpdateAnimatedImage(data);
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
for (int i = 0; i < data.repetition_count; ++i) {
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i);
if (i < data.repetition_count - 1)
task_runner_->VerifyDelay(frames.back().duration);
invalidation_count_ = 0;
}
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
data.paint_image_id = PaintImage::GetNextId();
data.repetition_count = kAnimationLoopInfinite;
controller_->UpdateAnimatedImage(data);
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
for (int i = 0; i < 7; ++i) {
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i);
if (i < data.repetition_count - 1)
task_runner_->VerifyDelay(frames.back().duration);
invalidation_count_ = 0;
}
begin_frame_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 1);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
data.paint_image_id = PaintImage::GetNextId();
data.repetition_count = kAnimationNone;
EXPECT_DCHECK_DEATH(controller_->UpdateAnimatedImage(data));
}
TEST_F(ImageAnimationControllerTest, DisplayCompleteFrameOnly) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(false, base::Milliseconds(4))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kPartiallyDone,
frames, kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, 2, 0);
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
data.completion_state = PaintImage::CompletionState::kDone;
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, DontLoopPartiallyLoadedImages) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kPartiallyDone,
frames, 2, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
frames.push_back(FrameMetadata(true, base::Milliseconds(4)));
data.completion_state = PaintImage::CompletionState::kDone;
data.frames = frames;
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
2u);
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames.back().duration);
AdvanceNow(frames.back().duration);
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
begin_frame_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, DontAdvanceUntilDesiredTime) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
EXPECT_EQ(animated_images.size(), 0u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames[0].duration);
base::TimeDelta time_remaining = base::Milliseconds(1);
AdvanceNow(frames[0].duration - time_remaining);
animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
EXPECT_EQ(animated_images.size(), 0u);
controller_->DidActivate();
EXPECT_FALSE(task_runner_->has_delay());
AdvanceNow(time_remaining);
animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
controller_->DidActivate();
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, RestartAfterSyncCutoff) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames[0].duration);
AdvanceNow(base::Minutes(10));
controller_->WillBeginImplFrame(BeginFrameArgs());
animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames[0].duration);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, DontSkipLoopsToCatchUpAfterLoad) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(5))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kPartiallyDone,
frames, kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0);
task_runner_->VerifyDelay(frames[2].duration);
AdvanceNow(frames[3].duration + frames[0].duration);
data.completion_state = PaintImage::CompletionState::kDone;
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
2u);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, FinishRepetitionsDuringCatchUp) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames, 3,
0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 1);
AdvanceNow(base::Minutes(1));
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
frames.size() - 1);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
frames.size() - 1);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, ResetAnimations) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames, 3,
0u);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0);
LoopOnceNoDelay(data.paint_image_id, frames, 2u, 1);
data.reset_animation_sequence_id++;
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
for (int i = 0; i < 3; ++i) {
bool restarting = i == 0;
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), i, {},
restarting);
}
invalidation_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(invalidation_count_, 0);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, ResetAnimationStateMapOnNavigation) {
std::vector<FrameMetadata> first_image_frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata first_data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone,
first_image_frames, kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(first_data);
FakeAnimationDriver first_driver;
controller_->RegisterAnimationDriver(first_data.paint_image_id,
&first_driver);
std::vector<FrameMetadata> second_image_frames = {
FrameMetadata(true, base::Milliseconds(5)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata second_data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone,
second_image_frames, kAnimationLoopOnce, 0);
controller_->UpdateAnimatedImage(second_data);
FakeAnimationDriver second_driver;
controller_->RegisterAnimationDriver(second_data.paint_image_id,
&second_driver);
controller_->AnimateForSyncTree(BeginFrameArgs());
controller_->UnregisterAnimationDriver(first_data.paint_image_id,
&first_driver);
EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 2u);
controller_->set_did_navigate();
controller_->DidActivate();
EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u);
controller_->UnregisterAnimationDriver(second_data.paint_image_id,
&second_driver);
EXPECT_EQ(controller_->animation_state_map_size_for_testing(), 1u);
}
TEST_F(ImageAnimationControllerTest, ImageWithNonVsyncAlignedDurations) {
interval_ = base::Milliseconds(1);
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2.5)),
FrameMetadata(true, base::Milliseconds(3.76)),
FrameMetadata(true, base::Milliseconds(4.27))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames, 3,
0u);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
std::vector<base::TimeDelta> expected_delays = {
base::Milliseconds(2), base::Milliseconds(4), base::Milliseconds(4)};
LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0,
expected_delays);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, ImageWithLessThanIntervalDurations) {
interval_ = base::Milliseconds(1);
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(0.5)),
FrameMetadata(true, base::Milliseconds(0.43)),
FrameMetadata(true, base::Milliseconds(0.76)),
FrameMetadata(true, base::Milliseconds(0.74)),
};
frames.push_back(FrameMetadata(true, interval_ - frames.back().duration));
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopOnce, 0u);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
auto invalidated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
2u);
controller_->DidActivate();
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, ImplFramesWhileInvalidationPending) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2.5)),
FrameMetadata(true, base::Milliseconds(3.76)),
FrameMetadata(true, base::Milliseconds(4.27))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames, 3,
0u);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
RunFrameRequestAndInvalidation();
controller_->WillBeginImplFrame(BeginFrameArgs());
EXPECT_FALSE(task_runner_->has_delay());
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerTest, MissedBeginFrameAfterRequest) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2.5)),
FrameMetadata(true, base::Milliseconds(3.76)),
FrameMetadata(true, base::Milliseconds(4.27))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames, 3,
0u);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
begin_frame_count_ = 0;
base::RunLoop().RunUntilIdle();
EXPECT_EQ(begin_frame_count_, 1);
controller_->WillBeginImplFrame(BeginFrameArgs(now_ - interval_));
EXPECT_EQ(begin_frame_count_, 2);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
class ImageAnimationControllerNoResyncTest
: public ImageAnimationControllerTest {
protected:
bool GetEnableImageAnimationResync() const override { return false; }
};
TEST_F(ImageAnimationControllerNoResyncTest, NoSyncCutoffAfterIdle) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
RunFrameRequestAndInvalidation();
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
0u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames[0].duration);
AdvanceNow(base::Minutes(10) + frames[0].duration);
controller_->WillBeginImplFrame(BeginFrameArgs());
animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
0u);
controller_->DidActivate();
task_runner_->VerifyDelay(frames[1].duration);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerNoResyncTest, SkipsLoopsAfterFirstIteration) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(5))};
DiscardableImageMap::AnimatedImageMetadata data(
PaintImage::GetNextId(), PaintImage::CompletionState::kPartiallyDone,
frames, kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
FakeAnimationDriver driver;
controller_->RegisterAnimationDriver(data.paint_image_id, &driver);
controller_->UpdateStateFromDrivers();
LoopOnceNoDelay(data.paint_image_id, frames, 3u, 0);
task_runner_->VerifyDelay(frames[2].duration);
AdvanceNow(frames[3].duration + frames[0].duration);
data.completion_state = PaintImage::CompletionState::kDone;
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
task_runner_->VerifyDelay(base::TimeDelta());
auto animated_images = controller_->AnimateForSyncTree(BeginFrameArgs());
EXPECT_EQ(animated_images.size(), 1u);
EXPECT_EQ(animated_images.count(data.paint_image_id), 1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::PENDING_TREE),
1u);
EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id,
WhichTree::ACTIVE_TREE),
2u);
controller_->UnregisterAnimationDriver(data.paint_image_id, &driver);
}
TEST_F(ImageAnimationControllerNoResyncTest,
ComputeConsistentContentFrameDuration) {
PaintImage::Id id1 = PaintImage::GetNextId();
FakeAnimationDriver driver;
{
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(2)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(5))};
DiscardableImageMap::AnimatedImageMetadata data(
id1, PaintImage::CompletionState::kPartiallyDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
controller_->RegisterAnimationDriver(id1, &driver);
controller_->UpdateStateFromDrivers();
EXPECT_EQ(controller_->GetConsistentContentFrameDuration(), std::nullopt);
}
{
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
id1, PaintImage::CompletionState::kPartiallyDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
std::optional<ImageAnimationController::ConsistentFrameDuration>
consistent_duration = controller_->GetConsistentContentFrameDuration();
ASSERT_TRUE(consistent_duration.has_value());
EXPECT_EQ(consistent_duration->frame_duration, base::Milliseconds(3));
EXPECT_EQ(consistent_duration->num_images, 1u);
}
PaintImage::Id id2 = PaintImage::GetNextId();
{
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(4)),
FrameMetadata(true, base::Milliseconds(4))};
DiscardableImageMap::AnimatedImageMetadata data(
id2, PaintImage::CompletionState::kPartiallyDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
controller_->RegisterAnimationDriver(id2, &driver);
controller_->UpdateStateFromDrivers();
EXPECT_EQ(controller_->GetConsistentContentFrameDuration(), std::nullopt);
}
{
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3)),
FrameMetadata(true, base::Milliseconds(3))};
DiscardableImageMap::AnimatedImageMetadata data(
id2, PaintImage::CompletionState::kPartiallyDone, frames,
kAnimationLoopInfinite, 0);
controller_->UpdateAnimatedImage(data);
controller_->UpdateStateFromDrivers();
std::optional<ImageAnimationController::ConsistentFrameDuration>
consistent_duration = controller_->GetConsistentContentFrameDuration();
ASSERT_TRUE(consistent_duration.has_value());
EXPECT_EQ(consistent_duration->frame_duration, base::Milliseconds(3));
EXPECT_EQ(consistent_duration->num_images, 2u);
}
controller_->UnregisterAnimationDriver(id1, &driver);
controller_->UnregisterAnimationDriver(id2, &driver);
}
}