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 TaskRunner should
6// pass in order to be conformant.  Here's how you use it to test your
7// implementation.
8//
9// Say your class is called MyTaskRunner.  Then you need to define a
10// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc
11// like this:
12//
13//   class MyTaskRunnerTestDelegate {
14//    public:
15//     // Tasks posted to the task runner after this and before
16//     // StopTaskRunner() is called is called should run successfully.
17//     void StartTaskRunner() {
18//       ...
19//     }
20//
21//     // Should return the task runner implementation.  Only called
22//     // after StartTaskRunner and before StopTaskRunner.
23//     scoped_refptr<MyTaskRunner> GetTaskRunner() {
24//       ...
25//     }
26//
27//     // Stop the task runner and make sure all tasks posted before
28//     // this is called are run. Caveat: delayed tasks are not run,
29       // they're simply deleted.
30//     void StopTaskRunner() {
31//       ...
32//     }
33//   };
34//
35// The TaskRunnerTest test harness will have a member variable of
36// this delegate type and will call its functions in the various
37// tests.
38//
39// Then you simply #include this file as well as gtest.h and add the
40// following statement to my_task_runner_unittest.cc:
41//
42//   INSTANTIATE_TYPED_TEST_CASE_P(
43//       MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate);
44//
45// Easy!
46
47#ifndef BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
48#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
49
50#include <cstddef>
51#include <map>
52
53#include "base/basictypes.h"
54#include "base/bind.h"
55#include "base/callback.h"
56#include "base/memory/ref_counted.h"
57#include "base/synchronization/condition_variable.h"
58#include "base/synchronization/lock.h"
59#include "base/task_runner.h"
60#include "base/threading/thread.h"
61#include "base/tracked_objects.h"
62#include "testing/gtest/include/gtest/gtest.h"
63
64namespace base {
65
66namespace internal {
67
68// Utility class that keeps track of how many times particular tasks
69// are run.
70class TaskTracker : public RefCountedThreadSafe<TaskTracker> {
71 public:
72  TaskTracker();
73
74  // Returns a closure that runs the given task and increments the run
75  // count of |i| by one.  |task| may be null.  It is guaranteed that
76  // only one task wrapped by a given tracker will be run at a time.
77  Closure WrapTask(const Closure& task, int i);
78
79  std::map<int, int> GetTaskRunCounts() const;
80
81  // Returns after the tracker observes a total of |count| task completions.
82  void WaitForCompletedTasks(int count);
83
84 private:
85  friend class RefCountedThreadSafe<TaskTracker>;
86
87  ~TaskTracker();
88
89  void RunTask(const Closure& task, int i);
90
91  mutable Lock lock_;
92  std::map<int, int> task_run_counts_;
93  int task_runs_;
94  ConditionVariable task_runs_cv_;
95
96  DISALLOW_COPY_AND_ASSIGN(TaskTracker);
97};
98
99}  // namespace internal
100
101template <typename TaskRunnerTestDelegate>
102class TaskRunnerTest : public testing::Test {
103 protected:
104  TaskRunnerTest() : task_tracker_(new internal::TaskTracker()) {}
105
106  const scoped_refptr<internal::TaskTracker> task_tracker_;
107  TaskRunnerTestDelegate delegate_;
108};
109
110TYPED_TEST_CASE_P(TaskRunnerTest);
111
112// We can't really test much, since TaskRunner provides very few
113// guarantees.
114
115// Post a bunch of tasks to the task runner.  They should all
116// complete.
117TYPED_TEST_P(TaskRunnerTest, Basic) {
118  std::map<int, int> expected_task_run_counts;
119
120  this->delegate_.StartTaskRunner();
121  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
122  // Post each ith task i+1 times.
123  for (int i = 0; i < 20; ++i) {
124    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
125    for (int j = 0; j < i + 1; ++j) {
126      task_runner->PostTask(FROM_HERE, ith_task);
127      ++expected_task_run_counts[i];
128    }
129  }
130  this->delegate_.StopTaskRunner();
131
132  EXPECT_EQ(expected_task_run_counts,
133            this->task_tracker_->GetTaskRunCounts());
134}
135
136// Post a bunch of delayed tasks to the task runner.  They should all
137// complete.
138TYPED_TEST_P(TaskRunnerTest, Delayed) {
139  std::map<int, int> expected_task_run_counts;
140  int expected_total_tasks = 0;
141
142  this->delegate_.StartTaskRunner();
143  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
144  // Post each ith task i+1 times with delays from 0-i.
145  for (int i = 0; i < 20; ++i) {
146    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
147    for (int j = 0; j < i + 1; ++j) {
148      task_runner->PostDelayedTask(
149          FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j));
150      ++expected_task_run_counts[i];
151      ++expected_total_tasks;
152    }
153  }
154  this->task_tracker_->WaitForCompletedTasks(expected_total_tasks);
155  this->delegate_.StopTaskRunner();
156
157  EXPECT_EQ(expected_task_run_counts,
158            this->task_tracker_->GetTaskRunCounts());
159}
160
161namespace internal {
162
163// Calls RunsTasksOnCurrentThread() on |task_runner| and expects it to
164// equal |expected_value|.
165void ExpectRunsTasksOnCurrentThread(
166    bool expected_value,
167    const scoped_refptr<TaskRunner>& task_runner);
168
169}  // namespace internal
170
171// Post a bunch of tasks to the task runner as well as to a separate
172// thread, each checking the value of RunsTasksOnCurrentThread(),
173// which should return true for the tasks posted on the task runner
174// and false for the tasks posted on the separate thread.
175TYPED_TEST_P(TaskRunnerTest, RunsTasksOnCurrentThread) {
176  std::map<int, int> expected_task_run_counts;
177
178  Thread thread("Non-task-runner thread");
179  ASSERT_TRUE(thread.Start());
180  this->delegate_.StartTaskRunner();
181
182  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
183  // Post each ith task i+1 times on the task runner and i+1 times on
184  // the non-task-runner thread.
185  for (int i = 0; i < 20; ++i) {
186    const Closure& ith_task_runner_task =
187        this->task_tracker_->WrapTask(
188            Bind(&internal::ExpectRunsTasksOnCurrentThread,
189                 true, task_runner),
190            i);
191    const Closure& ith_non_task_runner_task =
192        this->task_tracker_->WrapTask(
193            Bind(&internal::ExpectRunsTasksOnCurrentThread,
194                 false, task_runner),
195            i);
196    for (int j = 0; j < i + 1; ++j) {
197      task_runner->PostTask(FROM_HERE, ith_task_runner_task);
198      thread.message_loop()->PostTask(FROM_HERE, ith_non_task_runner_task);
199      expected_task_run_counts[i] += 2;
200    }
201  }
202
203  this->delegate_.StopTaskRunner();
204  thread.Stop();
205
206  EXPECT_EQ(expected_task_run_counts,
207            this->task_tracker_->GetTaskRunCounts());
208}
209
210REGISTER_TYPED_TEST_CASE_P(
211    TaskRunnerTest, Basic, Delayed, RunsTasksOnCurrentThread);
212
213}  // namespace base
214
215#endif  //#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
216