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 |