2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
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.
11 #if defined(WEBRTC_POSIX)
13 #endif // WEBRTC_POSIX
15 // TODO: Remove this once the cause of sporadic failures in these
16 // tests is tracked down.
19 #if defined(WEBRTC_WIN)
20 #include "webrtc/base/win32.h"
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"
30 #include "webrtc/test/testsupport/gtest_disable.h"
34 static int64 GetCurrentTime() {
35 return static_cast<int64>(Time()) * 10000;
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
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)
47 class IdTimeoutTask : public Task, public sigslot::has_slots<> {
49 explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
50 SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
53 sigslot::signal1<const int> SignalTimeoutId;
54 sigslot::signal1<const int> SignalDoneId;
56 virtual int ProcessStart() {
57 return STATE_RESPONSE;
60 void OnLocalTimeout() {
61 SignalTimeoutId(unique_id());
66 SignalDoneId(unique_id());
71 class StuckTask : public IdTimeoutTask {
73 explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
74 virtual int ProcessStart() {
79 class HappyTask : public IdTimeoutTask {
81 explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
82 time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
84 virtual int ProcessStart() {
85 if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
86 return STATE_RESPONSE;
95 // simple implementation of a task runner which uses Windows'
96 // GetSystemTimeAsFileTime() to get the current clock ticks
98 class MyTaskRunner : public TaskRunner {
100 virtual void WakeTasks() { RunTasks(); }
101 virtual int64 CurrentTime() {
102 return GetCurrentTime();
105 bool timeout_change() const {
106 return timeout_change_;
109 void clear_timeout_change() {
110 timeout_change_ = false;
113 virtual void OnTimeoutChange() {
114 timeout_change_ = true;
116 bool timeout_change_;
120 // this unit test is primarily concerned (for now) with the timeout
121 // functionality in tasks. It works as follows:
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
130 class TaskTest : public sigslot::has_slots<> {
134 // no need to delete any tasks; the task runner owns them
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();
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();
160 // start all the tasks in random order
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();
169 happy_[happy_index++].task_->Start();
171 } else if (stuck_index < STUCK_TASK_COUNT) {
172 stuck_[stuck_index++].task_->Start();
174 happy_[happy_index++].task_->Start();
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;
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);
188 // run the unblocked tasks
189 LOG(LS_INFO) << "Running tasks";
190 task_runner_.RunTasks();
192 std::cout << "Start time is " << GetCurrentTime() << std::endl;
194 // give all the stuck tasks time to timeout
195 for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
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();
203 LOG(LS_INFO) << "Polling tasks";
204 task_runner_.PollTasks();
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;
214 void OnTimeoutStuck(const int id) {
215 LOG(LS_INFO) << "Timed out task " << id;
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;
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);
231 void OnTimeoutHappy(const int id) {
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;
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);
246 void OnDoneHappy(const int id) {
248 for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
249 if (happy_[i].xlat_ == id) {
250 happy_[i].task_ = NULL;
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);
260 void check_passed() {
261 EXPECT_TRUE(task_runner_.AllChildrenDone());
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_);
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;
282 IdTimeoutTask *task_;
287 MyTaskRunner task_runner_;
288 TaskInfo stuck_[STUCK_TASK_COUNT];
289 TaskInfo happy_[HAPPY_TASK_COUNT];
292 TEST(start_task_test, Timeout) {
295 task_test.check_passed();
298 // Test for aborting the task while it is running
300 class AbortTask : public Task {
302 explicit AbortTask(TaskParent *parent) : Task(parent) {
303 set_timeout_seconds(1);
306 virtual int ProcessStart() {
311 DISALLOW_EVIL_CONSTRUCTORS(AbortTask);
314 class TaskAbortTest : public sigslot::has_slots<> {
318 // no need to delete any tasks; the task runner owns them
322 Task *abort_task = new AbortTask(&task_runner_);
323 abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
327 task_runner_.RunTasks();
332 FAIL() << "Task timed out instead of aborting.";
335 MyTaskRunner task_runner_;
336 DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest);
339 TEST(start_task_test, Abort) {
340 TaskAbortTest abort_test;
344 // Test for aborting a task to verify that it does the Wake operation
345 // which gets it deleted.
347 class SetBoolOnDeleteTask : public Task {
349 SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted)
351 set_when_deleted_(set_when_deleted) {
352 EXPECT_TRUE(NULL != set_when_deleted);
353 EXPECT_FALSE(*set_when_deleted);
356 virtual ~SetBoolOnDeleteTask() {
357 *set_when_deleted_ = true;
360 virtual int ProcessStart() {
361 return STATE_BLOCKED;
365 bool* set_when_deleted_;
366 DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask);
369 class AbortShouldWakeTest : public sigslot::has_slots<> {
371 AbortShouldWakeTest() {}
373 // no need to delete any tasks; the task runner owns them
374 ~AbortShouldWakeTest() {}
377 bool task_deleted = false;
378 Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted);
379 task_to_abort->Start();
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);
387 // avoid a crash (due to referencing a local variable)
388 // if the test fails.
389 task_runner_.RunTasks();
395 FAIL() << "Task timed out instead of aborting.";
398 MyTaskRunner task_runner_;
399 DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest);
402 TEST(start_task_test, AbortShouldWake) {
403 AbortShouldWakeTest abort_should_wake_test;
404 abort_should_wake_test.Start();
407 // Validate that TaskRunner's OnTimeoutChange gets called appropriately
408 // * When a task calls UpdateTaskTimeout
409 // * When the next timeout task time, times out
410 class TimeoutChangeTest : public sigslot::has_slots<> {
413 : task_count_(ARRAY_SIZE(stuck_tasks_)) {}
415 // no need to delete any tasks; the task runner owns them
416 ~TimeoutChangeTest() {}
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);
426 for (int i = task_count_ - 1; i >= 0; --i) {
427 stuck_tasks_[i]->Start();
429 task_runner_.clear_timeout_change();
431 // At this point, our timeouts are set as follows
432 // task[0] is 2 seconds, task[1] at 3 seconds, etc.
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();
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();
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();
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();
462 Thread::Current()->socketserver()->Wait(500, false);
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) {
471 stuck_tasks_[i] = NULL;
477 MyTaskRunner task_runner_;
478 StuckTask* (stuck_tasks_[3]);
480 DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest);
483 TEST(start_task_test, TimeoutChange) {
484 TimeoutChangeTest timeout_change_test;
485 timeout_change_test.Start();
488 class DeleteTestTaskRunner : public TaskRunner {
490 DeleteTestTaskRunner() {
492 virtual void WakeTasks() { }
493 virtual int64 CurrentTime() {
494 return GetCurrentTime();
497 DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner);
500 TEST(unstarted_task_test, DeleteTask) {
501 // 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);
507 // try deleting the task directly
508 HappyTask* child_happy_task = new HappyTask(happy_task);
509 delete child_happy_task;
511 // run the unblocked tasks
512 task_runner.RunTasks();
515 TEST(unstarted_task_test, DoNotDeleteTask1) {
516 // 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);
523 HappyTask* child_happy_task = new HappyTask(happy_task);
524 child_happy_task->Start();
526 // Never run the tasks
529 TEST(unstarted_task_test, DoNotDeleteTask2) {
530 // 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);
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);
542 // run the unblocked tasks
543 task_runner.RunTasks();