Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / base / task_unittest.cc
1 /*
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"
30
31 namespace rtc {
32
33 static int64 GetCurrentTime() {
34   return static_cast<int64>(Time()) * 10000;
35 }
36
37 // feel free to change these numbers.  Note that '0' won't work, though
38 #define STUCK_TASK_COUNT 5
39 #define HAPPY_TASK_COUNT 20
40
41 // this is a generic timeout task which, when it signals timeout, will
42 // include the unique ID of the task in the signal (we don't use this
43 // in production code because we haven't yet had occasion to generate
44 // an array of the same types of task)
45
46 class IdTimeoutTask : public Task, public sigslot::has_slots<> {
47  public:
48   explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
49     SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
50   }
51
52   sigslot::signal1<const int> SignalTimeoutId;
53   sigslot::signal1<const int> SignalDoneId;
54
55   virtual int ProcessStart() {
56     return STATE_RESPONSE;
57   }
58
59   void OnLocalTimeout() {
60     SignalTimeoutId(unique_id());
61   }
62
63  protected:
64   virtual void Stop() {
65     SignalDoneId(unique_id());
66     Task::Stop();
67   }
68 };
69
70 class StuckTask : public IdTimeoutTask {
71  public:
72   explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
73   virtual int ProcessStart() {
74     return STATE_BLOCKED;
75   }
76 };
77
78 class HappyTask : public IdTimeoutTask {
79  public:
80   explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
81     time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
82   }
83   virtual int ProcessStart() {
84     if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
85       return STATE_RESPONSE;
86     else
87       return STATE_BLOCKED;
88   }
89
90  private:
91   int time_to_perform_;
92 };
93
94 // simple implementation of a task runner which uses Windows'
95 // GetSystemTimeAsFileTime() to get the current clock ticks
96
97 class MyTaskRunner : public TaskRunner {
98  public:
99   virtual void WakeTasks() { RunTasks(); }
100   virtual int64 CurrentTime() {
101     return GetCurrentTime();
102   }
103
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
129 class 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 "
272             << stuck_[i].task_->timeout_time() << std::endl;        
273       }
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
291 TEST(start_task_test, Timeout) {
292   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
299 class 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:
310   DISALLOW_EVIL_CONSTRUCTORS(AbortTask);
311 };
312
313 class 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_;
335   DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest);
336 };
337
338 TEST(start_task_test, Abort) {
339   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
346 class 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_;
365   DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask);
366 };
367
368 class 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_;
398   DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest);
399 };
400
401 TEST(start_task_test, AbortShouldWake) {
402   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
409 class TimeoutChangeTest : public sigslot::has_slots<> {
410  public:
411   TimeoutChangeTest()
412     : task_count_(ARRAY_SIZE(stuck_tasks_)) {}
413
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) {
467     for (int i = 0; i < ARRAY_SIZE(stuck_tasks_); ++i) {
468       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_;
479   DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest);
480 };
481
482 TEST(start_task_test, TimeoutChange) {
483   TimeoutChangeTest timeout_change_test;
484   timeout_change_test.Start();
485 }
486
487 class DeleteTestTaskRunner : public TaskRunner {
488  public:
489   DeleteTestTaskRunner() {
490   }
491   virtual void WakeTasks() { }
492   virtual int64 CurrentTime() {
493     return GetCurrentTime();
494   }
495  private:
496   DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner);
497 };
498
499 TEST(unstarted_task_test, DeleteTask) {
500   // This test ensures that we don't
501   // crash if a task is deleted without running it.
502   DeleteTestTaskRunner task_runner;
503   HappyTask* happy_task = new HappyTask(&task_runner);
504   happy_task->Start();
505
506   // try deleting the task directly
507   HappyTask* child_happy_task = new HappyTask(happy_task);
508   delete child_happy_task;
509
510   // run the unblocked tasks
511   task_runner.RunTasks();
512 }
513
514 TEST(unstarted_task_test, DoNotDeleteTask1) {
515   // This test ensures that we don't
516   // crash if a task runner is deleted without
517   // running a certain task.
518   DeleteTestTaskRunner task_runner;
519   HappyTask* happy_task = new HappyTask(&task_runner);
520   happy_task->Start();
521
522   HappyTask* child_happy_task = new HappyTask(happy_task);
523   child_happy_task->Start();
524
525   // Never run the tasks
526 }
527
528 TEST(unstarted_task_test, DoNotDeleteTask2) {
529   // This test ensures that we don't
530   // crash if a taskrunner is delete with a
531   // task that has never been started.
532   DeleteTestTaskRunner task_runner;
533   HappyTask* happy_task = new HappyTask(&task_runner);
534   happy_task->Start();
535
536   // Do not start the task.
537   // Note: this leaks memory, so don't do this.
538   // Instead, always run your tasks or delete them.
539   new HappyTask(happy_task);
540
541   // run the unblocked tasks
542   task_runner.RunTasks();
543 }
544
545 }  // namespace rtc