blob: 73e7ff743dc27ba536c3f031115e4867d9ca7059 [file] [log] [blame]
#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_fuzzer_processor.h"
#include <algorithm>
#include <string>
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
namespace base {
namespace sequence_manager {
namespace {
TaskQueue::QueuePriority ToTaskQueuePriority(
SequenceManagerTestDescription::QueuePriority priority) {
switch (priority) {
case SequenceManagerTestDescription::BEST_EFFORT:
return TaskQueue::kBestEffortPriority;
case SequenceManagerTestDescription::LOW:
return TaskQueue::kLowPriority;
case SequenceManagerTestDescription::UNDEFINED:
case SequenceManagerTestDescription::NORMAL:
return TaskQueue::kNormalPriority;
case SequenceManagerTestDescription::HIGH:
return TaskQueue::kHighPriority;
case SequenceManagerTestDescription::HIGHEST:
return TaskQueue::kHighestPriority;
case SequenceManagerTestDescription::CONTROL:
return TaskQueue::kControlPriority;
}
}
} // namespace
void SequenceManagerFuzzerProcessor::ParseAndRun(
const SequenceManagerTestDescription& description) {
SequenceManagerFuzzerProcessor processor;
processor.RunTest(description);
}
SequenceManagerFuzzerProcessor::SequenceManagerFuzzerProcessor()
: SequenceManagerFuzzerProcessor(false) {}
SequenceManagerFuzzerProcessor::SequenceManagerFuzzerProcessor(
bool log_for_testing)
: log_for_testing_(log_for_testing) {
test_task_runner_ = WrapRefCounted(
new TestMockTimeTaskRunner(TestMockTimeTaskRunner::Type::kBoundToThread));
// A zero clock triggers some assertions.
test_task_runner_->AdvanceMockTickClock(TimeDelta::FromMilliseconds(1));
initial_time_ = test_task_runner_->GetMockTickClock()->NowTicks();
manager_ =
SequenceManagerForTest::Create(nullptr, ThreadTaskRunnerHandle::Get(),
test_task_runner_->GetMockTickClock());
TaskQueue::Spec spec = TaskQueue::Spec("default_task_queue");
task_queues_.emplace_back(manager_->CreateTaskQueue<TestTaskQueue>(spec));
}
void SequenceManagerFuzzerProcessor::RunTest(
const SequenceManagerTestDescription& description) {
for (const auto& initial_action : description.initial_actions()) {
RunAction(initial_action);
}
test_task_runner_->FastForwardUntilNoTasksRemain();
}
void SequenceManagerFuzzerProcessor::RunAction(
const SequenceManagerTestDescription::Action& action) {
if (action.has_create_task_queue()) {
ExecuteCreateTaskQueueAction(action.action_id(),
action.create_task_queue());
} else if (action.has_set_queue_priority()) {
ExecuteSetQueuePriorityAction(action.action_id(),
action.set_queue_priority());
} else if (action.has_set_queue_enabled()) {
ExecuteSetQueueEnabledAction(action.action_id(),
action.set_queue_enabled());
} else if (action.has_create_queue_voter()) {
ExecuteCreateQueueVoterAction(action.action_id(),
action.create_queue_voter());
} else if (action.has_shutdown_task_queue()) {
ExecuteShutdownTaskQueueAction(action.action_id(),
action.shutdown_task_queue());
} else if (action.has_cancel_task()) {
ExecuteCancelTaskAction(action.action_id(), action.cancel_task());
} else {
ExecutePostDelayedTaskAction(action.action_id(),
action.post_delayed_task());
}
}
void SequenceManagerFuzzerProcessor::ExecutePostDelayedTaskAction(
uint64_t action_id,
const SequenceManagerTestDescription::PostDelayedTaskAction& action) {
DCHECK(!task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kPostDelayedTask,
test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
std::unique_ptr<Task> pending_task = std::make_unique<Task>(this);
// TODO(farahcharab) After adding non-nestable/nestable tasks, fix this to
// PostNonNestableDelayedTask for the former and PostDelayedTask for the
// latter.
chosen_task_queue->PostDelayedTask(
FROM_HERE,
BindOnce(&Task::Execute, pending_task->weak_ptr_factory_.GetWeakPtr(),
action.task()),
TimeDelta::FromMilliseconds(action.delay_ms()));
pending_tasks_.push_back(std::move(pending_task));
}
void SequenceManagerFuzzerProcessor::ExecuteCreateTaskQueueAction(
uint64_t action_id,
const SequenceManagerTestDescription::CreateTaskQueueAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCreateTaskQueue,
test_task_runner_->GetMockTickClock()->NowTicks());
TaskQueue::Spec spec = TaskQueue::Spec("test_task_queue");
task_queues_.emplace_back(manager_->CreateTaskQueue<TestTaskQueue>(spec));
task_queues_.back().queue->SetQueuePriority(
ToTaskQueuePriority(action.initial_priority()));
}
void SequenceManagerFuzzerProcessor::ExecuteSetQueuePriorityAction(
uint64_t action_id,
const SequenceManagerTestDescription::SetQueuePriorityAction& action) {
DCHECK(!task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kSetQueuePriority,
test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
chosen_task_queue->SetQueuePriority(ToTaskQueuePriority(action.priority()));
}
void SequenceManagerFuzzerProcessor::ExecuteSetQueueEnabledAction(
uint64_t action_id,
const SequenceManagerTestDescription::SetQueueEnabledAction& action) {
DCHECK(!task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kSetQueueEnabled,
test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
if (task_queues_[queue_index].voters.empty()) {
task_queues_[queue_index].voters.push_back(
chosen_task_queue->CreateQueueEnabledVoter());
}
size_t voter_index =
action.voter_id() % task_queues_[queue_index].voters.size();
task_queues_[queue_index].voters[voter_index]->SetQueueEnabled(
action.enabled());
}
void SequenceManagerFuzzerProcessor::ExecuteCreateQueueVoterAction(
uint64_t action_id,
const SequenceManagerTestDescription::CreateQueueVoterAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCreateQueueVoter,
test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
task_queues_[queue_index].voters.push_back(
chosen_task_queue->CreateQueueEnabledVoter());
}
void SequenceManagerFuzzerProcessor::ExecuteShutdownTaskQueueAction(
uint64_t action_id,
const SequenceManagerTestDescription::ShutdownTaskQueueAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kShutdownTaskQueue,
test_task_runner_->GetMockTickClock()->NowTicks());
// We always want to have a default task queue.
if (task_queues_.size() > 1) {
size_t queue_index = action.task_queue_id() % task_queues_.size();
task_queues_[queue_index].queue.get()->ShutdownTaskQueue();
task_queues_.erase(task_queues_.begin() + queue_index);
}
}
void SequenceManagerFuzzerProcessor::ExecuteCancelTaskAction(
uint64_t action_id,
const SequenceManagerTestDescription::CancelTaskAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCancelTask,
test_task_runner_->GetMockTickClock()->NowTicks());
if (!pending_tasks_.empty()) {
size_t queue_index = action.task_id() % pending_tasks_.size();
pending_tasks_[queue_index]->weak_ptr_factory_.InvalidateWeakPtrs();
// If it is already running, it is a parent task and will be deleted when
// it is done.
if (!pending_tasks_[queue_index]->is_running) {
pending_tasks_.erase(pending_tasks_.begin() + queue_index);
}
}
}
void SequenceManagerFuzzerProcessor::ExecuteTask(
const SequenceManagerTestDescription::Task& task) {
TimeTicks start_time = test_task_runner_->GetMockTickClock()->NowTicks();
// We can limit the depth of the nested post delayed action when processing
// the proto.
for (const auto& task_action : task.actions()) {
// TODO(farahcharab) Add run loop to deal with nested tasks later. So far,
// we are assuming tasks are non-nestable.
RunAction(task_action);
}
TimeTicks end_time = test_task_runner_->GetMockTickClock()->NowTicks();
test_task_runner_->AdvanceMockTickClock(
TimeDelta::FromMilliseconds(task.duration_ms()) -
(end_time - start_time));
LogTaskForTesting(task.task_id(), start_time,
test_task_runner_->GetMockTickClock()->NowTicks());
}
void SequenceManagerFuzzerProcessor::DeleteTask(Task* task) {
size_t i = 0;
while (i < pending_tasks_.size() && task != pending_tasks_[i].get()) {
i++;
}
pending_tasks_.erase(pending_tasks_.begin() + i);
}
void SequenceManagerFuzzerProcessor::LogTaskForTesting(uint64_t task_id,
TimeTicks start_time,
TimeTicks end_time) {
if (!log_for_testing_)
return;
uint64_t start_time_ms = (start_time - initial_time_).InMilliseconds();
uint64_t end_time_ms = (end_time - initial_time_).InMilliseconds();
ordered_tasks_.emplace_back(task_id, start_time_ms, end_time_ms);
}
void SequenceManagerFuzzerProcessor::LogActionForTesting(
uint64_t action_id,
ActionForTest::ActionType type,
TimeTicks start_time) {
if (!log_for_testing_)
return;
ordered_actions_.emplace_back(action_id, type,
(start_time - initial_time_).InMilliseconds());
}
const std::vector<SequenceManagerFuzzerProcessor::TaskForTest>&
SequenceManagerFuzzerProcessor::ordered_tasks() const {
return ordered_tasks_;
}
const std::vector<SequenceManagerFuzzerProcessor::ActionForTest>&
SequenceManagerFuzzerProcessor::ordered_actions() const {
return ordered_actions_;
}
SequenceManagerFuzzerProcessor::Task::Task(
SequenceManagerFuzzerProcessor* processor)
: is_running(false), processor_(processor), weak_ptr_factory_(this) {}
void SequenceManagerFuzzerProcessor::Task::Execute(
const SequenceManagerTestDescription::Task& task) {
is_running = true;
processor_->ExecuteTask(task);
processor_->DeleteTask(this);
}
SequenceManagerFuzzerProcessor::TaskForTest::TaskForTest(uint64_t id,
uint64_t start,
uint64_t end)
: task_id(id), start_time_ms(start), end_time_ms(end) {}
bool SequenceManagerFuzzerProcessor::TaskForTest::operator==(
const TaskForTest& rhs) const {
return task_id == rhs.task_id && start_time_ms == rhs.start_time_ms &&
end_time_ms == rhs.end_time_ms;
}
SequenceManagerFuzzerProcessor::ActionForTest::ActionForTest(uint64_t id,
ActionType type,
uint64_t start)
: action_id(id), action_type(type), start_time_ms(start) {}
bool SequenceManagerFuzzerProcessor::ActionForTest::operator==(
const ActionForTest& rhs) const {
return action_id == rhs.action_id && action_type == rhs.action_type &&
start_time_ms == rhs.start_time_ms;
}
} // namespace sequence_manager
} // namespace base