blob: 5a3a60544ae3ba3348eb5658374609a028a6373d [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_POSIX)
12#include <sys/time.h>
13#endif // WEBRTC_POSIX
14
15// TODO: Remove this once the cause of sporadic failures in these
16// tests is tracked down.
17#include <iostream>
18
19#if defined(WEBRTC_WIN)
20#include "webrtc/base/win32.h"
21#endif // WEBRTC_WIN
22
23#include "webrtc/base/common.h"
24#include "webrtc/base/gunit.h"
25#include "webrtc/base/logging.h"
26#include "webrtc/base/task.h"
27#include "webrtc/base/taskrunner.h"
28#include "webrtc/base/thread.h"
29#include "webrtc/base/timeutils.h"
henrike@webrtc.orgfded02c2014-09-19 13:10:10 +000030#include "webrtc/test/testsupport/gtest_disable.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000031
32namespace rtc {
33
34static int64 GetCurrentTime() {
35 return static_cast<int64>(Time()) * 10000;
36}
37
38// feel free to change these numbers. Note that '0' won't work, though
39#define STUCK_TASK_COUNT 5
40#define HAPPY_TASK_COUNT 20
41
42// this is a generic timeout task which, when it signals timeout, will
43// include the unique ID of the task in the signal (we don't use this
44// in production code because we haven't yet had occasion to generate
45// an array of the same types of task)
46
47class IdTimeoutTask : public Task, public sigslot::has_slots<> {
48 public:
49 explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
50 SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
51 }
52
53 sigslot::signal1<const int> SignalTimeoutId;
54 sigslot::signal1<const int> SignalDoneId;
55
56 virtual int ProcessStart() {
57 return STATE_RESPONSE;
58 }
59
60 void OnLocalTimeout() {
61 SignalTimeoutId(unique_id());
62 }
63
64 protected:
65 virtual void Stop() {
66 SignalDoneId(unique_id());
67 Task::Stop();
68 }
69};
70
71class StuckTask : public IdTimeoutTask {
72 public:
73 explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
74 virtual int ProcessStart() {
75 return STATE_BLOCKED;
76 }
77};
78
79class HappyTask : public IdTimeoutTask {
80 public:
81 explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
82 time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
83 }
84 virtual int ProcessStart() {
85 if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
86 return STATE_RESPONSE;
87 else
88 return STATE_BLOCKED;
89 }
90
91 private:
92 int time_to_perform_;
93};
94
95// simple implementation of a task runner which uses Windows'
96// GetSystemTimeAsFileTime() to get the current clock ticks
97
98class MyTaskRunner : public TaskRunner {
99 public:
100 virtual void WakeTasks() { RunTasks(); }
101 virtual int64 CurrentTime() {
102 return GetCurrentTime();
103 }
104
105 bool timeout_change() const {
106 return timeout_change_;
107 }
108
109 void clear_timeout_change() {
110 timeout_change_ = false;
111 }
112 protected:
113 virtual void OnTimeoutChange() {
114 timeout_change_ = true;
115 }
116 bool timeout_change_;
117};
118
119//
120// this unit test is primarily concerned (for now) with the timeout
121// functionality in tasks. It works as follows:
122//
123// * Create a bunch of tasks, some "stuck" (ie., guaranteed to timeout)
124// and some "happy" (will immediately finish).
125// * Set the timeout on the "stuck" tasks to some number of seconds between
126// 1 and the number of stuck tasks
127// * Start all the stuck & happy tasks in random order
128// * Wait "number of stuck tasks" seconds and make sure everything timed out
129
130class TaskTest : public sigslot::has_slots<> {
131 public:
132 TaskTest() {}
133
134 // no need to delete any tasks; the task runner owns them
135 ~TaskTest() {}
136
137 void Start() {
138 // create and configure tasks
139 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
140 stuck_[i].task_ = new StuckTask(&task_runner_);
141 stuck_[i].task_->SignalTimeoutId.connect(this,
142 &TaskTest::OnTimeoutStuck);
143 stuck_[i].timed_out_ = false;
144 stuck_[i].xlat_ = stuck_[i].task_->unique_id();
145 stuck_[i].task_->set_timeout_seconds(i + 1);
146 LOG(LS_INFO) << "Task " << stuck_[i].xlat_ << " created with timeout "
147 << stuck_[i].task_->timeout_seconds();
148 }
149
150 for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
151 happy_[i].task_ = new HappyTask(&task_runner_);
152 happy_[i].task_->SignalTimeoutId.connect(this,
153 &TaskTest::OnTimeoutHappy);
154 happy_[i].task_->SignalDoneId.connect(this,
155 &TaskTest::OnDoneHappy);
156 happy_[i].timed_out_ = false;
157 happy_[i].xlat_ = happy_[i].task_->unique_id();
158 }
159
160 // start all the tasks in random order
161 int stuck_index = 0;
162 int happy_index = 0;
163 for (int i = 0; i < STUCK_TASK_COUNT + HAPPY_TASK_COUNT; ++i) {
164 if ((stuck_index < STUCK_TASK_COUNT) &&
165 (happy_index < HAPPY_TASK_COUNT)) {
166 if (rand() % 2 == 1) {
167 stuck_[stuck_index++].task_->Start();
168 } else {
169 happy_[happy_index++].task_->Start();
170 }
171 } else if (stuck_index < STUCK_TASK_COUNT) {
172 stuck_[stuck_index++].task_->Start();
173 } else {
174 happy_[happy_index++].task_->Start();
175 }
176 }
177
178 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
179 std::cout << "Stuck task #" << i << " timeout is " <<
180 stuck_[i].task_->timeout_seconds() << " at " <<
181 stuck_[i].task_->timeout_time() << std::endl;
182 }
183
184 // just a little self-check to make sure we started all the tasks
185 ASSERT_EQ(STUCK_TASK_COUNT, stuck_index);
186 ASSERT_EQ(HAPPY_TASK_COUNT, happy_index);
187
188 // run the unblocked tasks
189 LOG(LS_INFO) << "Running tasks";
190 task_runner_.RunTasks();
191
192 std::cout << "Start time is " << GetCurrentTime() << std::endl;
193
194 // give all the stuck tasks time to timeout
195 for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
196 ++i) {
197 Thread::Current()->ProcessMessages(1000);
198 for (int j = 0; j < HAPPY_TASK_COUNT; ++j) {
199 if (happy_[j].task_) {
200 happy_[j].task_->Wake();
201 }
202 }
203 LOG(LS_INFO) << "Polling tasks";
204 task_runner_.PollTasks();
205 }
206
207 // We see occasional test failures here due to the stuck tasks not having
208 // timed-out yet, which seems like it should be impossible. To help track
209 // this down we have added logging of the timing information, which we send
210 // directly to stdout so that we get it in opt builds too.
211 std::cout << "End time is " << GetCurrentTime() << std::endl;
212 }
213
214 void OnTimeoutStuck(const int id) {
215 LOG(LS_INFO) << "Timed out task " << id;
216
217 int i;
218 for (i = 0; i < STUCK_TASK_COUNT; ++i) {
219 if (stuck_[i].xlat_ == id) {
220 stuck_[i].timed_out_ = true;
221 stuck_[i].task_ = NULL;
222 break;
223 }
224 }
225
226 // getting a bad ID here is a failure, but let's continue
227 // running to see what else might go wrong
228 EXPECT_LT(i, STUCK_TASK_COUNT);
229 }
230
231 void OnTimeoutHappy(const int id) {
232 int i;
233 for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
234 if (happy_[i].xlat_ == id) {
235 happy_[i].timed_out_ = true;
236 happy_[i].task_ = NULL;
237 break;
238 }
239 }
240
241 // getting a bad ID here is a failure, but let's continue
242 // running to see what else might go wrong
243 EXPECT_LT(i, HAPPY_TASK_COUNT);
244 }
245
246 void OnDoneHappy(const int id) {
247 int i;
248 for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
249 if (happy_[i].xlat_ == id) {
250 happy_[i].task_ = NULL;
251 break;
252 }
253 }
254
255 // getting a bad ID here is a failure, but let's continue
256 // running to see what else might go wrong
257 EXPECT_LT(i, HAPPY_TASK_COUNT);
258 }
259
260 void check_passed() {
261 EXPECT_TRUE(task_runner_.AllChildrenDone());
262
263 // make sure none of our happy tasks timed out
264 for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
265 EXPECT_FALSE(happy_[i].timed_out_);
266 }
267
268 // make sure all of our stuck tasks timed out
269 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
270 EXPECT_TRUE(stuck_[i].timed_out_);
271 if (!stuck_[i].timed_out_) {
272 std::cout << "Stuck task #" << i << " timeout is at "
273 << stuck_[i].task_->timeout_time() << std::endl;
274 }
275 }
276
277 std::cout.flush();
278 }
279
280 private:
281 struct TaskInfo {
282 IdTimeoutTask *task_;
283 bool timed_out_;
284 int xlat_;
285 };
286
287 MyTaskRunner task_runner_;
288 TaskInfo stuck_[STUCK_TASK_COUNT];
289 TaskInfo happy_[HAPPY_TASK_COUNT];
290};
291
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000292TEST(start_task_test, Timeout) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000293 TaskTest task_test;
294 task_test.Start();
295 task_test.check_passed();
296}
297
298// Test for aborting the task while it is running
299
300class AbortTask : public Task {
301 public:
302 explicit AbortTask(TaskParent *parent) : Task(parent) {
303 set_timeout_seconds(1);
304 }
305
306 virtual int ProcessStart() {
307 Abort();
308 return STATE_NEXT;
309 }
310 private:
311 DISALLOW_EVIL_CONSTRUCTORS(AbortTask);
312};
313
314class TaskAbortTest : public sigslot::has_slots<> {
315 public:
316 TaskAbortTest() {}
317
318 // no need to delete any tasks; the task runner owns them
319 ~TaskAbortTest() {}
320
321 void Start() {
322 Task *abort_task = new AbortTask(&task_runner_);
323 abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
324 abort_task->Start();
325
326 // run the task
327 task_runner_.RunTasks();
328 }
329
330 private:
331 void OnTimeout() {
332 FAIL() << "Task timed out instead of aborting.";
333 }
334
335 MyTaskRunner task_runner_;
336 DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest);
337};
338
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000339TEST(start_task_test, Abort) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000340 TaskAbortTest abort_test;
341 abort_test.Start();
342}
343
344// Test for aborting a task to verify that it does the Wake operation
345// which gets it deleted.
346
347class SetBoolOnDeleteTask : public Task {
348 public:
349 SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted)
350 : Task(parent),
351 set_when_deleted_(set_when_deleted) {
352 EXPECT_TRUE(NULL != set_when_deleted);
353 EXPECT_FALSE(*set_when_deleted);
354 }
355
356 virtual ~SetBoolOnDeleteTask() {
357 *set_when_deleted_ = true;
358 }
359
360 virtual int ProcessStart() {
361 return STATE_BLOCKED;
362 }
363
364 private:
365 bool* set_when_deleted_;
366 DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask);
367};
368
369class AbortShouldWakeTest : public sigslot::has_slots<> {
370 public:
371 AbortShouldWakeTest() {}
372
373 // no need to delete any tasks; the task runner owns them
374 ~AbortShouldWakeTest() {}
375
376 void Start() {
377 bool task_deleted = false;
378 Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted);
379 task_to_abort->Start();
380
381 // Task::Abort() should call TaskRunner::WakeTasks(). WakeTasks calls
382 // TaskRunner::RunTasks() immediately which should delete the task.
383 task_to_abort->Abort();
384 EXPECT_TRUE(task_deleted);
385
386 if (!task_deleted) {
387 // avoid a crash (due to referencing a local variable)
388 // if the test fails.
389 task_runner_.RunTasks();
390 }
391 }
392
393 private:
394 void OnTimeout() {
395 FAIL() << "Task timed out instead of aborting.";
396 }
397
398 MyTaskRunner task_runner_;
399 DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest);
400};
401
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000402TEST(start_task_test, AbortShouldWake) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000403 AbortShouldWakeTest abort_should_wake_test;
404 abort_should_wake_test.Start();
405}
406
407// Validate that TaskRunner's OnTimeoutChange gets called appropriately
408// * When a task calls UpdateTaskTimeout
409// * When the next timeout task time, times out
410class TimeoutChangeTest : public sigslot::has_slots<> {
411 public:
412 TimeoutChangeTest()
413 : task_count_(ARRAY_SIZE(stuck_tasks_)) {}
414
415 // no need to delete any tasks; the task runner owns them
416 ~TimeoutChangeTest() {}
417
418 void Start() {
419 for (int i = 0; i < task_count_; ++i) {
420 stuck_tasks_[i] = new StuckTask(&task_runner_);
421 stuck_tasks_[i]->set_timeout_seconds(i + 2);
422 stuck_tasks_[i]->SignalTimeoutId.connect(this,
423 &TimeoutChangeTest::OnTimeoutId);
424 }
425
426 for (int i = task_count_ - 1; i >= 0; --i) {
427 stuck_tasks_[i]->Start();
428 }
429 task_runner_.clear_timeout_change();
430
431 // At this point, our timeouts are set as follows
432 // task[0] is 2 seconds, task[1] at 3 seconds, etc.
433
434 stuck_tasks_[0]->set_timeout_seconds(2);
435 // Now, task[0] is 2 seconds, task[1] at 3 seconds...
436 // so timeout change shouldn't be called.
437 EXPECT_FALSE(task_runner_.timeout_change());
438 task_runner_.clear_timeout_change();
439
440 stuck_tasks_[0]->set_timeout_seconds(1);
441 // task[0] is 1 seconds, task[1] at 3 seconds...
442 // The smallest timeout got smaller so timeout change be called.
443 EXPECT_TRUE(task_runner_.timeout_change());
444 task_runner_.clear_timeout_change();
445
446 stuck_tasks_[1]->set_timeout_seconds(2);
447 // task[0] is 1 seconds, task[1] at 2 seconds...
448 // The smallest timeout is still 1 second so no timeout change.
449 EXPECT_FALSE(task_runner_.timeout_change());
450 task_runner_.clear_timeout_change();
451
452 while (task_count_ > 0) {
453 int previous_count = task_count_;
454 task_runner_.PollTasks();
455 if (previous_count != task_count_) {
456 // We only get here when a task times out. When that
457 // happens, the timeout change should get called because
458 // the smallest timeout is now in the past.
459 EXPECT_TRUE(task_runner_.timeout_change());
460 task_runner_.clear_timeout_change();
461 }
462 Thread::Current()->socketserver()->Wait(500, false);
463 }
464 }
465
466 private:
467 void OnTimeoutId(const int id) {
468 for (int i = 0; i < ARRAY_SIZE(stuck_tasks_); ++i) {
469 if (stuck_tasks_[i] && stuck_tasks_[i]->unique_id() == id) {
470 task_count_--;
471 stuck_tasks_[i] = NULL;
472 break;
473 }
474 }
475 }
476
477 MyTaskRunner task_runner_;
478 StuckTask* (stuck_tasks_[3]);
479 int task_count_;
480 DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest);
481};
482
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000483TEST(start_task_test, TimeoutChange) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000484 TimeoutChangeTest timeout_change_test;
485 timeout_change_test.Start();
486}
487
488class DeleteTestTaskRunner : public TaskRunner {
489 public:
490 DeleteTestTaskRunner() {
491 }
492 virtual void WakeTasks() { }
493 virtual int64 CurrentTime() {
494 return GetCurrentTime();
495 }
496 private:
497 DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner);
498};
499
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000500TEST(unstarted_task_test, DeleteTask) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000501 // This test ensures that we don't
502 // crash if a task is deleted without running it.
503 DeleteTestTaskRunner task_runner;
504 HappyTask* happy_task = new HappyTask(&task_runner);
505 happy_task->Start();
506
507 // try deleting the task directly
508 HappyTask* child_happy_task = new HappyTask(happy_task);
509 delete child_happy_task;
510
511 // run the unblocked tasks
512 task_runner.RunTasks();
513}
514
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000515TEST(unstarted_task_test, DoNotDeleteTask1) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000516 // This test ensures that we don't
517 // crash if a task runner is deleted without
518 // running a certain task.
519 DeleteTestTaskRunner task_runner;
520 HappyTask* happy_task = new HappyTask(&task_runner);
521 happy_task->Start();
522
523 HappyTask* child_happy_task = new HappyTask(happy_task);
524 child_happy_task->Start();
525
526 // Never run the tasks
527}
528
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000529TEST(unstarted_task_test, DoNotDeleteTask2) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000530 // This test ensures that we don't
531 // crash if a taskrunner is delete with a
532 // task that has never been started.
533 DeleteTestTaskRunner task_runner;
534 HappyTask* happy_task = new HappyTask(&task_runner);
535 happy_task->Start();
536
537 // Do not start the task.
538 // Note: this leaks memory, so don't do this.
539 // Instead, always run your tasks or delete them.
540 new HappyTask(happy_task);
541
542 // run the unblocked tasks
543 task_runner.RunTasks();
544}
545
546} // namespace rtc