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