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   // TODO(akalin): Remove this check (http://crbug.com/149144).
    182   if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
    183     DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
    184         "non-zero delays; skipping";
    185     return;
    186   }
    187 
    188   const int kTaskCount = 20;
    189   const int kDelayIncrementMs = 50;
    190 
    191   this->delegate_.StartTaskRunner();
    192   const scoped_refptr<SequencedTaskRunner> task_runner =
    193       this->delegate_.GetTaskRunner();
    194 
    195   for (int i = 0; i < kTaskCount; ++i) {
    196     this->task_tracker_->PostWrappedDelayedNonNestableTask(
    197         task_runner,
    198         Closure(),
    199         TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
    200   }
    201 
    202   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    203   this->delegate_.StopTaskRunner();
    204 
    205   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    206                                          kTaskCount));
    207 }
    208 
    209 // This test posts a fast, non-nestable task from within each of a number of
    210 // slow, non-nestable tasks and checks that they all run in the sequence they
    211 // were posted in and that there is no execution overlap whatsoever.
    212 TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
    213   const int kParentCount = 10;
    214   const int kChildrenPerParent = 10;
    215 
    216   this->delegate_.StartTaskRunner();
    217   const scoped_refptr<SequencedTaskRunner> task_runner =
    218       this->delegate_.GetTaskRunner();
    219 
    220   for (int i = 0; i < kParentCount; ++i) {
    221     Closure task = Bind(
    222         &internal::SequencedTaskTracker::PostNonNestableTasks,
    223         this->task_tracker_,
    224         task_runner,
    225         kChildrenPerParent);
    226     this->task_tracker_->PostWrappedNonNestableTask(task_runner, task);
    227   }
    228 
    229   this->delegate_.StopTaskRunner();
    230 
    231   EXPECT_TRUE(CheckNonNestableInvariants(
    232       this->task_tracker_->GetTaskEvents(),
    233       kParentCount * (kChildrenPerParent + 1)));
    234 }
    235 
    236 // This test posts a delayed task, and checks that the task is run later than
    237 // the specified time.
    238 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) {
    239   // TODO(akalin): Remove this check (http://crbug.com/149144).
    240   if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
    241     DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
    242         "non-zero delays; skipping";
    243     return;
    244   }
    245 
    246   const int kTaskCount = 1;
    247   const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
    248 
    249   this->delegate_.StartTaskRunner();
    250   const scoped_refptr<SequencedTaskRunner> task_runner =
    251       this->delegate_.GetTaskRunner();
    252 
    253   Time time_before_run = Time::Now();
    254   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    255       task_runner, Closure(), kDelay);
    256   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    257   this->delegate_.StopTaskRunner();
    258   Time time_after_run = Time::Now();
    259 
    260   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    261                                          kTaskCount));
    262   EXPECT_LE(kDelay, time_after_run - time_before_run);
    263 }
    264 
    265 // This test posts two tasks with the same delay, and checks that the tasks are
    266 // run in the order in which they were posted.
    267 //
    268 // NOTE: This is actually an approximate test since the API only takes a
    269 // "delay" parameter, so we are not exactly simulating two tasks that get
    270 // posted at the exact same time. It would be nice if the API allowed us to
    271 // specify the desired run time.
    272 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
    273   // TODO(akalin): Remove this check (http://crbug.com/149144).
    274   if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
    275     DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
    276         "non-zero delays; skipping";
    277     return;
    278   }
    279 
    280   const int kTaskCount = 2;
    281   const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
    282 
    283   this->delegate_.StartTaskRunner();
    284   const scoped_refptr<SequencedTaskRunner> task_runner =
    285       this->delegate_.GetTaskRunner();
    286 
    287   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    288       task_runner, Closure(), kDelay);
    289   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    290       task_runner, Closure(), kDelay);
    291   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    292   this->delegate_.StopTaskRunner();
    293 
    294   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    295                                          kTaskCount));
    296 }
    297 
    298 // This test posts a normal task and a delayed task, and checks that the
    299 // delayed task runs after the normal task even if the normal task takes
    300 // a long time to run.
    301 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
    302   // TODO(akalin): Remove this check (http://crbug.com/149144).
    303   if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
    304     DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
    305         "non-zero delays; skipping";
    306     return;
    307   }
    308 
    309   const int kTaskCount = 2;
    310 
    311   this->delegate_.StartTaskRunner();
    312   const scoped_refptr<SequencedTaskRunner> task_runner =
    313       this->delegate_.GetTaskRunner();
    314 
    315   this->task_tracker_->PostWrappedNonNestableTask(
    316       task_runner, base::Bind(&PlatformThread::Sleep,
    317                               TimeDelta::FromMilliseconds(50)));
    318   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    319       task_runner, Closure(), TimeDelta::FromMilliseconds(10));
    320   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    321   this->delegate_.StopTaskRunner();
    322 
    323   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    324                                          kTaskCount));
    325 }
    326 
    327 // Test that a pile of normal tasks and a delayed task run in the
    328 // time-to-run order.
    329 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
    330   // TODO(akalin): Remove this check (http://crbug.com/149144).
    331   if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
    332     DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
    333         "non-zero delays; skipping";
    334     return;
    335   }
    336 
    337   const int kTaskCount = 11;
    338 
    339   this->delegate_.StartTaskRunner();
    340   const scoped_refptr<SequencedTaskRunner> task_runner =
    341       this->delegate_.GetTaskRunner();
    342 
    343   for (int i = 0; i < kTaskCount - 1; i++) {
    344     this->task_tracker_->PostWrappedNonNestableTask(
    345         task_runner, base::Bind(&PlatformThread::Sleep,
    346                                 TimeDelta::FromMilliseconds(50)));
    347   }
    348   this->task_tracker_->PostWrappedDelayedNonNestableTask(
    349       task_runner, Closure(), TimeDelta::FromMilliseconds(10));
    350   this->task_tracker_->WaitForCompletedTasks(kTaskCount);
    351   this->delegate_.StopTaskRunner();
    352 
    353   EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
    354                                          kTaskCount));
    355 }
    356 
    357 
    358 // TODO(francoisk777 (at) gmail.com) Add a test, similiar to the above, which runs
    359 // some tasked nestedly (which should be implemented in the test
    360 // delegate). Also add, to the the test delegate, a predicate which checks
    361 // whether the implementation supports nested tasks.
    362 //
    363 
    364 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
    365                            SequentialNonNestable,
    366                            SequentialNestable,
    367                            SequentialDelayedNonNestable,
    368                            NonNestablePostFromNonNestableTask,
    369                            DelayedTaskBasic,
    370                            DelayedTasksSameDelay,
    371                            DelayedTaskAfterLongTask,
    372                            DelayedTaskAfterManyLongTasks);
    373 
    374 }  // namespace base
    375 
    376 #endif  // BASE_TASK_RUNNER_TEST_TEMPLATE_H_
    377