Chromium Code Reviews| Index: base/test/sequenced_task_runner_test_template.h |
| =================================================================== |
| --- base/test/sequenced_task_runner_test_template.h (revision 0) |
| +++ base/test/sequenced_task_runner_test_template.h (revision 0) |
| @@ -0,0 +1,308 @@ |
| +// 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. |
| + |
| +// This class defines tests that implementations of SequencedTaskRunner should |
| +// pass in order to be conformant. Here's how you use it to test your |
|
akalin
2012/03/20 22:16:08
Remove 2nd sentence, merge paragraph below with th
Francois
2012/03/26 09:33:21
Done.
|
| +// implementation. |
| +// |
| +// See task_runner_test_template.h for a description of how to use the |
| +// constructs in this file; these work the same. |
| + |
| +#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| +#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| +#pragma once |
| + |
| +#include <cstddef> |
| + |
|
akalin
2012/03/20 22:16:08
remove extra newline
Francois
2012/03/26 09:33:21
Done.
|
| +#include <vector> |
| + |
| +#include "base/basictypes.h" |
| +#include "base/bind.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/sequenced_task_runner.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/tracked_objects.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace base { |
| + |
| +namespace internal { |
| + |
| +// Utility class used in the tests below. |
| +class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { |
| + public: |
| + struct TaskPhase { |
|
akalin
2012/03/20 22:16:08
I think TaskEvent is a better name
Francois
2012/03/26 09:33:21
Done.
|
| + enum Type { POST, START, END }; |
| + TaskPhase(int i, Type type) : i_(i), type_(type) {} |
| + bool operator==(const TaskPhase& phase) { |
|
akalin
2012/03/20 22:16:08
prefer
bool Equals(const TaskPhase& phase) const
Francois
2012/03/26 09:33:21
Will do next time, but it isn't required anymore,
|
| + return (i_ == phase.i_ && type_ == phase.type_); |
| + } |
| + int i_; |
|
akalin
2012/03/20 22:16:08
typically public member variables don't have an ap
Francois
2012/03/26 09:33:21
Done.
|
| + Type type_; |
| + }; |
| + |
| + SequencedTaskTracker(); |
| + |
| + int GetNextPostOrdinal(); |
|
akalin
2012/03/20 22:16:08
shouldn't need this function (see comments below)
Francois
2012/03/26 09:33:21
Done.
|
| + |
| + void TaskPosted(int i); |
|
akalin
2012/03/20 22:16:08
these functions shouldn't be exposed. Instead, yo
Francois
2012/03/26 09:33:21
Done. And DoNothing isn't required if null tasks a
|
| + void TaskStarted(int i); |
| + void TaskEnded(int i); |
| + |
| + const std::vector<TaskPhase>& GetTaskPhases() const; |
| + |
| + void FastTask(int i); |
| + void SlowTask(int i); |
| + |
| + // Task which posts a fast, non-nestable task. |
| + void PostFastNonNestableFromSlowNonNestable( |
| + scoped_refptr<SequencedTaskRunner> task_runner, |
| + const int base_status_i, |
| + const int child_count); |
| + |
| + private: |
| + friend class RefCountedThreadSafe<SequencedTaskTracker>; |
| + |
| + ~SequencedTaskTracker(); |
| + |
| + public: |
|
akalin
2012/03/20 22:16:08
these shouldn't be public
Francois
2012/03/26 09:33:21
Done.
|
| + std::vector<TaskPhase> phases_; |
| + mutable Lock phase_lock_; |
|
akalin
2012/03/20 22:16:08
add a comment saying that phase_lock_ protects pha
Francois
2012/03/26 09:33:21
Done.
|
| + |
| + private: |
| + int next_post_i_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); |
| +}; |
| + |
| +} // namespace internal |
| + |
| +template <typename TaskRunnerTestDelegate> |
| +class SequencedTaskRunnerTest : public testing::Test { |
| + protected: |
| + SequencedTaskRunnerTest() |
| + : task_tracker_(new internal::SequencedTaskTracker()) {} |
| + |
| + // Checks that each phase type occurs in the same order as the tasks were |
| + // posted, and that the phases for each given task occur in the expected |
| + // order (POST, START, END). |
| + ::testing::AssertionResult TaskPhasesInOrder(int task_count); |
| + |
| + const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; |
| + TaskRunnerTestDelegate delegate_; |
| +}; |
| + |
| +TYPED_TEST_CASE_P(SequencedTaskRunnerTest); |
| + |
| +// This test posts N non-nestable tasks in sequence, and expects them to run |
| +// in FIFO order, with no part of any two tasks' execution |
| +// overlapping. I.e. that each task starts only after the previously-posted |
| +// one has finished. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) { |
| + const int task_count = 1000; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task; |
| + if (i == 0) { |
|
akalin
2012/03/20 22:16:08
with the comments above, you can move the i=0 case
Francois
2012/03/26 09:33:21
Done.
|
| + task = Bind(&internal::SequencedTaskTracker::SlowTask, |
| + this->task_tracker_, i); |
| + } else { |
| + task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + } |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostNonNestableTask(FROM_HERE, task); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); |
| +} |
| + |
| +// This test posts N nestable tasks in sequence. It has the same expectations |
| +// as SequentialNonNestable because even though the tasks are nestable, they |
| +// will not be run nestedly in this case. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) { |
| + const int task_count = 1000; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task; |
| + if (i == 0) { |
| + task = Bind(&internal::SequencedTaskTracker::SlowTask, |
| + this->task_tracker_, i); |
| + } else { |
| + task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + } |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostTask(FROM_HERE, task); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); |
| +} |
| + |
| +// This test posts non-nestable tasks in order of increasing delay, and checks |
| +// that that the tasks are run in FIFO order and that there is no execution |
| +// overlap whatsoever between any two tasks. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { |
| + if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { |
| + DLOG(INFO) << "This SequencedTaskRunner doesn't handle " |
| + "non-zero delays; skipping"; |
| + return; |
| + } |
| + |
| + const int task_count = 20; |
| + const int delay_increment_ms = 50; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostNonNestableDelayedTask( |
| + FROM_HERE, |
| + task, |
| + TimeDelta::FromMilliseconds(delay_increment_ms * i)); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); |
| +} |
| + |
| +// This test posts a fast, non-nestable task from within each of a number of |
| +// slow, non-nestable tasks and checks that they all run in the sequence they |
| +// were posted in and that there is no execution overlap whatsoever. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) { |
| + const int parent_count = 10; |
| + const int children_per_parent = 10; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + const int end = parent_count; |
| + for (int i = 0; i < end; ++i) { |
| + AutoLock phase_lock(this->task_tracker_->phase_lock_); |
| + const int post_i = this->task_tracker_->GetNextPostOrdinal(); |
| + Closure task = Bind( |
| + &internal::SequencedTaskTracker::PostFastNonNestableFromSlowNonNestable, |
| + this->task_tracker_, |
| + task_runner, |
| + post_i, |
| + children_per_parent); |
| + this->task_tracker_->phases_.push_back( |
| + internal::SequencedTaskTracker::TaskPhase( |
| + post_i, internal::SequencedTaskTracker::TaskPhase::POST)); |
| + task_runner->PostNonNestableTask(FROM_HERE, task); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_TRUE(this->TaskPhasesInOrder(parent_count * |
| + (children_per_parent + 1))); |
| +} |
| + |
| +// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which which |
| +// runs some tasked nestedly (which should be implemented in the test |
| +// delegate). Also add, to the the test delegate, a predicate which checks |
| +// whether the implementation supports nested tasks. |
| +// |
| + |
| +REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, |
| + SequentialNonNestable, |
| + SequentialNestable, |
| + SequentialDelayedNonNestable, |
| + NonNestablePostFromNonNestableTask |
| + ); |
| + |
| +template <typename TaskRunnerTestDelegate> |
|
akalin
2012/03/20 22:16:08
the code below should probably go before the unit
Francois
2012/03/26 09:33:21
Done.
|
| +::testing::AssertionResult |
| +SequencedTaskRunnerTest<TaskRunnerTestDelegate>::TaskPhasesInOrder( |
|
akalin
2012/03/20 22:16:08
this function does a bit too much -- it's complica
Francois
2012/03/26 09:33:21
Done.
|
| + int task_count) { |
| + typedef internal::SequencedTaskTracker::TaskPhase TaskPhase; |
| + |
| + std::vector<TaskPhase> phases = task_tracker_->GetTaskPhases(); |
| + |
| + if (phases.size() != task_count * 3U) { |
| + return ::testing::AssertionFailure() |
| + << "Expecting " << (task_count * 3) << " task phases, but found only" |
| + << phases.size(); |
| + } |
| + |
| + // Stores the order of phases for each task. |
| + std::vector<std::vector<TaskPhase::Type> > phase_orders(task_count); |
| + |
| + int expected_post_task = 0; |
| + int expected_start_task = 0; |
| + int expected_end_task = 0; |
| + |
| + std::vector<TaskPhase>::const_iterator phase; |
| + for (phase = phases.begin(); phase != phases.end(); ++phase) { |
| + switch (phase->type_) { |
| + case TaskPhase::POST: |
| + if (phase->i_ != expected_post_task) { |
| + return ::testing::AssertionFailure() |
| + << "POST phase for task " << phase->i_ |
| + << " is out of order; expecting one for task " |
| + << expected_post_task << ", instead"; |
| + } |
| + phase_orders[expected_post_task].push_back(TaskPhase::POST); |
| + ++expected_post_task; |
| + break; |
| + case TaskPhase::START: |
| + if (phase->i_ != expected_start_task) { |
| + return ::testing::AssertionFailure() |
| + << "START phase for task " << phase->i_ |
| + << " is out of order; expecting one for task " |
| + << expected_start_task << ", instead"; |
| + } |
| + phase_orders[expected_start_task].push_back(TaskPhase::START); |
| + ++expected_start_task; |
| + break; |
| + case TaskPhase::END: |
| + if (phase->i_ != expected_end_task) { |
| + return ::testing::AssertionFailure() |
| + << "END phase for task " << phase->i_ |
| + << " is out of order; expecting one for task " |
| + << expected_end_task << ", instead"; |
| + } |
| + if (phase_orders[expected_end_task].size() != 2) { |
| + return ::testing::AssertionFailure() |
| + << "POST and/or START phase(s) for task " << phase->i_ |
| + << " did not occur before its END phase"; |
| + } |
| + if (phase_orders[expected_end_task][1] != TaskPhase::START) { |
| + return ::testing::AssertionFailure() |
| + << "START phase for task " << phase->i_ |
| + << " did not occur before its END phase"; |
| + } |
| + if (phase_orders[expected_end_task][0] != TaskPhase::POST) { |
| + return ::testing::AssertionFailure() |
| + << "POST phase for task " << phase->i_ |
| + << " did not occur before its START and END phases"; |
| + } |
| + ++expected_end_task; |
| + break; |
| + } |
| + } |
| + return ::testing::AssertionSuccess(); |
| +} |
| + |
| +} // namespace base |
| + |
| +#endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_ |
| Property changes on: base/test/sequenced_task_runner_test_template.h |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |