diff options
Diffstat (limited to 'base/test/sequenced_task_runner_test_template.cc')
-rw-r--r-- | base/test/sequenced_task_runner_test_template.cc | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/base/test/sequenced_task_runner_test_template.cc b/base/test/sequenced_task_runner_test_template.cc new file mode 100644 index 0000000000..de6849230e --- /dev/null +++ b/base/test/sequenced_task_runner_test_template.cc @@ -0,0 +1,269 @@ +// Copyright (c) 2012 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 "base/test/sequenced_task_runner_test_template.h" + +#include <ostream> + +#include "base/location.h" + +namespace base { + +namespace internal { + +TaskEvent::TaskEvent(int i, Type type) + : i(i), type(type) { +} + +SequencedTaskTracker::SequencedTaskTracker() + : next_post_i_(0), + task_end_count_(0), + task_end_cv_(&lock_) { +} + +void SequencedTaskTracker::PostWrappedNonNestableTask( + SequencedTaskRunner* task_runner, + const Closure& task) { + AutoLock event_lock(lock_); + const int post_i = next_post_i_++; + Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this, + task, post_i); + task_runner->PostNonNestableTask(FROM_HERE, wrapped_task); + TaskPosted(post_i); +} + +void SequencedTaskTracker::PostWrappedNestableTask( + SequencedTaskRunner* task_runner, + const Closure& task) { + AutoLock event_lock(lock_); + const int post_i = next_post_i_++; + Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this, + task, post_i); + task_runner->PostTask(FROM_HERE, wrapped_task); + TaskPosted(post_i); +} + +void SequencedTaskTracker::PostWrappedDelayedNonNestableTask( + SequencedTaskRunner* task_runner, + const Closure& task, + TimeDelta delay) { + AutoLock event_lock(lock_); + const int post_i = next_post_i_++; + Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this, + task, post_i); + task_runner->PostNonNestableDelayedTask(FROM_HERE, wrapped_task, delay); + TaskPosted(post_i); +} + +void SequencedTaskTracker::PostNonNestableTasks( + SequencedTaskRunner* task_runner, + int task_count) { + for (int i = 0; i < task_count; ++i) { + PostWrappedNonNestableTask(task_runner, Closure()); + } +} + +void SequencedTaskTracker::RunTask(const Closure& task, int task_i) { + TaskStarted(task_i); + if (!task.is_null()) + task.Run(); + TaskEnded(task_i); +} + +void SequencedTaskTracker::TaskPosted(int i) { + // Caller must own |lock_|. + events_.push_back(TaskEvent(i, TaskEvent::POST)); +} + +void SequencedTaskTracker::TaskStarted(int i) { + AutoLock lock(lock_); + events_.push_back(TaskEvent(i, TaskEvent::START)); +} + +void SequencedTaskTracker::TaskEnded(int i) { + AutoLock lock(lock_); + events_.push_back(TaskEvent(i, TaskEvent::END)); + ++task_end_count_; + task_end_cv_.Signal(); +} + +const std::vector<TaskEvent>& +SequencedTaskTracker::GetTaskEvents() const { + return events_; +} + +void SequencedTaskTracker::WaitForCompletedTasks(int count) { + AutoLock lock(lock_); + while (task_end_count_ < count) + task_end_cv_.Wait(); +} + +SequencedTaskTracker::~SequencedTaskTracker() = default; + +void PrintTo(const TaskEvent& event, std::ostream* os) { + *os << "(i=" << event.i << ", type="; + switch (event.type) { + case TaskEvent::POST: *os << "POST"; break; + case TaskEvent::START: *os << "START"; break; + case TaskEvent::END: *os << "END"; break; + } + *os << ")"; +} + +namespace { + +// Returns the task ordinals for the task event type |type| in the order that +// they were recorded. +std::vector<int> GetEventTypeOrder(const std::vector<TaskEvent>& events, + TaskEvent::Type type) { + std::vector<int> tasks; + std::vector<TaskEvent>::const_iterator event; + for (event = events.begin(); event != events.end(); ++event) { + if (event->type == type) + tasks.push_back(event->i); + } + return tasks; +} + +// Returns all task events for task |task_i|. +std::vector<TaskEvent::Type> GetEventsForTask( + const std::vector<TaskEvent>& events, + int task_i) { + std::vector<TaskEvent::Type> task_event_orders; + std::vector<TaskEvent>::const_iterator event; + for (event = events.begin(); event != events.end(); ++event) { + if (event->i == task_i) + task_event_orders.push_back(event->type); + } + return task_event_orders; +} + +// Checks that the task events for each task in |events| occur in the order +// {POST, START, END}, and that there is only one instance of each event type +// per task. +::testing::AssertionResult CheckEventOrdersForEachTask( + const std::vector<TaskEvent>& events, + int task_count) { + std::vector<TaskEvent::Type> expected_order; + expected_order.push_back(TaskEvent::POST); + expected_order.push_back(TaskEvent::START); + expected_order.push_back(TaskEvent::END); + + // This is O(n^2), but it runs fast enough currently so is not worth + // optimizing. + for (int i = 0; i < task_count; ++i) { + const std::vector<TaskEvent::Type> task_events = + GetEventsForTask(events, i); + if (task_events != expected_order) { + return ::testing::AssertionFailure() + << "Events for task " << i << " are out of order; expected: " + << ::testing::PrintToString(expected_order) << "; actual: " + << ::testing::PrintToString(task_events); + } + } + return ::testing::AssertionSuccess(); +} + +// Checks that no two tasks were running at the same time. I.e. the only +// events allowed between the START and END of a task are the POSTs of other +// tasks. +::testing::AssertionResult CheckNoTaskRunsOverlap( + const std::vector<TaskEvent>& events) { + // If > -1, we're currently inside a START, END pair. + int current_task_i = -1; + + std::vector<TaskEvent>::const_iterator event; + for (event = events.begin(); event != events.end(); ++event) { + bool spurious_event_found = false; + + if (current_task_i == -1) { // Not inside a START, END pair. + switch (event->type) { + case TaskEvent::POST: + break; + case TaskEvent::START: + current_task_i = event->i; + break; + case TaskEvent::END: + spurious_event_found = true; + break; + } + + } else { // Inside a START, END pair. + bool interleaved_task_detected = false; + + switch (event->type) { + case TaskEvent::POST: + if (event->i == current_task_i) + spurious_event_found = true; + break; + case TaskEvent::START: + interleaved_task_detected = true; + break; + case TaskEvent::END: + if (event->i != current_task_i) + interleaved_task_detected = true; + else + current_task_i = -1; + break; + } + + if (interleaved_task_detected) { + return ::testing::AssertionFailure() + << "Found event " << ::testing::PrintToString(*event) + << " between START and END events for task " << current_task_i + << "; event dump: " << ::testing::PrintToString(events); + } + } + + if (spurious_event_found) { + const int event_i = event - events.begin(); + return ::testing::AssertionFailure() + << "Spurious event " << ::testing::PrintToString(*event) + << " at position " << event_i << "; event dump: " + << ::testing::PrintToString(events); + } + } + + return ::testing::AssertionSuccess(); +} + +} // namespace + +::testing::AssertionResult CheckNonNestableInvariants( + const std::vector<TaskEvent>& events, + int task_count) { + const std::vector<int> post_order = + GetEventTypeOrder(events, TaskEvent::POST); + const std::vector<int> start_order = + GetEventTypeOrder(events, TaskEvent::START); + const std::vector<int> end_order = + GetEventTypeOrder(events, TaskEvent::END); + + if (start_order != post_order) { + return ::testing::AssertionFailure() + << "Expected START order (which equals actual POST order): \n" + << ::testing::PrintToString(post_order) + << "\n Actual START order:\n" + << ::testing::PrintToString(start_order); + } + + if (end_order != post_order) { + return ::testing::AssertionFailure() + << "Expected END order (which equals actual POST order): \n" + << ::testing::PrintToString(post_order) + << "\n Actual END order:\n" + << ::testing::PrintToString(end_order); + } + + const ::testing::AssertionResult result = + CheckEventOrdersForEachTask(events, task_count); + if (!result) + return result; + + return CheckNoTaskRunsOverlap(events); +} + +} // namespace internal + +} // namespace base |