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 "cc/resources/worker_pool.h"
6
7#include <vector>
8
9#include "cc/base/completion_event.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace cc {
13
14namespace {
15
16class FakeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
17 public:
18  FakeWorkerPoolTaskImpl(const base::Closure& callback,
19                         const base::Closure& reply)
20      : callback_(callback),
21        reply_(reply) {
22  }
23
24  // Overridden from internal::WorkerPoolTask:
25  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
26    if (!callback_.is_null())
27      callback_.Run();
28  }
29  virtual void CompleteOnOriginThread() OVERRIDE {
30    if (!reply_.is_null())
31      reply_.Run();
32  }
33
34 private:
35  virtual ~FakeWorkerPoolTaskImpl() {}
36
37  const base::Closure callback_;
38  const base::Closure reply_;
39
40  DISALLOW_COPY_AND_ASSIGN(FakeWorkerPoolTaskImpl);
41};
42
43class FakeWorkerPool : public WorkerPool {
44 public:
45  struct Task {
46    Task(const base::Closure& callback,
47         const base::Closure& reply,
48         const base::Closure& dependent,
49         unsigned dependent_count,
50         unsigned priority) : callback(callback),
51                              reply(reply),
52                              dependent(dependent),
53                              dependent_count(dependent_count),
54                              priority(priority) {
55    }
56
57    base::Closure callback;
58    base::Closure reply;
59    base::Closure dependent;
60    unsigned dependent_count;
61    unsigned priority;
62  };
63  FakeWorkerPool() : WorkerPool(1, "test") {}
64  virtual ~FakeWorkerPool() {}
65
66  static scoped_ptr<FakeWorkerPool> Create() {
67    return make_scoped_ptr(new FakeWorkerPool);
68  }
69
70  void ScheduleTasks(const std::vector<Task>& tasks) {
71    TaskVector new_tasks;
72    TaskVector new_dependents;
73    TaskGraph new_graph;
74
75    scoped_refptr<FakeWorkerPoolTaskImpl> new_completion_task(
76        new FakeWorkerPoolTaskImpl(
77            base::Bind(&FakeWorkerPool::OnTasksCompleted,
78                       base::Unretained(this)),
79            base::Closure()));
80    scoped_ptr<internal::GraphNode> completion_node(
81        new internal::GraphNode(new_completion_task.get(), 0u));
82
83    for (std::vector<Task>::const_iterator it = tasks.begin();
84         it != tasks.end(); ++it) {
85      scoped_refptr<FakeWorkerPoolTaskImpl> new_task(
86          new FakeWorkerPoolTaskImpl(it->callback, it->reply));
87      scoped_ptr<internal::GraphNode> node(
88          new internal::GraphNode(new_task.get(), it->priority));
89
90      DCHECK(it->dependent_count);
91      for (unsigned i = 0; i < it->dependent_count; ++i) {
92        scoped_refptr<FakeWorkerPoolTaskImpl> new_dependent_task(
93            new FakeWorkerPoolTaskImpl(it->dependent, base::Closure()));
94        scoped_ptr<internal::GraphNode> dependent_node(
95            new internal::GraphNode(new_dependent_task.get(), it->priority));
96        dependent_node->add_dependent(completion_node.get());
97        completion_node->add_dependency();
98        node->add_dependent(dependent_node.get());
99        dependent_node->add_dependency();
100        new_graph.set(new_dependent_task.get(), dependent_node.Pass());
101        new_dependents.push_back(new_dependent_task.get());
102      }
103
104      new_graph.set(new_task.get(), node.Pass());
105      new_tasks.push_back(new_task.get());
106    }
107
108    new_graph.set(new_completion_task.get(), completion_node.Pass());
109
110    scheduled_tasks_completion_.reset(new CompletionEvent);
111
112    SetTaskGraph(&new_graph);
113
114    dependents_.swap(new_dependents);
115    completion_task_.swap(new_completion_task);
116    tasks_.swap(new_tasks);
117  }
118
119  void WaitForTasksToComplete() {
120    DCHECK(scheduled_tasks_completion_);
121    scheduled_tasks_completion_->Wait();
122  }
123
124 private:
125  typedef std::vector<scoped_refptr<internal::WorkerPoolTask> > TaskVector;
126
127  void OnTasksCompleted() {
128    DCHECK(scheduled_tasks_completion_);
129    scheduled_tasks_completion_->Signal();
130  }
131
132  TaskVector tasks_;
133  TaskVector dependents_;
134  scoped_refptr<FakeWorkerPoolTaskImpl> completion_task_;
135  scoped_ptr<CompletionEvent> scheduled_tasks_completion_;
136
137  DISALLOW_COPY_AND_ASSIGN(FakeWorkerPool);
138};
139
140class WorkerPoolTest : public testing::Test {
141 public:
142  WorkerPoolTest() {}
143  virtual ~WorkerPoolTest() {}
144
145  // Overridden from testing::Test:
146  virtual void SetUp() OVERRIDE {
147    worker_pool_ = FakeWorkerPool::Create();
148  }
149  virtual void TearDown() OVERRIDE {
150    worker_pool_->Shutdown();
151    worker_pool_->CheckForCompletedTasks();
152  }
153
154  void ResetIds() {
155    run_task_ids_.clear();
156    on_task_completed_ids_.clear();
157  }
158
159  void RunAllTasks() {
160    worker_pool_->WaitForTasksToComplete();
161    worker_pool_->CheckForCompletedTasks();
162  }
163
164  FakeWorkerPool* worker_pool() {
165    return worker_pool_.get();
166  }
167
168  void RunTask(unsigned id) {
169    run_task_ids_.push_back(id);
170  }
171
172  void OnTaskCompleted(unsigned id) {
173    on_task_completed_ids_.push_back(id);
174  }
175
176  const std::vector<unsigned>& run_task_ids() {
177    return run_task_ids_;
178  }
179
180  const std::vector<unsigned>& on_task_completed_ids() {
181    return on_task_completed_ids_;
182  }
183
184 private:
185  scoped_ptr<FakeWorkerPool> worker_pool_;
186  std::vector<unsigned> run_task_ids_;
187  std::vector<unsigned> on_task_completed_ids_;
188};
189
190TEST_F(WorkerPoolTest, Basic) {
191  EXPECT_EQ(0u, run_task_ids().size());
192  EXPECT_EQ(0u, on_task_completed_ids().size());
193
194  worker_pool()->ScheduleTasks(
195      std::vector<FakeWorkerPool::Task>(
196          1,
197          FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
198                                          base::Unretained(this),
199                                          0u),
200                               base::Bind(&WorkerPoolTest::OnTaskCompleted,
201                                          base::Unretained(this),
202                                          0u),
203                               base::Closure(),
204                               1u,
205                               0u)));
206  RunAllTasks();
207
208  EXPECT_EQ(1u, run_task_ids().size());
209  EXPECT_EQ(1u, on_task_completed_ids().size());
210
211  worker_pool()->ScheduleTasks(
212      std::vector<FakeWorkerPool::Task>(
213          1,
214          FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
215                                          base::Unretained(this),
216                                          0u),
217                               base::Bind(&WorkerPoolTest::OnTaskCompleted,
218                                          base::Unretained(this),
219                                          0u),
220                               base::Bind(&WorkerPoolTest::RunTask,
221                                          base::Unretained(this),
222                                          0u),
223                               1u,
224                               0u)));
225  RunAllTasks();
226
227  EXPECT_EQ(3u, run_task_ids().size());
228  EXPECT_EQ(2u, on_task_completed_ids().size());
229
230  worker_pool()->ScheduleTasks(
231      std::vector<FakeWorkerPool::Task>(
232          1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
233                                             base::Unretained(this),
234                                             0u),
235                                  base::Bind(&WorkerPoolTest::OnTaskCompleted,
236                                             base::Unretained(this),
237                                             0u),
238                                  base::Bind(&WorkerPoolTest::RunTask,
239                                             base::Unretained(this),
240                                             0u),
241                                  2u,
242                                  0u)));
243  RunAllTasks();
244
245  EXPECT_EQ(6u, run_task_ids().size());
246  EXPECT_EQ(3u, on_task_completed_ids().size());
247}
248
249TEST_F(WorkerPoolTest, Dependencies) {
250  worker_pool()->ScheduleTasks(
251      std::vector<FakeWorkerPool::Task>(
252          1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
253                                             base::Unretained(this),
254                                             0u),
255                                  base::Bind(&WorkerPoolTest::OnTaskCompleted,
256                                             base::Unretained(this),
257                                             0u),
258                                  base::Bind(&WorkerPoolTest::RunTask,
259                                             base::Unretained(this),
260                                             1u),
261                                  1u,
262                                  0u)));
263  RunAllTasks();
264
265  // Check if task ran before dependent.
266  ASSERT_EQ(2u, run_task_ids().size());
267  EXPECT_EQ(0u, run_task_ids()[0]);
268  EXPECT_EQ(1u, run_task_ids()[1]);
269  ASSERT_EQ(1u, on_task_completed_ids().size());
270  EXPECT_EQ(0u, on_task_completed_ids()[0]);
271
272  worker_pool()->ScheduleTasks(
273      std::vector<FakeWorkerPool::Task>(
274          1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
275                                             base::Unretained(this),
276                                             2u),
277                                  base::Bind(&WorkerPoolTest::OnTaskCompleted,
278                                             base::Unretained(this),
279                                             2u),
280                                  base::Bind(&WorkerPoolTest::RunTask,
281                                             base::Unretained(this),
282                                             3u),
283                                  2u,
284                                  0u)));
285  RunAllTasks();
286
287  // Task should only run once.
288  ASSERT_EQ(5u, run_task_ids().size());
289  EXPECT_EQ(2u, run_task_ids()[2]);
290  EXPECT_EQ(3u, run_task_ids()[3]);
291  EXPECT_EQ(3u, run_task_ids()[4]);
292  ASSERT_EQ(2u, on_task_completed_ids().size());
293  EXPECT_EQ(2u, on_task_completed_ids()[1]);
294}
295
296TEST_F(WorkerPoolTest, Priority) {
297  {
298    FakeWorkerPool::Task tasks[] = {
299        FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
300                                        base::Unretained(this),
301                                        0u),
302                             base::Bind(&WorkerPoolTest::OnTaskCompleted,
303                                        base::Unretained(this),
304                                        0u),
305                             base::Bind(&WorkerPoolTest::RunTask,
306                                        base::Unretained(this),
307                                        2u),
308                             1u,
309                             1u),  // Priority 1
310        FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
311                                        base::Unretained(this),
312                                        1u),
313                             base::Bind(&WorkerPoolTest::OnTaskCompleted,
314                                        base::Unretained(this),
315                                        1u),
316                             base::Bind(&WorkerPoolTest::RunTask,
317                                        base::Unretained(this),
318                                        3u),
319                             1u,
320                             0u)  // Priority 0
321    };
322    worker_pool()->ScheduleTasks(
323        std::vector<FakeWorkerPool::Task>(tasks, tasks + arraysize(tasks)));
324  }
325  RunAllTasks();
326
327  // Check if tasks ran in order of priority.
328  ASSERT_EQ(4u, run_task_ids().size());
329  EXPECT_EQ(1u, run_task_ids()[0]);
330  EXPECT_EQ(3u, run_task_ids()[1]);
331  EXPECT_EQ(0u, run_task_ids()[2]);
332  EXPECT_EQ(2u, run_task_ids()[3]);
333  ASSERT_EQ(2u, on_task_completed_ids().size());
334  EXPECT_EQ(1u, on_task_completed_ids()[0]);
335  EXPECT_EQ(0u, on_task_completed_ids()[1]);
336
337  ResetIds();
338  {
339    std::vector<FakeWorkerPool::Task> tasks;
340    tasks.push_back(
341        FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
342                                        base::Unretained(this),
343                                        0u),
344                             base::Bind(&WorkerPoolTest::OnTaskCompleted,
345                                        base::Unretained(this),
346                                        0u),
347                             base::Bind(&WorkerPoolTest::RunTask,
348                                        base::Unretained(this),
349                                        3u),
350                             1u,    // 1 dependent
351                             1u));  // Priority 1
352    tasks.push_back(
353        FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
354                                        base::Unretained(this),
355                                        1u),
356                             base::Bind(&WorkerPoolTest::OnTaskCompleted,
357                                        base::Unretained(this),
358                                        1u),
359                             base::Bind(&WorkerPoolTest::RunTask,
360                                        base::Unretained(this),
361                                        4u),
362                             2u,    // 2 dependents
363                             1u));  // Priority 1
364    tasks.push_back(
365        FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask,
366                                        base::Unretained(this),
367                                        2u),
368                             base::Bind(&WorkerPoolTest::OnTaskCompleted,
369                                        base::Unretained(this),
370                                        2u),
371                             base::Bind(&WorkerPoolTest::RunTask,
372                                        base::Unretained(this),
373                                        5u),
374                             1u,    // 1 dependent
375                             0u));  // Priority 0
376    worker_pool()->ScheduleTasks(tasks);
377  }
378  RunAllTasks();
379
380  // Check if tasks ran in order of priority and that task with more
381  // dependents ran first when priority is the same.
382  ASSERT_LE(3u, run_task_ids().size());
383  EXPECT_EQ(2u, run_task_ids()[0]);
384  EXPECT_EQ(5u, run_task_ids()[1]);
385  EXPECT_EQ(1u, run_task_ids()[2]);
386  ASSERT_EQ(3u, on_task_completed_ids().size());
387  EXPECT_EQ(2u, on_task_completed_ids()[0]);
388  EXPECT_EQ(1u, on_task_completed_ids()[1]);
389  EXPECT_EQ(0u, on_task_completed_ids()[2]);
390}
391
392}  // namespace
393
394}  // namespace cc
395