| // 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) { |
| } |
| |
| void SequencedTaskTracker::PostWrappedNonNestableTask( |
| const scoped_refptr<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( |
| const scoped_refptr<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( |
| const scoped_refptr<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( |
| const scoped_refptr<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)); |
| } |
| |
| const std::vector<TaskEvent>& |
| SequencedTaskTracker::GetTaskEvents() const { |
| return events_; |
| } |
| |
| SequencedTaskTracker::~SequencedTaskTracker() { |
| } |
| |
| 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 |