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
12 namespace cc {
13
14 namespace {
15
16 class FakeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
17 public:
FakeWorkerPoolTaskImpl(const base::Closure & callback,const base::Closure & reply)18 FakeWorkerPoolTaskImpl(const base::Closure& callback,
19 const base::Closure& reply)
20 : callback_(callback),
21 reply_(reply) {
22 }
23
24 // Overridden from internal::WorkerPoolTask:
RunOnWorkerThread(unsigned thread_index)25 virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
26 if (!callback_.is_null())
27 callback_.Run();
28 }
CompleteOnOriginThread()29 virtual void CompleteOnOriginThread() OVERRIDE {
30 if (!reply_.is_null())
31 reply_.Run();
32 }
33
34 private:
~FakeWorkerPoolTaskImpl()35 virtual ~FakeWorkerPoolTaskImpl() {}
36
37 const base::Closure callback_;
38 const base::Closure reply_;
39
40 DISALLOW_COPY_AND_ASSIGN(FakeWorkerPoolTaskImpl);
41 };
42
43 class FakeWorkerPool : public WorkerPool {
44 public:
45 struct Task {
Taskcc::__anonf843a01a0111::FakeWorkerPool::Task46 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 };
FakeWorkerPool()63 FakeWorkerPool() : WorkerPool(1, "test") {}
~FakeWorkerPool()64 virtual ~FakeWorkerPool() {}
65
Create()66 static scoped_ptr<FakeWorkerPool> Create() {
67 return make_scoped_ptr(new FakeWorkerPool);
68 }
69
ScheduleTasks(const std::vector<Task> & tasks)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
WaitForTasksToComplete()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
OnTasksCompleted()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
140 class WorkerPoolTest : public testing::Test {
141 public:
WorkerPoolTest()142 WorkerPoolTest() {}
~WorkerPoolTest()143 virtual ~WorkerPoolTest() {}
144
145 // Overridden from testing::Test:
SetUp()146 virtual void SetUp() OVERRIDE {
147 worker_pool_ = FakeWorkerPool::Create();
148 }
TearDown()149 virtual void TearDown() OVERRIDE {
150 worker_pool_->Shutdown();
151 worker_pool_->CheckForCompletedTasks();
152 }
153
ResetIds()154 void ResetIds() {
155 run_task_ids_.clear();
156 on_task_completed_ids_.clear();
157 }
158
RunAllTasks()159 void RunAllTasks() {
160 worker_pool_->WaitForTasksToComplete();
161 worker_pool_->CheckForCompletedTasks();
162 }
163
worker_pool()164 FakeWorkerPool* worker_pool() {
165 return worker_pool_.get();
166 }
167
RunTask(unsigned id)168 void RunTask(unsigned id) {
169 run_task_ids_.push_back(id);
170 }
171
OnTaskCompleted(unsigned id)172 void OnTaskCompleted(unsigned id) {
173 on_task_completed_ids_.push_back(id);
174 }
175
run_task_ids()176 const std::vector<unsigned>& run_task_ids() {
177 return run_task_ids_;
178 }
179
on_task_completed_ids()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
TEST_F(WorkerPoolTest,Basic)190 TEST_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
TEST_F(WorkerPoolTest,Dependencies)249 TEST_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
TEST_F(WorkerPoolTest,Priority)296 TEST_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