blob: c89eabf0470d2b82e32f0499577ac89abce7f1af [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/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h"
#include <memory>
#include "base/strings/strcat.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/scheduler/test/fuzzer/proto/sequence_manager_test_description.pb.h"
#include "third_party/protobuf/src/google/protobuf/text_format.h"
#include "third_party/protobuf/src/google/protobuf/util/message_differencer.h"
namespace base {
namespace sequence_manager {
using testing::ContainerEq;
using testing::UnorderedElementsAreArray;
class SequenceManagerFuzzerProcessorForTest
: public SequenceManagerFuzzerProcessor {
public:
SequenceManagerFuzzerProcessorForTest()
: SequenceManagerFuzzerProcessor(true) {}
static void ParseAndRun(std::string test_description,
Vector<Vector<TaskForTest>>* executed_tasks,
Vector<Vector<ActionForTest>>* executed_actions) {
SequenceManagerTestDescription proto_description;
google::protobuf::TextFormat::ParseFromString(test_description,
&proto_description);
SequenceManagerFuzzerProcessorForTest processor;
processor.RunTest(proto_description);
if (executed_tasks)
*executed_tasks = processor.ordered_tasks();
if (executed_actions)
*executed_actions = processor.ordered_actions();
}
static void ParseAndRunSingleThread(std::string test_description,
Vector<TaskForTest>* executed_tasks,
Vector<ActionForTest>* executed_actions) {
SequenceManagerTestDescription proto_description;
google::protobuf::TextFormat::ParseFromString(
base::StrCat(
{"main_thread_actions { create_thread {", test_description, "}}"}),
&proto_description);
SequenceManagerFuzzerProcessorForTest processor;
processor.RunTest(proto_description);
if (executed_tasks)
*executed_tasks = processor.ordered_tasks()[1];
if (executed_actions)
*executed_actions = processor.ordered_actions()[1];
}
using SequenceManagerFuzzerProcessor::ordered_actions;
using SequenceManagerFuzzerProcessor::ordered_tasks;
using SequenceManagerFuzzerProcessor::ActionForTest;
using SequenceManagerFuzzerProcessor::TaskForTest;
};
using ActionForTest = SequenceManagerFuzzerProcessorForTest::ActionForTest;
using TaskForTest = SequenceManagerFuzzerProcessorForTest::TaskForTest;
TEST(SequenceManagerFuzzerProcessorTest, CreateTaskQueue) {
Vector<ActionForTest> executed_actions;
// Describes a test that creates a task queue and posts a task to create a
// task queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
task_id : 1
actions {
action_id : 3
create_task_queue {
}
}
}
}
})",
nullptr, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kCreateTaskQueue,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, CreateQueueVoter) {
Vector<ActionForTest> executed_actions;
// Describes a test that creates a voter and posts a task to create a queue
// voter.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_queue_voter {
task_queue_id : 1
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
task_id : 1
actions {
action_id : 3
create_queue_voter {
}
}
}
}
})",
nullptr, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateQueueVoter,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kCreateQueueVoter,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, PostDelayedTaskWDuration) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
// Posts an 10 ms delayed task of duration 20 ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task_queue_id : 1
delay_ms : 10
task {
task_id : 1
duration_ms : 20
}
}
})",
&executed_tasks, &executed_actions);
Vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 10, 30);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, SetQueuePriority) {
Vector<ActionForTest> executed_actions;
// Describes a test that sets the priority of queue and posts a task to set
// the priority of a queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
set_queue_priority {
task_queue_id: 2
priority: CONTROL
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
task_id : 1
actions {
action_id : 3
set_queue_priority {
task_queue_id : 1
priority : LOW
}
}
}
}
})",
nullptr, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kSetQueuePriority,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kSetQueuePriority,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabled) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
// Describes a test that posts a number of tasks to a certain queue, disable
// that queue, and post some more tasks to the same queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task_queue_id: 1
task {
task_id : 1
}
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
task_queue_id: 1
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 3
set_queue_enabled {
task_queue_id: 1
enabled: false
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
task_queue_id: 1
task {
task_id : 3
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kSetQueueEnabled,
0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
// All the tasks posted to the task queue with id 1 do not get executed since
// this task queue is disabled.
EXPECT_TRUE(executed_tasks.IsEmpty());
}
TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWDelays) {
Vector<TaskForTest> executed_tasks;
// Describes a test that posts two tasks to disable and enable a queue after
// 10ms and 20ms, respectively; and other no-op tasks in the different
// intervals to verify that the queue is indeed being disabled/enabled
// properly.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id: 1
task {
task_id : 1
}
}
}
initial_thread_actions {
action_id : 3
post_delayed_task {
delay_ms : 15
task_queue_id: 1
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 10
task_queue_id: 1
task {
task_id : 3
actions {
action_id : 5
set_queue_enabled {
task_queue_id: 1
enabled: false
}
}
}
}
}
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 20
task_queue_id: 0
task {
task_id : 4
actions {
action_id : 7
set_queue_enabled {
task_queue_id: 1
enabled: true
}
}
}
}
}
initial_thread_actions {
action_id : 8
post_delayed_task {
task_queue_id: 1
delay_ms : 20
task {
task_id : 5
}
}
})",
&executed_tasks, nullptr);
Vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 0, 0);
// Task that disables the queue.
expected_tasks.emplace_back(3, 10, 10);
// Task that enable the queue.
expected_tasks.emplace_back(4, 20, 20);
// Task couldn't execute at scheduled time i.e. 15ms since its queue was
// disabled at that time.
expected_tasks.emplace_back(2, 20, 20);
expected_tasks.emplace_back(5, 20, 20);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, MultipleVoters) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
// Describes a test that creates two voters for a queue, where one voter
// enables the queue, and the other disables it.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_queue_voter {
task_queue_id : 1
}
}
initial_thread_actions {
action_id : 2
create_queue_voter {
task_queue_id : 1
}
}
initial_thread_actions {
action_id : 3
set_queue_enabled {
voter_id : 1
task_queue_id : 1
enabled : true
}
}
initial_thread_actions {
action_id : 4
set_queue_enabled {
voter_id : 2
task_queue_id : 1
enabled : false
}
}
initial_thread_actions {
action_id : 5
post_delayed_task {
task_queue_id: 1
task {
task_id : 1
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateQueueVoter,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kCreateQueueVoter,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kSetQueueEnabled,
0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kSetQueueEnabled,
0);
expected_actions.emplace_back(5, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
// Queue is enabled only if all voters enable it.
EXPECT_TRUE(executed_tasks.IsEmpty());
}
TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id: 1
task {
task_id : 1
}
}
}
initial_thread_actions {
action_id :3
post_delayed_task {
delay_ms : 10
task_queue_id: 1
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
task_queue_id: 0
delay_ms : 10
task {
task_id : 3
}
}
}
initial_thread_actions {
action_id : 5
shutdown_task_queue {
task_queue_id: 1
}
}
initial_thread_actions {
action_id : 6
post_delayed_task {
task_queue_id: 1
task {
task_id : 4
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(
5, ActionForTest::ActionType::kShutdownTaskQueue, 0);
expected_actions.emplace_back(6, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
// Note that the task with id 4 isn't posted to the queue that was shutdown,
// since that was posted to the first available queue (Check
// sequence_manager_test_description.proto for more details).
expected_tasks.emplace_back(4, 0, 0);
expected_tasks.emplace_back(3, 10, 10);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest,
ShutdownTaskQueueWhenOneQueueAvailable) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
task_id : 1
}
}
}
initial_thread_actions {
action_id : 2
shutdown_task_queue {
task_queue_id: 1
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(
2, ActionForTest::ActionType::kShutdownTaskQueue, 0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
// We always want to have a default task queue in every thread. So, if
// we have only one queue, the shutdown action is effectively a no-op.
expected_tasks.emplace_back(1, 0, 0);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, ShutdownPostingTaskQueue) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id : 0
task{
task_id : 1
actions {
action_id : 3
shutdown_task_queue {
task_queue_id : 0
}
}
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(
3, ActionForTest::ActionType::kShutdownTaskQueue, 0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 0, 0);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, CancelParentTask) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
task_id : 0
actions {
action_id : 2
post_delayed_task {
task {
task_id : 1
}
}
}
actions {
action_id : 3
cancel_task {
task_id : 0
}
}
actions {
action_id : 4
post_delayed_task {
task {
task_id : 2
}
}
}
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kCancelTask, 0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(0, 0, 0);
expected_tasks.emplace_back(1, 0, 0);
expected_tasks.emplace_back(2, 0, 0);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, CancelTask) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
task_id : 1
}
}
}
initial_thread_actions {
action_id : 2
cancel_task {
task_id : 1
}
}
)",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kCancelTask, 0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
EXPECT_TRUE(executed_tasks.IsEmpty());
}
TEST(SequenceManagerFuzzerProcessorTest, CancelTaskWhenNoneArePending) {
Vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
cancel_task {
task_id : 1
}
}
)",
nullptr, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCancelTask, 0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest,
TaskDurationBlocksOtherPendingTasksPostedFromOutsideOfTask) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
// Posts a task of duration 40 ms and a 10 ms delayed task of duration 20 ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
delay_ms : 10
task {
task_id : 1
duration_ms : 20
}
}
}
initial_thread_actions {
action_id :2
post_delayed_task {
delay_ms : 0
task {
task_id : 2
duration_ms : 40
}
}
})",
&executed_tasks, &executed_actions);
Vector<TaskForTest> expected_tasks;
// Task with id 2 is expected to run first and block the other task until it
// done.
expected_tasks.emplace_back(2, 0, 40);
expected_tasks.emplace_back(1, 40, 60);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest,
TaskDurationBlocksOtherNonNestableTaskWhenPostedFromWithinTask) {
Vector<TaskForTest> executed_tasks;
// Posts an instant task of duration 40 ms that posts another non-nested
// instant task.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
post_delayed_task {
task {
task_id : 1
duration_ms : 40
actions {
post_delayed_task {
task {
task_id : 2
}
}
}
}
}
})",
&executed_tasks, nullptr);
Vector<TaskForTest> expected_tasks;
// Task with task id 1 is expected to run for 40 ms, and block the other
// posted task from running until its done. Note that the task with id 2 is
// blocked since it is non-nested, so it is not supposed to run from win
// the posting task.
expected_tasks.emplace_back(1, 0, 40);
expected_tasks.emplace_back(2, 40, 40);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, PostNonEmptyTask) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
// Posts a 5 ms delayed task of duration 40 ms that creates a task queue,
// posts a 4 ms delayed task, posts an instant task, creates a task queue,
// and then posts a 40 ms delayed task.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
delay_ms: 5
task {
task_id : 1
duration_ms : 40
actions {
action_id : 2
create_task_queue {
}
}
actions {
action_id : 3
post_delayed_task {
delay_ms : 4
task {
task_id : 2
}
}
}
actions {
action_id: 4
post_delayed_task {
task {
task_id : 3
}
}
}
actions {
action_id : 5
create_task_queue {
}
}
actions {
action_id : 6
post_delayed_task {
delay_ms : 40
task {
task_id : 4
}
}
}
}
}
})",
&executed_tasks, &executed_actions);
Vector<TaskForTest> expected_tasks;
// Task with task id 1 is expected to run first, and block all other pending
// tasks until its done. The remaining tasks will be executed in
// non-decreasing order of the delay parameter with ties broken by
// the post order.
expected_tasks.emplace_back(1, 5, 45);
expected_tasks.emplace_back(3, 45, 45);
expected_tasks.emplace_back(2, 45, 45);
expected_tasks.emplace_back(4, 45, 45);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kCreateTaskQueue,
5);
expected_actions.emplace_back(3, ActionForTest::ActionType::kPostDelayedTask,
5);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
5);
expected_actions.emplace_back(5, ActionForTest::ActionType::kCreateTaskQueue,
5);
expected_actions.emplace_back(6, ActionForTest::ActionType::kPostDelayedTask,
5);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
Vector<TaskForTest> executed_tasks;
Vector<ActionForTest> executed_actions;
// Creates a task queue, posts a task after 20 ms delay, posts a 10 ms
// duration task after 15 ms of delay, and posts a task after 100 ms of delay.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
task {
task_id : 1
actions {
action_id : 3
create_task_queue {
}
}
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 20
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 5
post_delayed_task {
delay_ms : 15
task {
task_id : 3
duration_ms : 10
}
}
}
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 100
task {
task_id : 4
}
}
})",
&executed_tasks, &executed_actions);
Vector<TaskForTest> expected_tasks;
// Tasks are expected to run in order of non-decreasing delay with ties broken
// by order of posting. Note that the task with id 3 will block the task with
// id 2 from running at its scheduled time.
expected_tasks.emplace_back(1, 10, 10);
expected_tasks.emplace_back(3, 15, 25);
expected_tasks.emplace_back(2, 25, 25);
expected_tasks.emplace_back(4, 100, 100);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(5, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(6, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kCreateTaskQueue,
10);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, InsertAndRemoveFence) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
// Describes a test that inserts a fence to a task queue after a delay of
// 20ms, posts a task to it after a delay of 25ms, and removes the fence after
// a delay of 30ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue{
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 20
task_queue_id : 2
task {
task_id : 1
actions {
action_id : 3
insert_fence {
position: NOW
task_queue_id: 1
}
}
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 30
task_queue_id : 2
task {
task_id : 2
actions {
action_id : 5
remove_fence {
task_queue_id: 1
}
}
}
}
}
initial_thread_actions {
action_id: 6
post_delayed_task {
delay_ms: 25
task_queue_id: 1
task {
task_id: 3
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(4, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(6, ActionForTest::ActionType::kPostDelayedTask,
0);
expected_actions.emplace_back(3, ActionForTest::ActionType::kInsertFence, 20);
expected_actions.emplace_back(5, ActionForTest::ActionType::kRemoveFence, 30);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
Vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 20, 20);
expected_tasks.emplace_back(2, 30, 30);
// Task with id 3 will not execute until the fence is removed from the task
// queue it was posted to.
expected_tasks.emplace_back(3, 30, 30);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, ThrottleTaskQueue) {
Vector<ActionForTest> executed_actions;
Vector<TaskForTest> executed_tasks;
// Describes a test that throttles a task queue, and posts a task to it.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
insert_fence {
position: BEGINNING_OF_TIME
task_queue_id: 1
}
}
initial_thread_actions {
action_id: 2
post_delayed_task {
task_queue_id: 1
task {
task_id: 3
}
}
})",
&executed_tasks, &executed_actions);
Vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kInsertFence, 0);
expected_actions.emplace_back(2, ActionForTest::ActionType::kPostDelayedTask,
0);
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
// Task queue with id 1 is throttled, so posted tasks will not get executed.
EXPECT_TRUE(executed_tasks.IsEmpty());
}
TEST(SequenceManagerFuzzerProcessorTest, MultipleThreadsButNotInteracting) {
std::string thread_actions =
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
task {
task_id : 1
actions {
action_id : 3
create_task_queue {
}
}
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 20
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 5
post_delayed_task {
delay_ms : 15
task {
task_id : 3
duration_ms : 10
}
}
}
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 100
task {
task_id : 4
}
}
}
}
})";
// Threads initialized with same list of actions.
Vector<std::string> threads{thread_actions, thread_actions, thread_actions,
thread_actions, thread_actions};
Vector<Vector<ActionForTest>> executed_actions;
Vector<Vector<TaskForTest>> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
base::StrCat(threads), &executed_tasks, &executed_actions);
// |expected_tasks[0]| is empty since the main thread doesn't execute any
// task.
Vector<Vector<TaskForTest>> expected_tasks(6);
for (int i = 1; i <= 5; i++) {
// Created thread tasks: tasks are expected to run in order of
// non-decreasing delay with ties broken by order of posting. Note that the
// task with id 3 will block the task with id 2 from running at its
// scheduled time.
expected_tasks[i].emplace_back(1, 10, 10);
expected_tasks[i].emplace_back(3, 15, 25);
expected_tasks[i].emplace_back(2, 25, 25);
expected_tasks[i].emplace_back(4, 100, 100);
}
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
Vector<Vector<ActionForTest>> expected_actions(6);
for (int i = 1; i <= 5; i++) {
// Main thread action: creating the Ith thread.
expected_actions[0].emplace_back(
1, ActionForTest::ActionType::kCreateThread, 0);
// Actions of the Ith thread.
expected_actions[i].emplace_back(
1, ActionForTest::ActionType::kCreateTaskQueue, 0);
expected_actions[i].emplace_back(
2, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
4, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
5, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
6, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
3, ActionForTest::ActionType::kCreateTaskQueue, 10);
}
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, CreateThreadRecursively) {
Vector<Vector<ActionForTest>> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 2
create_thread {
initial_thread_actions {
action_id : 3
create_thread {}
}
}
}
}
}
)",
nullptr, &executed_actions);
// Last thread has no actions, so |expected_actions[3]| is empty.
Vector<Vector<ActionForTest>> expected_actions(4);
for (int i = 0; i <= 2; i++) {
// Actions of the Ith thread.
expected_actions[i].emplace_back(
i + 1, ActionForTest::ActionType::kCreateThread, 0);
}
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
// Flaky. See https://crbug.com/878203.
TEST(SequenceManagerFuzzerProcessorTest, DISABLED_PostTaskToCreateThread) {
Vector<Vector<ActionForTest>> executed_actions;
Vector<Vector<TaskForTest>> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
task_id: 1
actions {
action_id : 3
create_thread {
}
}
}
}
}
initial_thread_actions {
action_id : 4
create_thread {
}
}
}
}
main_thread_actions {
action_id : 5
create_thread {
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 20
task {
task_id: 2
duration_ms : 30
actions {
action_id : 7
create_thread {
}
}
}
}
}
}
})",
&executed_tasks, &executed_actions);
// Third, Fourth and Fifth created threads execute no actions.
Vector<Vector<ActionForTest>> expected_actions(6);
expected_actions[0].emplace_back(1, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[0].emplace_back(5, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[1].emplace_back(
2, ActionForTest::ActionType::kPostDelayedTask, 0);
// Posted messages execute after instant actions.
expected_actions[1].emplace_back(4, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[1].emplace_back(3, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[2].emplace_back(
6, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[2].emplace_back(7, ActionForTest::ActionType::kCreateThread,
20);
// Order isn't deterministic, since threads only start running once all the
// initial threads are created, and as a result the logging order isn't
// deterministic,
EXPECT_THAT(executed_actions, UnorderedElementsAreArray(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest,
CrossThreadPostingOnlyOneThreadAvaible) {
Vector<Vector<TaskForTest>> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
create_thread {
initial_thread_actions{
post_delayed_task {
delay_ms: 30
task {
task_id: 1
actions {
cross_thread_post {
thread_id : 1
task {
task_id: 2
}
}
}
}
}
}
}
})",
&executed_tasks, nullptr);
Vector<Vector<TaskForTest>> expected_tasks(2);
expected_tasks[1].emplace_back(1, 30, 30);
expected_tasks[1].emplace_back(2, 30, 30);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, CrossThreadPosting) {
Vector<Vector<TaskForTest>> executed_tasks;
// Thread posts a 10ms delayed task of duration 20ms to another thread.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
create_thread {
}
}
main_thread_actions{
create_thread {
initial_thread_actions{
cross_thread_post {
thread_id : 0
delay_ms: 10
task {
duration_ms: 20
task_id: 1
}
}
}
}
})",
&executed_tasks, nullptr);
Vector<Vector<TaskForTest>> expected_tasks(3);
expected_tasks[1].emplace_back(1, 10, 30);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, AdvanceThreadsClockSynchronously) {
Vector<Vector<TaskForTest>> executed_tasks;
// First created thread has a task posted with a delay of 30 ms. Second thread
// posts a task to be executed on the first thread after a 10 ms delay.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
create_thread {
initial_thread_actions {
post_delayed_task {
delay_ms : 30
task {
task_id: 1
}
}
}
}
}
main_thread_actions {
create_thread {
initial_thread_actions{
cross_thread_post {
delay_ms: 10
thread_id : 0
task {
task_id: 2
}
}
}
}
})",
&executed_tasks, nullptr);
Vector<Vector<TaskForTest>> expected_tasks(3);
// This test checks if the clock is correctly advanced. It does so
// by checking if the tasks were executed at the expected times.
expected_tasks[1].emplace_back(2, 10, 10);
expected_tasks[1].emplace_back(1, 30, 30);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest,
AdvanceThreadClockByTaskDurationSynchronously) {
Vector<Vector<TaskForTest>> executed_tasks;
// A thread has instant task posted with a duration of 50ms, which posts
// a task with duration 20ms to be executed after 10ms on another thread.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
create_thread {
}
}
main_thread_actions {
create_thread {
initial_thread_actions{
post_delayed_task {
task {
task_id: 1
duration_ms: 50
actions {
cross_thread_post {
thread_id: 0
delay_ms : 10
task {
task_id : 2
duration_ms: 20
}
}
}
}
}
}
}
})",
&executed_tasks, nullptr);
Vector<Vector<TaskForTest>> expected_tasks(3);
// This test checks if the clock is correctly advanced when tasks have
// durations. It does so by checking if the tasks were executed at the
// expected times.
expected_tasks[1].emplace_back(2, 10, 30);
expected_tasks[2].emplace_back(1, 0, 50);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
}
TEST(SequenceManagerFuzzerProcessorTest, CrossThreadPostFromChildThreads) {
// We do not wait for child threads to start, so their ThreadManager* might
// not be registered by the time we cross post the task
SequenceManagerTestDescription description;
auto* main_thread =
description.add_main_thread_actions()->mutable_create_thread();
for (int i = 0; i < 100; ++i) {
auto* child_thread =
main_thread->add_initial_thread_actions()->mutable_create_thread();
child_thread->add_initial_thread_actions()
->mutable_cross_thread_post()
->set_thread_id(i + 1234);
}
SequenceManagerFuzzerProcessor::ParseAndRun(description);
}
} // namespace sequence_manager
} // namespace base