|  | // 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. | 
|  |  | 
|  | // SequencedTaskRunnerTest defines tests that implementations of | 
|  | // SequencedTaskRunner should pass in order to be conformant. | 
|  | // See task_runner_test_template.h for a description of how to use the | 
|  | // constructs in this file; these work the same. | 
|  |  | 
|  | #ifndef BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ | 
|  | #define BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <iosfwd> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/sequenced_task_runner.h" | 
|  | #include "base/synchronization/condition_variable.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "base/time/time.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | struct TaskEvent { | 
|  | enum Type { POST, START, END }; | 
|  | TaskEvent(int i, Type type); | 
|  | int i; | 
|  | Type type; | 
|  | }; | 
|  |  | 
|  | // Utility class used in the tests below. | 
|  | class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { | 
|  | public: | 
|  | SequencedTaskTracker(); | 
|  |  | 
|  | // Posts the non-nestable task |task|, and records its post event. | 
|  | void PostWrappedNonNestableTask(SequencedTaskRunner* task_runner, | 
|  | const Closure& task); | 
|  |  | 
|  | // Posts the nestable task |task|, and records its post event. | 
|  | void PostWrappedNestableTask(SequencedTaskRunner* task_runner, | 
|  | const Closure& task); | 
|  |  | 
|  | // Posts the delayed non-nestable task |task|, and records its post event. | 
|  | void PostWrappedDelayedNonNestableTask(SequencedTaskRunner* task_runner, | 
|  | const Closure& task, | 
|  | TimeDelta delay); | 
|  |  | 
|  | // Posts |task_count| non-nestable tasks. | 
|  | void PostNonNestableTasks(SequencedTaskRunner* task_runner, int task_count); | 
|  |  | 
|  | const std::vector<TaskEvent>& GetTaskEvents() const; | 
|  |  | 
|  | // Returns after the tracker observes a total of |count| task completions. | 
|  | void WaitForCompletedTasks(int count); | 
|  |  | 
|  | private: | 
|  | friend class RefCountedThreadSafe<SequencedTaskTracker>; | 
|  |  | 
|  | ~SequencedTaskTracker(); | 
|  |  | 
|  | // A task which runs |task|, recording the start and end events. | 
|  | void RunTask(const Closure& task, int task_i); | 
|  |  | 
|  | // Records a post event for task |i|. The owner is expected to be holding | 
|  | // |lock_| (unlike |TaskStarted| and |TaskEnded|). | 
|  | void TaskPosted(int i); | 
|  |  | 
|  | // Records a start event for task |i|. | 
|  | void TaskStarted(int i); | 
|  |  | 
|  | // Records a end event for task |i|. | 
|  | void TaskEnded(int i); | 
|  |  | 
|  | // Protects events_, next_post_i_, task_end_count_ and task_end_cv_. | 
|  | Lock lock_; | 
|  |  | 
|  | // The events as they occurred for each task (protected by lock_). | 
|  | std::vector<TaskEvent> events_; | 
|  |  | 
|  | // The ordinal to be used for the next task-posting task (protected by | 
|  | // lock_). | 
|  | int next_post_i_; | 
|  |  | 
|  | // The number of task end events we've received. | 
|  | int task_end_count_; | 
|  | ConditionVariable task_end_cv_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); | 
|  | }; | 
|  |  | 
|  | void PrintTo(const TaskEvent& event, std::ostream* os); | 
|  |  | 
|  | // Checks the non-nestable task invariants for all tasks in |events|. | 
|  | // | 
|  | // The invariants are: | 
|  | // 1) Events started and ended in the same order that they were posted. | 
|  | // 2) Events for an individual tasks occur in the order {POST, START, END}, | 
|  | //    and there is only one instance of each event type for a task. | 
|  | // 3) The only events between a task's START and END events are the POSTs of | 
|  | //    other tasks. I.e. tasks were run sequentially, not interleaved. | 
|  | ::testing::AssertionResult CheckNonNestableInvariants( | 
|  | const std::vector<TaskEvent>& events, | 
|  | int task_count); | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | template <typename TaskRunnerTestDelegate> | 
|  | class SequencedTaskRunnerTest : public testing::Test { | 
|  | protected: | 
|  | SequencedTaskRunnerTest() | 
|  | : task_tracker_(new internal::SequencedTaskTracker()) {} | 
|  |  | 
|  | 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 kTaskCount = 1000; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | this->task_tracker_->PostWrappedNonNestableTask( | 
|  | task_runner.get(), | 
|  | Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1))); | 
|  | for (int i = 1; i < kTaskCount; ++i) { | 
|  | this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(), | 
|  | Closure()); | 
|  | } | 
|  |  | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  | // 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 kTaskCount = 1000; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | this->task_tracker_->PostWrappedNestableTask( | 
|  | task_runner.get(), | 
|  | Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1))); | 
|  | for (int i = 1; i < kTaskCount; ++i) { | 
|  | this->task_tracker_->PostWrappedNestableTask(task_runner.get(), Closure()); | 
|  | } | 
|  |  | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | const int kTaskCount = 20; | 
|  | const int kDelayIncrementMs = 50; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | for (int i = 0; i < kTaskCount; ++i) { | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask( | 
|  | task_runner.get(), Closure(), | 
|  | TimeDelta::FromMilliseconds(kDelayIncrementMs * i)); | 
|  | } | 
|  |  | 
|  | this->task_tracker_->WaitForCompletedTasks(kTaskCount); | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  | // 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 kParentCount = 10; | 
|  | const int kChildrenPerParent = 10; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | for (int i = 0; i < kParentCount; ++i) { | 
|  | Closure task = Bind( | 
|  | &internal::SequencedTaskTracker::PostNonNestableTasks, | 
|  | this->task_tracker_, | 
|  | RetainedRef(task_runner), | 
|  | kChildrenPerParent); | 
|  | this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(), task); | 
|  | } | 
|  |  | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants( | 
|  | this->task_tracker_->GetTaskEvents(), | 
|  | kParentCount * (kChildrenPerParent + 1))); | 
|  | } | 
|  |  | 
|  | // This test posts two tasks with the same delay, and checks that the tasks are | 
|  | // run in the order in which they were posted. | 
|  | // | 
|  | // NOTE: This is actually an approximate test since the API only takes a | 
|  | // "delay" parameter, so we are not exactly simulating two tasks that get | 
|  | // posted at the exact same time. It would be nice if the API allowed us to | 
|  | // specify the desired run time. | 
|  | TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) { | 
|  | const int kTaskCount = 2; | 
|  | const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(), | 
|  | Closure(), kDelay); | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(), | 
|  | Closure(), kDelay); | 
|  | this->task_tracker_->WaitForCompletedTasks(kTaskCount); | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  | // This test posts a normal task and a delayed task, and checks that the | 
|  | // delayed task runs after the normal task even if the normal task takes | 
|  | // a long time to run. | 
|  | TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) { | 
|  | const int kTaskCount = 2; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | this->task_tracker_->PostWrappedNonNestableTask( | 
|  | task_runner.get(), | 
|  | base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50))); | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask( | 
|  | task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10)); | 
|  | this->task_tracker_->WaitForCompletedTasks(kTaskCount); | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  | // Test that a pile of normal tasks and a delayed task run in the | 
|  | // time-to-run order. | 
|  | TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) { | 
|  | const int kTaskCount = 11; | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | for (int i = 0; i < kTaskCount - 1; i++) { | 
|  | this->task_tracker_->PostWrappedNonNestableTask( | 
|  | task_runner.get(), | 
|  | base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50))); | 
|  | } | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask( | 
|  | task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10)); | 
|  | this->task_tracker_->WaitForCompletedTasks(kTaskCount); | 
|  | this->delegate_.StopTaskRunner(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | } | 
|  |  | 
|  |  | 
|  | // TODO(francoisk777@gmail.com) Add a test, similiar to the above, 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. | 
|  | // | 
|  |  | 
|  | // The SequencedTaskRunnerTest test case verifies behaviour that is expected | 
|  | // from a sequenced task runner in order to be conformant. | 
|  | REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, | 
|  | SequentialNonNestable, | 
|  | SequentialNestable, | 
|  | SequentialDelayedNonNestable, | 
|  | NonNestablePostFromNonNestableTask, | 
|  | DelayedTasksSameDelay, | 
|  | DelayedTaskAfterLongTask, | 
|  | DelayedTaskAfterManyLongTasks); | 
|  |  | 
|  | template <typename TaskRunnerTestDelegate> | 
|  | class SequencedTaskRunnerDelayedTest | 
|  | : public SequencedTaskRunnerTest<TaskRunnerTestDelegate> {}; | 
|  |  | 
|  | TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest); | 
|  |  | 
|  | // This test posts a delayed task, and checks that the task is run later than | 
|  | // the specified time. | 
|  | TYPED_TEST_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic) { | 
|  | const int kTaskCount = 1; | 
|  | const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); | 
|  |  | 
|  | this->delegate_.StartTaskRunner(); | 
|  | const scoped_refptr<SequencedTaskRunner> task_runner = | 
|  | this->delegate_.GetTaskRunner(); | 
|  |  | 
|  | Time time_before_run = Time::Now(); | 
|  | this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(), | 
|  | Closure(), kDelay); | 
|  | this->task_tracker_->WaitForCompletedTasks(kTaskCount); | 
|  | this->delegate_.StopTaskRunner(); | 
|  | Time time_after_run = Time::Now(); | 
|  |  | 
|  | EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), | 
|  | kTaskCount)); | 
|  | EXPECT_LE(kDelay, time_after_run - time_before_run); | 
|  | } | 
|  |  | 
|  | // SequencedTaskRunnerDelayedTest tests that the |delay| parameter of | 
|  | // is used to actually wait for |delay| ms before executing the task. | 
|  | // This is not mandatory for a SequencedTaskRunner to be compliant. | 
|  | REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic); | 
|  |  | 
|  | }  // namespace base | 
|  |  | 
|  | #endif  // BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |