• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 #include "content/browser/startup_task_runner.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/location.h"
11 #include "base/run_loop.h"
12 #include "base/task_runner.h"
13 
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace content {
18 namespace {
19 
20 using base::Closure;
21 using testing::_;
22 using testing::Assign;
23 using testing::Invoke;
24 using testing::WithArg;
25 
26 int observer_calls = 0;
27 int task_count = 0;
28 int observer_result;
29 base::Closure task;
30 
31 // I couldn't get gMock's SaveArg to compile, hence had to save the argument
32 // this way
SaveTaskArg(const Closure & arg)33 bool SaveTaskArg(const Closure& arg) {
34   task = arg;
35   return true;
36 }
37 
Observer(int result)38 void Observer(int result) {
39   observer_calls++;
40   observer_result = result;
41 }
42 
43 class StartupTaskRunnerTest : public testing::Test {
44  public:
45 
SetUp()46   virtual void SetUp() {
47     last_task_ = 0;
48     observer_calls = 0;
49     task_count = 0;
50   }
51 
Task1()52   int Task1() {
53     last_task_ = 1;
54     task_count++;
55     return 0;
56   }
57 
Task2()58   int Task2() {
59     last_task_ = 2;
60     task_count++;
61     return 0;
62   }
63 
FailingTask()64   int FailingTask() {
65     // Task returning failure
66     last_task_ = 3;
67     task_count++;
68     return 1;
69   }
70 
GetLastTask()71   int GetLastTask() { return last_task_; }
72 
73  private:
74 
75   int last_task_;
76 };
77 
78 // We can't use the real message loop, even if we want to, since doing so on
79 // Android requires a complex Java infrastructure. The test would have to built
80 // as a content_shell test; but content_shell startup invokes the class we are
81 // trying to test.
82 //
83 // The mocks are not directly in TaskRunnerProxy because reference counted
84 // objects seem to confuse the mocking framework
85 
86 class MockTaskRunner {
87  public:
88   MOCK_METHOD3(
89       PostDelayedTask,
90       bool(const tracked_objects::Location&, const Closure&, base::TimeDelta));
91   MOCK_METHOD3(
92       PostNonNestableDelayedTask,
93       bool(const tracked_objects::Location&, const Closure&, base::TimeDelta));
94 };
95 
96 class TaskRunnerProxy : public base::SingleThreadTaskRunner {
97  public:
TaskRunnerProxy(MockTaskRunner * mock)98   TaskRunnerProxy(MockTaskRunner* mock) : mock_(mock) {}
RunsTasksOnCurrentThread() const99   virtual bool RunsTasksOnCurrentThread() const OVERRIDE { return true; }
PostDelayedTask(const tracked_objects::Location & location,const Closure & closure,base::TimeDelta delta)100   virtual bool PostDelayedTask(const tracked_objects::Location& location,
101                                const Closure& closure,
102                                base::TimeDelta delta) OVERRIDE {
103     return mock_->PostDelayedTask(location, closure, delta);
104   }
PostNonNestableDelayedTask(const tracked_objects::Location & location,const Closure & closure,base::TimeDelta delta)105   virtual bool PostNonNestableDelayedTask(
106       const tracked_objects::Location& location,
107       const Closure& closure,
108       base::TimeDelta delta) OVERRIDE {
109     return mock_->PostNonNestableDelayedTask(location, closure, delta);
110   }
111 
112  private:
113   MockTaskRunner* mock_;
~TaskRunnerProxy()114   virtual ~TaskRunnerProxy() {}
115 };
116 
TEST_F(StartupTaskRunnerTest,SynchronousExecution)117 TEST_F(StartupTaskRunnerTest, SynchronousExecution) {
118   MockTaskRunner mock_runner;
119   scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
120 
121   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
122   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
123 
124   StartupTaskRunner runner(base::Bind(&Observer), proxy);
125 
126   StartupTask task1 =
127       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
128   runner.AddTask(task1);
129   EXPECT_EQ(GetLastTask(), 0);
130   StartupTask task2 =
131       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
132   runner.AddTask(task2);
133 
134   // Nothing should run until we tell them to.
135   EXPECT_EQ(GetLastTask(), 0);
136   runner.RunAllTasksNow();
137 
138   // On an immediate StartupTaskRunner the tasks should now all have run.
139   EXPECT_EQ(GetLastTask(), 2);
140 
141   EXPECT_EQ(task_count, 2);
142   EXPECT_EQ(observer_calls, 1);
143   EXPECT_EQ(observer_result, 0);
144 
145   // Running the tasks asynchronously shouldn't do anything
146   // In particular Post... should not be called
147   runner.StartRunningTasksAsync();
148 
149   // No more tasks should be run and the observer should not have been called
150   // again
151   EXPECT_EQ(task_count, 2);
152   EXPECT_EQ(observer_calls, 1);
153 }
154 
TEST_F(StartupTaskRunnerTest,NullObserver)155 TEST_F(StartupTaskRunnerTest, NullObserver) {
156   MockTaskRunner mock_runner;
157   scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
158 
159   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
160   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
161 
162   StartupTaskRunner runner(base::Callback<void(int)>(), proxy);
163 
164   StartupTask task1 =
165       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
166   runner.AddTask(task1);
167   EXPECT_EQ(GetLastTask(), 0);
168   StartupTask task2 =
169       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
170   runner.AddTask(task2);
171 
172   // Nothing should run until we tell them to.
173   EXPECT_EQ(GetLastTask(), 0);
174   runner.RunAllTasksNow();
175 
176   // On an immediate StartupTaskRunner the tasks should now all have run.
177   EXPECT_EQ(GetLastTask(), 2);
178   EXPECT_EQ(task_count, 2);
179 
180   // Running the tasks asynchronously shouldn't do anything
181   // In particular Post... should not be called
182   runner.StartRunningTasksAsync();
183 
184   // No more tasks should have been run
185   EXPECT_EQ(task_count, 2);
186 
187   EXPECT_EQ(observer_calls, 0);
188 }
189 
TEST_F(StartupTaskRunnerTest,SynchronousExecutionFailedTask)190 TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) {
191   MockTaskRunner mock_runner;
192   scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
193 
194   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
195   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
196 
197   StartupTaskRunner runner(base::Bind(&Observer), proxy);
198 
199   StartupTask task3 =
200       base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
201   runner.AddTask(task3);
202   EXPECT_EQ(GetLastTask(), 0);
203   StartupTask task2 =
204       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
205   runner.AddTask(task2);
206 
207   // Nothing should run until we tell them to.
208   EXPECT_EQ(GetLastTask(), 0);
209   runner.RunAllTasksNow();
210 
211   // Only the first task should have run, since it failed
212   EXPECT_EQ(GetLastTask(), 3);
213   EXPECT_EQ(task_count, 1);
214   EXPECT_EQ(observer_calls, 1);
215   EXPECT_EQ(observer_result, 1);
216 
217   // After a failed task all remaining tasks should be cancelled
218   // In particular Post... should not be called by running asynchronously
219   runner.StartRunningTasksAsync();
220 
221   // The observer should only be called the first time the queue completes and
222   // no more tasks should have run
223   EXPECT_EQ(observer_calls, 1);
224   EXPECT_EQ(task_count, 1);
225 }
226 
TEST_F(StartupTaskRunnerTest,AsynchronousExecution)227 TEST_F(StartupTaskRunnerTest, AsynchronousExecution) {
228 
229   MockTaskRunner mock_runner;
230   scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
231 
232   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
233   EXPECT_CALL(
234       mock_runner,
235       PostNonNestableDelayedTask(_, _, base::TimeDelta::FromMilliseconds(0)))
236       .Times(testing::Between(2, 3))
237       .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
238 
239   StartupTaskRunner runner(base::Bind(&Observer), proxy);
240 
241   StartupTask task1 =
242       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
243   runner.AddTask(task1);
244   StartupTask task2 =
245       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
246   runner.AddTask(task2);
247 
248   // Nothing should run until we tell them to.
249   EXPECT_EQ(GetLastTask(), 0);
250   runner.StartRunningTasksAsync();
251 
252   // No tasks should have run yet, since we the message loop hasn't run.
253   EXPECT_EQ(GetLastTask(), 0);
254 
255   // Fake the actual message loop. Each time a task is run a new task should
256   // be added to the queue, hence updating "task". The loop should actually run
257   // at most 3 times (once for each task plus possibly once for the observer),
258   // the "4" is a backstop.
259   for (int i = 0; i < 4 && observer_calls == 0; i++) {
260     task.Run();
261     EXPECT_EQ(i + 1, GetLastTask());
262   }
263   EXPECT_EQ(task_count, 2);
264   EXPECT_EQ(observer_calls, 1);
265   EXPECT_EQ(observer_result, 0);
266 
267   // Check that running synchronously now doesn't do anything
268 
269   runner.RunAllTasksNow();
270   EXPECT_EQ(task_count, 2);
271   EXPECT_EQ(observer_calls, 1);
272 }
273 
TEST_F(StartupTaskRunnerTest,AsynchronousExecutionFailedTask)274 TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) {
275 
276   MockTaskRunner mock_runner;
277   scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
278 
279   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
280   EXPECT_CALL(
281       mock_runner,
282       PostNonNestableDelayedTask(_, _, base::TimeDelta::FromMilliseconds(0)))
283       .Times(testing::Between(1, 2))
284       .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
285 
286   StartupTaskRunner runner(base::Bind(&Observer), proxy);
287 
288   StartupTask task3 =
289       base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
290   runner.AddTask(task3);
291   StartupTask task2 =
292       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
293   runner.AddTask(task2);
294 
295   // Nothing should run until we tell them to.
296   EXPECT_EQ(GetLastTask(), 0);
297   runner.StartRunningTasksAsync();
298 
299   // No tasks should have run yet, since we the message loop hasn't run.
300   EXPECT_EQ(GetLastTask(), 0);
301 
302   // Fake the actual message loop. Each time a task is run a new task should
303   // be added to the queue, hence updating "task". The loop should actually run
304   // at most twice (once for the failed task plus possibly once for the
305   // observer), the "4" is a backstop.
306   for (int i = 0; i < 4 && observer_calls == 0; i++) {
307     task.Run();
308   }
309   EXPECT_EQ(GetLastTask(), 3);
310   EXPECT_EQ(task_count, 1);
311 
312   EXPECT_EQ(observer_calls, 1);
313   EXPECT_EQ(observer_result, 1);
314 
315   // Check that running synchronously now doesn't do anything
316   runner.RunAllTasksNow();
317   EXPECT_EQ(observer_calls, 1);
318   EXPECT_EQ(task_count, 1);
319 }
320 }  // namespace
321 }  // namespace content
322