blob: e5ace413e1c0b0980f71d64cfecc313459e8bb23 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_tick_clock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h"
#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_type.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include <memory>
using ::testing::_;
using ::testing::AtLeast;
using ::testing::Mock;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrictMock;
using ::testing::Truly;
// This test uses actual threads since mutator logic requires it. This means we
// have dependency on Blink platform to create threads.
namespace blink {
namespace {
std::unique_ptr<Thread> CreateThread(const char* name) {
return Platform::Current()->CreateThread(
ThreadCreationParams(ThreadType::kTestThread).SetThreadNameForTest(name));
}
class MockAnimationWorkletMutator
: public GarbageCollected<MockAnimationWorkletMutator>,
public AnimationWorkletMutator {
public:
MockAnimationWorkletMutator(
scoped_refptr<base::SingleThreadTaskRunner> expected_runner)
: expected_runner_(expected_runner) {}
~MockAnimationWorkletMutator() override {}
std::unique_ptr<AnimationWorkletOutput> Mutate(
std::unique_ptr<AnimationWorkletInput> input) override {
return std::unique_ptr<AnimationWorkletOutput>(MutateRef(*input));
}
// Blocks the worklet thread by posting a task that will complete only when
// signaled. This blocking ensures that tests of async mutations do not
// encounter race conditions when validating queuing strategies.
void BlockWorkletThread() {
PostCrossThreadTask(
*expected_runner_, FROM_HERE,
CrossThreadBindOnce(
[](base::WaitableEvent* start_processing_event) {
start_processing_event->Wait();
},
WTF::CrossThreadUnretained(&start_processing_event_)));
}
void UnblockWorkletThread() { start_processing_event_.Signal(); }
MOCK_CONST_METHOD0(GetWorkletId, int());
MOCK_METHOD1(MutateRef,
AnimationWorkletOutput*(const AnimationWorkletInput&));
scoped_refptr<base::SingleThreadTaskRunner> expected_runner_;
base::WaitableEvent start_processing_event_;
};
class MockCompositorMutatorClient : public CompositorMutatorClient {
public:
MockCompositorMutatorClient(
std::unique_ptr<AnimationWorkletMutatorDispatcherImpl> mutator)
: CompositorMutatorClient(std::move(mutator)) {}
~MockCompositorMutatorClient() override {}
// gmock cannot mock methods with move-only args so we forward it to ourself.
void SetMutationUpdate(
std::unique_ptr<cc::MutatorOutputState> output_state) override {
SetMutationUpdateRef(output_state.get());
}
MOCK_METHOD1(SetMutationUpdateRef,
void(cc::MutatorOutputState* output_state));
};
class AnimationWorkletMutatorDispatcherImplTest : public ::testing::Test {
public:
void SetUp() override {
auto mutator = std::make_unique<AnimationWorkletMutatorDispatcherImpl>(
base::ThreadTaskRunnerHandle::Get());
mutator_ = mutator.get();
client_ =
std::make_unique<::testing::StrictMock<MockCompositorMutatorClient>>(
std::move(mutator));
}
void TearDown() override { mutator_ = nullptr; }
std::unique_ptr<::testing::StrictMock<MockCompositorMutatorClient>> client_;
AnimationWorkletMutatorDispatcherImpl* mutator_;
};
std::unique_ptr<AnimationWorkletDispatcherInput> CreateTestMutatorInput() {
AnimationWorkletInput::AddAndUpdateState state1{
{11, 1}, "test1", 5000, nullptr, nullptr};
AnimationWorkletInput::AddAndUpdateState state2{
{22, 2}, "test2", 5000, nullptr, nullptr};
auto input = std::make_unique<AnimationWorkletDispatcherInput>();
input->Add(std::move(state1));
input->Add(std::move(state2));
return input;
}
bool OnlyIncludesAnimation1(const AnimationWorkletInput& in) {
return in.added_and_updated_animations.size() == 1 &&
in.added_and_updated_animations[0].worklet_animation_id.animation_id ==
1;
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
RegisteredAnimatorShouldOnlyReceiveInputForItself) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(Truly(OnlyIncludesAnimation1)))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->MutateSynchronously(CreateTestMutatorInput());
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
RegisteredAnimatorShouldNotBeMutatedWhenNoInput) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
AnimationWorkletInput::AddAndUpdateState state{
{22, 2}, "test2", 5000, nullptr, nullptr};
auto input = std::make_unique<AnimationWorkletDispatcherInput>();
input->Add(std::move(state));
mutator_->MutateSynchronously(std::move(input));
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
MutationUpdateIsNotInvokedWithNoRegisteredAnimators) {
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
std::unique_ptr<AnimationWorkletDispatcherInput> input =
std::make_unique<AnimationWorkletDispatcherInput>();
mutator_->MutateSynchronously(std::move(input));
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
MutationUpdateIsNotInvokedWithNullOutput) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
mutator_->MutateSynchronously(CreateTestMutatorInput());
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
MutationUpdateIsInvokedCorrectlyWithSingleRegisteredAnimator) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->MutateSynchronously(CreateTestMutatorInput());
// The above call blocks on mutator threads running their tasks so we can
// safely verify here.
Mock::VerifyAndClearExpectations(client_.get());
// Ensure mutator is not invoked after unregistration.
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
mutator_->UnregisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator));
mutator_->MutateSynchronously(CreateTestMutatorInput());
Mock::VerifyAndClearExpectations(client_.get());
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnSameThread) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(second_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
mutator_->MutateSynchronously(CreateTestMutatorInput());
}
TEST_F(
AnimationWorkletMutatorDispatcherImplTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnDifferentThreads) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
std::unique_ptr<Thread> second_thread = CreateThread("SecondAnimationThread");
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
second_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(second_mutator),
second_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
mutator_->MutateSynchronously(CreateTestMutatorInput());
// The above call blocks on mutator threads running their tasks so we can
// safely verify here.
Mock::VerifyAndClearExpectations(client_.get());
// Ensure first_mutator is not invoked after unregistration.
mutator_->UnregisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator));
EXPECT_CALL(*first_mutator, GetWorkletId()).Times(0);
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
mutator_->MutateSynchronously(CreateTestMutatorInput());
Mock::VerifyAndClearExpectations(client_.get());
}
TEST_F(AnimationWorkletMutatorDispatcherImplTest,
DispatcherShouldNotHangWhenMutatorGoesAway) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId()).WillRepeatedly(Return(11));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
// Shutdown the thread so its task runner no longer executes tasks.
first_thread.reset();
mutator_->MutateSynchronously(CreateTestMutatorInput());
Mock::VerifyAndClearExpectations(client_.get());
}
// -----------------------------------------------------------------------
// Asynchronous version of tests.
using MutatorDispatcherRef =
scoped_refptr<AnimationWorkletMutatorDispatcherImpl>;
class AnimationWorkletMutatorDispatcherImplAsyncTest
: public AnimationWorkletMutatorDispatcherImplTest {
public:
AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
CreateIntermediateResultCallback(MutateStatus expected_result) {
return CrossThreadBindOnce(
&AnimationWorkletMutatorDispatcherImplAsyncTest ::
VerifyExpectedMutationResult,
CrossThreadUnretained(this), expected_result);
}
AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
CreateNotReachedCallback() {
return CrossThreadBindOnce([](MutateStatus unused) {
NOTREACHED() << "Mutate complete callback should not have been triggered";
});
}
AnimationWorkletMutatorDispatcher::AsyncMutationCompleteCallback
CreateTestCompleteCallback(
MutateStatus expected_result = MutateStatus::kCompletedWithUpdate) {
return CrossThreadBindOnce(
&AnimationWorkletMutatorDispatcherImplAsyncTest ::
VerifyCompletedMutationResultAndFinish,
CrossThreadUnretained(this), expected_result);
}
// Executes run loop until quit closure is called.
void WaitForTestCompletion() { run_loop_.Run(); }
void VerifyExpectedMutationResult(MutateStatus expectation,
MutateStatus result) {
EXPECT_EQ(expectation, result);
IntermediateResultCallbackRef();
}
void VerifyCompletedMutationResultAndFinish(MutateStatus expectation,
MutateStatus result) {
EXPECT_EQ(expectation, result);
run_loop_.Quit();
}
// Verifying that intermediate result callbacks are invoked the correct number
// of times.
MOCK_METHOD0(IntermediateResultCallbackRef, void());
static const MutateQueuingStrategy kNormalPriority =
MutateQueuingStrategy::kQueueAndReplaceNormalPriority;
static const MutateQueuingStrategy kHighPriority =
MutateQueuingStrategy::kQueueHighPriority;
private:
base::RunLoop run_loop_;
};
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
RegisteredAnimatorShouldOnlyReceiveInputForItself) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
RegisteredAnimatorShouldNotBeMutatedWhenNoInput) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
AnimationWorkletInput::AddAndUpdateState state{
{22, 2}, "test2", 5000, nullptr, nullptr};
auto input = std::make_unique<AnimationWorkletDispatcherInput>();
input->Add(std::move(state));
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_FALSE(mutator_->MutateAsynchronously(std::move(input), kNormalPriority,
CreateNotReachedCallback()));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsNotInvokedWithNoRegisteredAnimators) {
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
std::unique_ptr<AnimationWorkletDispatcherInput> input =
std::make_unique<AnimationWorkletDispatcherInput>();
EXPECT_FALSE(mutator_->MutateAsynchronously(std::move(input), kNormalPriority,
CreateNotReachedCallback()));
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsNotInvokedWithNullOutput) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(0);
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateTestCompleteCallback(MutateStatus::kCompletedNoUpdate)));
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateIsInvokedCorrectlyWithSingleRegisteredAnimator) {
// Create a thread to run mutator tasks.
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
WaitForTestCompletion();
// Above call blocks until complete signal is received.
Mock::VerifyAndClearExpectations(client_.get());
// Ensure mutator is not invoked after unregistration.
mutator_->UnregisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator));
EXPECT_FALSE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateNotReachedCallback()));
Mock::VerifyAndClearExpectations(client_.get());
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnSameThread) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(second_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
WaitForTestCompletion();
}
TEST_F(
AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateInvokedCorrectlyWithTwoRegisteredAnimatorsOnDifferentThreads) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstAnimationThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
std::unique_ptr<Thread> second_thread = CreateThread("SecondAnimationThread");
MockAnimationWorkletMutator* second_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
second_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(second_mutator),
second_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*second_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(22));
EXPECT_CALL(*second_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateDroppedWhenBusy) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(1))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(1)
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(1);
// Block Responses until all requests have been queued.
first_mutator->BlockWorkletThread();
// Response for first mutator call is blocked until after the second
// call is sent.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
// Second request dropped since busy processing first.
EXPECT_FALSE(mutator_->MutateAsynchronously(CreateTestMutatorInput(),
MutateQueuingStrategy::kDrop,
CreateNotReachedCallback()));
// Unblock first request.
first_mutator->UnblockWorkletThread();
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateQueuedWhenBusy) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(2))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(2)
.WillOnce(Return(new AnimationWorkletOutput()))
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
EXPECT_CALL(*this, IntermediateResultCallbackRef()).Times(1);
// Block Responses until all requests have been queued.
first_mutator->BlockWorkletThread();
// Response for first mutator call is blocked until after the second
// call is sent.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateIntermediateResultCallback(MutateStatus::kCompletedWithUpdate)));
// First request still processing, queue request.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
// Unblock first request.
first_mutator->UnblockWorkletThread();
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateQueueWithReplacementWhenBusy) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(2))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(2)
.WillOnce(Return(new AnimationWorkletOutput()))
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
EXPECT_CALL(*this, IntermediateResultCallbackRef()).Times(2);
// Block Responses until all requests have been queued.
first_mutator->BlockWorkletThread();
// Response for first mutator call is blocked until after the second
// call is sent.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateIntermediateResultCallback(MutateStatus::kCompletedWithUpdate)));
// First request still processing, queue a second request, which will get
// canceled by a third request.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateIntermediateResultCallback(MutateStatus::kCanceled)));
// First request still processing, clobber second request in queue.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
// Unblock first request.
first_mutator->UnblockWorkletThread();
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest,
MutationUpdateMultipleQueuesWhenBusy) {
std::unique_ptr<Thread> first_thread = CreateThread("FirstThread");
MockAnimationWorkletMutator* first_mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
first_thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(
WrapCrossThreadPersistent(first_mutator), first_thread->GetTaskRunner());
EXPECT_CALL(*first_mutator, GetWorkletId())
.Times(AtLeast(3))
.WillRepeatedly(Return(11));
EXPECT_CALL(*first_mutator, MutateRef(_))
.Times(3)
.WillOnce(Return(new AnimationWorkletOutput()))
.WillOnce(Return(new AnimationWorkletOutput()))
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(3);
EXPECT_CALL(*this, IntermediateResultCallbackRef()).Times(2);
// Block Responses until all requests have been queued.
first_mutator->BlockWorkletThread();
// Response for first mutator call is blocked until after the second
// call is sent.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateIntermediateResultCallback(MutateStatus::kCompletedWithUpdate)));
// First request still processing, queue a second request.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
// First request still processing. This request uses a separate queue from the
// second request. It should not replace the second request but should be
// dispatched ahead of the second request.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kHighPriority,
CreateIntermediateResultCallback(MutateStatus::kCompletedWithUpdate)));
// Unblock first request.
first_mutator->UnblockWorkletThread();
WaitForTestCompletion();
}
TEST_F(AnimationWorkletMutatorDispatcherImplAsyncTest, HistogramTester) {
const char* histogram_name =
"Animation.AnimationWorklet.Dispatcher.AsynchronousMutateDuration";
base::HistogramTester histogram_tester;
std::unique_ptr<base::TickClock> mock_clock =
std::make_unique<base::SimpleTestTickClock>();
base::SimpleTestTickClock* mock_clock_ptr =
static_cast<base::SimpleTestTickClock*>(mock_clock.get());
mutator_->SetClockForTesting(std::move(mock_clock));
std::unique_ptr<Thread> thread = CreateThread("MyThread");
MockAnimationWorkletMutator* mutator =
MakeGarbageCollected<MockAnimationWorkletMutator>(
thread->GetTaskRunner());
mutator_->RegisterAnimationWorkletMutator(WrapCrossThreadPersistent(mutator),
thread->GetTaskRunner());
EXPECT_CALL(*mutator, GetWorkletId())
.Times(AtLeast(2))
.WillRepeatedly(Return(11));
EXPECT_CALL(*mutator, MutateRef(_))
.Times(2)
.WillOnce(Return(new AnimationWorkletOutput()))
.WillOnce(Return(new AnimationWorkletOutput()));
EXPECT_CALL(*client_, SetMutationUpdateRef(_)).Times(2);
// Block Responses until all requests have been queued.
mutator->BlockWorkletThread();
base::TimeDelta time_delta = base::TimeDelta::FromMilliseconds(10);
// Expected Elapsed time is the sum of all clock advancements until unblocked,
// which totals to 30 ms.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kHighPriority,
CreateIntermediateResultCallback(MutateStatus::kCompletedWithUpdate)));
mock_clock_ptr->Advance(time_delta);
// This request will get stomped by the next request, but the start time is
// preserved.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority,
CreateIntermediateResultCallback(MutateStatus::kCanceled)));
mock_clock_ptr->Advance(time_delta);
// Replaces previous request. Since 10 ms has elapsed prior to replacing the
// previous request, the expected elapsed time is 20 ms.
EXPECT_TRUE(mutator_->MutateAsynchronously(
CreateTestMutatorInput(), kNormalPriority, CreateTestCompleteCallback()));
mock_clock_ptr->Advance(time_delta);
mutator->UnblockWorkletThread();
WaitForTestCompletion();
histogram_tester.ExpectTotalCount(histogram_name, 2);
// Times are in microseconds.
histogram_tester.ExpectBucketCount(histogram_name, 20000, 1);
histogram_tester.ExpectBucketCount(histogram_name, 30000, 1);
}
} // namespace
} // namespace blink