Chromium Code Reviews| Index: base/test/sequenced_task_runner_test_template.cc |
| =================================================================== |
| --- base/test/sequenced_task_runner_test_template.cc (revision 0) |
| +++ base/test/sequenced_task_runner_test_template.cc (revision 0) |
| @@ -0,0 +1,263 @@ |
| +// 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> |
|
Francois
2012/03/26 09:33:21
For SequencedTaskTracker::TaskEvent::PrintTo, whic
|
| + |
| +#include "base/tracked_objects.h" |
|
akalin
2012/03/26 18:44:08
looks like this should be base/location.h (this ch
Francois
2012/03/28 14:32:42
Done.
|
| + |
| +namespace base { |
| + |
| +namespace internal { |
| + |
| +SequencedTaskTracker::TaskEvent::TaskEvent(int i_in, Type type_in) |
|
akalin
2012/03/26 18:44:08
don't use _in, just name them the same; C++'s vari
Francois
2012/03/28 14:32:42
Done.
|
| + : i(i_in), type(type_in) { |
| +} |
| + |
| +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<SequencedTaskTracker::TaskEvent>& |
| +SequencedTaskTracker::GetTaskEvents() const { |
| + return events_; |
| +} |
| + |
| +SequencedTaskTracker::~SequencedTaskTracker() { |
| +} |
| + |
| +void PrintTo(const SequencedTaskTracker::TaskEvent& event, std::ostream* os) { |
| + *os << "(i=" << event.i << ", type="; |
| + switch (event.type) { |
| + case SequencedTaskTracker::TaskEvent::POST: *os << "POST"; break; |
| + case SequencedTaskTracker::TaskEvent::START: *os << "START"; break; |
| + case SequencedTaskTracker::TaskEvent::END: *os << "END"; break; |
| + } |
| + *os << ")"; |
| +} |
| + |
| +void SleepForOneSecond() { |
| + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); |
| +} |
| + |
| +typedef SequencedTaskTracker::TaskEvent TaskEvent; |
| + |
| +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); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
|
akalin
2012/03/26 18:44:08
This takes n^2 time, which isn't great but probabl
Francois
2012/03/28 14:32:42
Done.
|
| + 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 |
| Property changes on: base/test/sequenced_task_runner_test_template.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |