Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This class defines tests that implementations of SequencedTaskRunner should
      6 // pass in order to be conformant. See task_runner_test_template.h for a
      7 // description of how to use the constructs in this file; these work the same.
      8 
      9 #ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
     10 #define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
     11 
     12 #include <cstddef>
     13 #include <iosfwd>
     14 #include <vector>
     15 
     16 #include "base/basictypes.h"
     17 #include "base/bind.h"
     18 #include "base/callback.h"
     19 #include "base/memory/ref_counted.h"
     20 #include "base/sequenced_task_runner.h"
     21 #include "base/synchronization/condition_variable.h"
     22 #include "base/synchronization/lock.h"
     23 #include "base/time/time.h"
     24 #include "testing/gtest/include/gtest/gtest.h"
     25 
     26 namespace base {
     27 
     28 namespace internal {
     29 
     30 struct TaskEvent {
     31   enum Type { POST, START, END };
     32   TaskEvent(int i, Type type);
     33   int i;
     34   Type type;
     35 };
     36 
     37 // Utility class used in the tests below.
     38 class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
     39  public:
     40   SequencedTaskTracker();
     41 
     42   // Posts the non-nestable task |task|, and records its post event.
     43   void PostWrappedNonNestableTask(
     44       const scoped_refptr<SequencedTaskRunner>& task_runner,
     45       const Closure& task);
     46 
     47   // Posts the nestable task |task|, and records its post event.
     48   void PostWrappedNestableTask(
     49       const scoped_refptr<SequencedTaskRunner>& task_runner,
     50       const Closure& task);
     51 
     52   // Posts the delayed non-nestable task |task|, and records its post event.
     53   void PostWrappedDelayedNonNestableTask(
     54       const scoped_refptr<SequencedTaskRunner>& task_runner,
     55       const Closure& task,
     56       TimeDelta delay);
     57 
     58   // Posts |task_count| non-nestable tasks.
     59   void PostNonNestableTasks(
     60       const scoped_refptr<SequencedTaskRunner>& task_runner,
     61       int task_count);
     62 
     63   const std::vector<TaskEvent>& GetTaskEvents() const;
     64 
     65   // Returns after the tracker observes a total of |count| task completions.
     66   void WaitForCompletedTasks(int count);
     67 
     68  private:
     69   friend class RefCountedThreadSafe<SequencedTaskTracker>;
     70 
     71   ~SequencedTaskTracker();
     72 
     73   // A task which runs |task|, recording the start and end events.
     74   void RunTask(const Closure& task, int task_i);
     75 
     76   // Records a post event for task |i|. The owner is expected to be holding
     77   // |lock_| (unlike |TaskStarted| and |TaskEnded|).
     78   void TaskPosted(int i);
     79 
     80   // Records a start event for task |i|.
     81   void TaskStarted(int i);
     82 
     83   // Records a end event for task |i|.
     84   void TaskEnded(int i);
     85 
     86   // Protects events_, next_post_i_, task_end_count_ and task_end_cv_.
     87   Lock lock_;
     88 
     89   // The events as they occurred for each task (protected by lock_).
     90   std::vector<TaskEvent> events_;
     91 
     92   // The ordinal to be used for the next task-posting task (protected by
     93   // lock_).
     94   int next_post_i_;
     95 
     96   // The number of task end events we've received.
     97   int task_end_count_;
     98   ConditionVariable task_end_cv_;
     99 
    100   DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
    101 };
    102 
    103 void PrintTo(const TaskEvent& event, std::ostream* os);
    104 
    105 // Checks the non-nestable task invariants for all tasks in |events|.
    106 //
    107 // The invariants are:
    108 // 1) Events started and ended in the same order that they were posted.
    109 // 2) Events for an individual tasks occur in the order {POST, START, END},
    110 //    and there is only one instance of each event type for a task.
    111 // 3) The only events between a task's START and END events are the POSTs of
    112 //    other tasks. I.e. tasks were run sequentially, not interleaved.
    113 ::testing::AssertionResult CheckNonNestableInvariants(
    114     const std::vector<TaskEvent>& events,
    115     int task_count);
    116 
    117 }  // namespace internal
    118 
    119 template <typename TaskRunnerTestDelegate>
    120 class SequencedTaskRunnerTest : public testing::Test {
    121  protected:
    122   SequencedTaskRunnerTest()
    123       : task_tracker_(new internal::SequencedTaskTracker()) {}
    124 
    125   const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
    126   TaskRunnerTestDelegate delegate_;
    127 };
    128 
    129 TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
    130 
    131 // This test posts N non-nestable tasks in sequence, and expects them to run
    132 // in FIFO order, with no part of any two tasks' execution
    133 // overlapping. I.e. that each task starts only after the previously-posted
    134 // one has finished.
    135 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
    136   const int kTaskCount = 1000;
    137 
    138   this->delegate_.StartTaskRunner();
    139   const scoped_refptr<SequencedTaskRunner> task_runner =
    140       this->delegate_.GetTaskRunner();
    141 
    142   this->task_tracker_->PostWrappedNonNestableTask(
    143       task_runner, Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
    144   for (int i = 1; i < kTaskCount; ++i) {
    145     this->task_tracker_->PostWrappedNonNestableTask(task_runner, Closure());
    146   }
    147 
    148   this->delegate_.StopTaskRunner();
    149 
    150   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    151                                          kTaskCount));
    152 }
    153 
    154 // This test posts N nestable tasks in sequence. It has the same expectations
    155 // as SequentialNonNestable because even though the tasks are nestable, they
    156 // will not be run nestedly in this case.
    157 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
    158   const int kTaskCount = 1000;
    159 
    160   this->delegate_.StartTaskRunner();
    161   const scoped_refptr<SequencedTaskRunner> task_runner =
    162       this->delegate_.GetTaskRunner();
    163 
    164   this->task_tracker_->PostWrappedNestableTask(
    165       task_runner,
    166       Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
    167   for (int i = 1; i < kTaskCount; ++i) {
    168     this->task_tracker_->PostWrappedNestableTask(task_runner, Closure());
    169   }
    170 
    171   this->delegate_.StopTaskRunner();
    172 
    173   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    174                                          kTaskCount));
    175 }
    176 
    177 // This test posts non-nestable tasks in order of increasing delay, and checks
    178 // that that the tasks are run in FIFO order and that there is no execution
    179 // overlap whatsoever between any two tasks.
    180 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
    181   const int kTaskCount = 20;
    182   const int kDelayIncrementMs = 50;
    183 
    184   this->delegate_.StartTaskRunner();
    185   const scoped_refptr<SequencedTaskRunner> task_runner =
    186       this->delegate_.GetTaskRunner();
    187 
    188   for (int i = 0; i < kTaskCount; ++i) {
    189     this->task_tracker_->PostWrappedDelayedNonNestableTask(
    190         task_runner,
    191         Closure(),
    192         TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
    193   }
    194 
    195   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    196   this->delegate_.StopTaskRunner();
    197 
    198   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    199                                          kTaskCount));
    200 }
    201 
    202 // This test posts a fast, non-nestable task from within each of a number of
    203 // slow, non-nestable tasks and checks that they all run in the sequence they
    204 // were posted in and that there is no execution overlap whatsoever.
    205 TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
    206   const int kParentCount = 10;
    207   const int kChildrenPerParent = 10;
    208 
    209   this->delegate_.StartTaskRunner();
    210   const scoped_refptr<SequencedTaskRunner> task_runner =
    211       this->delegate_.GetTaskRunner();
    212 
    213   for (int i = 0; i < kParentCount; ++i) {
    214     Closure task = Bind(
    215         &internal::SequencedTaskTracker::PostNonNestableTasks,
    216         this->task_tracker_,
    217         task_runner,
    218         kChildrenPerParent);
    219     this->task_tracker_->PostWrappedNonNestableTask(task_runner, task);
    220   }
    221 
    222   this->delegate_.StopTaskRunner();
    223 
    224   EXPECT_TRUE(CheckNonNestableInvariants(
    225       this->task_tracker_->GetTaskEvents(),
    226       kParentCount * (kChildrenPerParent + 1)));
    227 }
    228 
    229 // This test posts a delayed task, and checks that the task is run later than
    230 // the specified time.
    231 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) {
    232   const int kTaskCount = 1;
    233   const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
    234 
    235   this->delegate_.StartTaskRunner();
    236   const scoped_refptr<SequencedTaskRunner> task_runner =
    237       this->delegate_.GetTaskRunner();
    238 
    239   Time time_before_run = Time::Now();
    240   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    241       task_runner, Closure(), kDelay);
    242   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    243   this->delegate_.StopTaskRunner();
    244   Time time_after_run = Time::Now();
    245 
    246   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    247                                          kTaskCount));
    248   EXPECT_LE(kDelay, time_after_run - time_before_run);
    249 }
    250 
    251 // This test posts two tasks with the same delay, and checks that the tasks are
    252 // run in the order in which they were posted.
    253 //
    254 // NOTE: This is actually an approximate test since the API only takes a
    255 // "delay" parameter, so we are not exactly simulating two tasks that get
    256 // posted at the exact same time. It would be nice if the API allowed us to
    257 // specify the desired run time.
    258 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
    259   const int kTaskCount = 2;
    260   const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
    261 
    262   this->delegate_.StartTaskRunner();
    263   const scoped_refptr<SequencedTaskRunner> task_runner =
    264       this->delegate_.GetTaskRunner();
    265 
    266   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    267       task_runner, Closure(), kDelay);
    268   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    269       task_runner, Closure(), kDelay);
    270   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    271   this->delegate_.StopTaskRunner();
    272 
    273   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    274                                          kTaskCount));
    275 }
    276 
    277 // This test posts a normal task and a delayed task, and checks that the
    278 // delayed task runs after the normal task even if the normal task takes
    279 // a long time to run.
    280 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
    281   const int kTaskCount = 2;
    282 
    283   this->delegate_.StartTaskRunner();
    284   const scoped_refptr<SequencedTaskRunner> task_runner =
    285       this->delegate_.GetTaskRunner();
    286 
    287   this->task_tracker_->PostWrappedNonNestableTask(
    288       task_runner, base::Bind(&PlatformThread::Sleep,
    289                               TimeDelta::FromMilliseconds(50)));
    290   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    291       task_runner, Closure(), TimeDelta::FromMilliseconds(10));
    292   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    293   this->delegate_.StopTaskRunner();
    294 
    295   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    296                                          kTaskCount));
    297 }
    298 
    299 // Test that a pile of normal tasks and a delayed task run in the
    300 // time-to-run order.
    301 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
    302   const int kTaskCount = 11;
    303 
    304   this->delegate_.StartTaskRunner();
    305   const scoped_refptr<SequencedTaskRunner> task_runner =
    306       this->delegate_.GetTaskRunner();
    307 
    308   for (int i = 0; i < kTaskCount - 1; i++) {
    309     this->task_tracker_->PostWrappedNonNestableTask(
    310         task_runner, base::Bind(&PlatformThread::Sleep,
    311                                 TimeDelta::FromMilliseconds(50)));
    312   }
    313   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    314       task_runner, Closure(), TimeDelta::FromMilliseconds(10));
    315   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    316   this->delegate_.StopTaskRunner();
    317 
    318   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    319                                          kTaskCount));
    320 }
    321 
    322 
    323 // TODO(francoisk777 (at) gmail.com) Add a test, similiar to the above, which runs
    324 // some tasked nestedly (which should be implemented in the test
    325 // delegate). Also add, to the the test delegate, a predicate which checks
    326 // whether the implementation supports nested tasks.
    327 //
    328 
    329 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
    330                            SequentialNonNestable,
    331                            SequentialNestable,
    332                            SequentialDelayedNonNestable,
    333                            NonNestablePostFromNonNestableTask,
    334                            DelayedTaskBasic,
    335                            DelayedTasksSameDelay,
    336                            DelayedTaskAfterLongTask,
    337                            DelayedTaskAfterManyLongTasks);
    338 
    339 }  // namespace base
    340 
    341 #endif  // BASE_TASK_RUNNER_TEST_TEMPLATE_H_
    342