Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(504)

Side by Side Diff: third_party/libjingle_xmpp/task_runner/task_unittest.cc

Issue 2694903005: Add a copy of webrtc's TaskRunner abstraction, which is going to be deleted in webrtc. (Closed)
Patch Set: Delete dependencies on webrtc's rtc_task_runner. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "third_party/webrtc/base/win32.h"
21 #endif // WEBRTC_WIN
22
23 #include "third_party/libjingle_xmpp/task_runner/task.h"
24 #include "third_party/libjingle_xmpp/task_runner/taskrunner.h"
25 #include "third_party/webrtc/base/arraysize.h"
26 #include "third_party/webrtc/base/constructormagic.h"
27 #include "third_party/webrtc/base/gunit.h"
28 #include "third_party/webrtc/base/thread.h"
29 #include "third_party/webrtc/base/timeutils.h"
30 #include "third_party/webrtc_overrides/webrtc/base/logging.h"
31
32 namespace rtc {
33
34 static int64_t GetCurrentTime() {
35 return TimeMillis() * 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
47 class 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
71 class StuckTask : public IdTimeoutTask {
72 public:
73 explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
74 virtual int ProcessStart() {
75 return STATE_BLOCKED;
76 }
77 };
78
79 class 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
98 class MyTaskRunner : public TaskRunner {
99 public:
100 virtual void WakeTasks() { RunTasks(); }
101 virtual int64_t CurrentTime() { return GetCurrentTime(); }
102
103 bool timeout_change() const {
104 return timeout_change_;
105 }
106
107 void clear_timeout_change() {
108 timeout_change_ = false;
109 }
110 protected:
111 virtual void OnTimeoutChange() {
112 timeout_change_ = true;
113 }
114 bool timeout_change_;
115 };
116
117 //
118 // this unit test is primarily concerned (for now) with the timeout
119 // functionality in tasks. It works as follows:
120 //
121 // * Create a bunch of tasks, some "stuck" (ie., guaranteed to timeout)
122 // and some "happy" (will immediately finish).
123 // * Set the timeout on the "stuck" tasks to some number of seconds between
124 // 1 and the number of stuck tasks
125 // * Start all the stuck & happy tasks in random order
126 // * Wait "number of stuck tasks" seconds and make sure everything timed out
127
128 class TaskTest : public sigslot::has_slots<> {
129 public:
130 TaskTest() {}
131
132 // no need to delete any tasks; the task runner owns them
133 ~TaskTest() {}
134
135 void Start() {
136 // create and configure tasks
137 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
138 stuck_[i].task_ = new StuckTask(&task_runner_);
139 stuck_[i].task_->SignalTimeoutId.connect(this,
140 &TaskTest::OnTimeoutStuck);
141 stuck_[i].timed_out_ = false;
142 stuck_[i].xlat_ = stuck_[i].task_->unique_id();
143 stuck_[i].task_->set_timeout_seconds(i + 1);
144 LOG(LS_INFO) << "Task " << stuck_[i].xlat_ << " created with timeout "
145 << stuck_[i].task_->timeout_seconds();
146 }
147
148 for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
149 happy_[i].task_ = new HappyTask(&task_runner_);
150 happy_[i].task_->SignalTimeoutId.connect(this,
151 &TaskTest::OnTimeoutHappy);
152 happy_[i].task_->SignalDoneId.connect(this,
153 &TaskTest::OnDoneHappy);
154 happy_[i].timed_out_ = false;
155 happy_[i].xlat_ = happy_[i].task_->unique_id();
156 }
157
158 // start all the tasks in random order
159 int stuck_index = 0;
160 int happy_index = 0;
161 for (int i = 0; i < STUCK_TASK_COUNT + HAPPY_TASK_COUNT; ++i) {
162 if ((stuck_index < STUCK_TASK_COUNT) &&
163 (happy_index < HAPPY_TASK_COUNT)) {
164 if (rand() % 2 == 1) {
165 stuck_[stuck_index++].task_->Start();
166 } else {
167 happy_[happy_index++].task_->Start();
168 }
169 } else if (stuck_index < STUCK_TASK_COUNT) {
170 stuck_[stuck_index++].task_->Start();
171 } else {
172 happy_[happy_index++].task_->Start();
173 }
174 }
175
176 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
177 std::cout << "Stuck task #" << i << " timeout is " <<
178 stuck_[i].task_->timeout_seconds() << " at " <<
179 stuck_[i].task_->timeout_time() << std::endl;
180 }
181
182 // just a little self-check to make sure we started all the tasks
183 ASSERT_EQ(STUCK_TASK_COUNT, stuck_index);
184 ASSERT_EQ(HAPPY_TASK_COUNT, happy_index);
185
186 // run the unblocked tasks
187 LOG(LS_INFO) << "Running tasks";
188 task_runner_.RunTasks();
189
190 std::cout << "Start time is " << GetCurrentTime() << std::endl;
191
192 // give all the stuck tasks time to timeout
193 for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
194 ++i) {
195 Thread::Current()->ProcessMessages(1000);
196 for (int j = 0; j < HAPPY_TASK_COUNT; ++j) {
197 if (happy_[j].task_) {
198 happy_[j].task_->Wake();
199 }
200 }
201 LOG(LS_INFO) << "Polling tasks";
202 task_runner_.PollTasks();
203 }
204
205 // We see occasional test failures here due to the stuck tasks not having
206 // timed-out yet, which seems like it should be impossible. To help track
207 // this down we have added logging of the timing information, which we send
208 // directly to stdout so that we get it in opt builds too.
209 std::cout << "End time is " << GetCurrentTime() << std::endl;
210 }
211
212 void OnTimeoutStuck(const int id) {
213 LOG(LS_INFO) << "Timed out task " << id;
214
215 int i;
216 for (i = 0; i < STUCK_TASK_COUNT; ++i) {
217 if (stuck_[i].xlat_ == id) {
218 stuck_[i].timed_out_ = true;
219 stuck_[i].task_ = NULL;
220 break;
221 }
222 }
223
224 // getting a bad ID here is a failure, but let's continue
225 // running to see what else might go wrong
226 EXPECT_LT(i, STUCK_TASK_COUNT);
227 }
228
229 void OnTimeoutHappy(const int id) {
230 int i;
231 for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
232 if (happy_[i].xlat_ == id) {
233 happy_[i].timed_out_ = true;
234 happy_[i].task_ = NULL;
235 break;
236 }
237 }
238
239 // getting a bad ID here is a failure, but let's continue
240 // running to see what else might go wrong
241 EXPECT_LT(i, HAPPY_TASK_COUNT);
242 }
243
244 void OnDoneHappy(const int id) {
245 int i;
246 for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
247 if (happy_[i].xlat_ == id) {
248 happy_[i].task_ = NULL;
249 break;
250 }
251 }
252
253 // getting a bad ID here is a failure, but let's continue
254 // running to see what else might go wrong
255 EXPECT_LT(i, HAPPY_TASK_COUNT);
256 }
257
258 void check_passed() {
259 EXPECT_TRUE(task_runner_.AllChildrenDone());
260
261 // make sure none of our happy tasks timed out
262 for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
263 EXPECT_FALSE(happy_[i].timed_out_);
264 }
265
266 // make sure all of our stuck tasks timed out
267 for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
268 EXPECT_TRUE(stuck_[i].timed_out_);
269 if (!stuck_[i].timed_out_) {
270 std::cout << "Stuck task #" << i << " timeout is at "
271 << stuck_[i].task_->timeout_time() << std::endl;
272 }
273 }
274
275 std::cout.flush();
276 }
277
278 private:
279 struct TaskInfo {
280 IdTimeoutTask *task_;
281 bool timed_out_;
282 int xlat_;
283 };
284
285 MyTaskRunner task_runner_;
286 TaskInfo stuck_[STUCK_TASK_COUNT];
287 TaskInfo happy_[HAPPY_TASK_COUNT];
288 };
289
290 TEST(start_task_test, Timeout) {
291 TaskTest task_test;
292 task_test.Start();
293 task_test.check_passed();
294 }
295
296 // Test for aborting the task while it is running
297
298 class AbortTask : public Task {
299 public:
300 explicit AbortTask(TaskParent *parent) : Task(parent) {
301 set_timeout_seconds(1);
302 }
303
304 virtual int ProcessStart() {
305 Abort();
306 return STATE_NEXT;
307 }
308 private:
309 RTC_DISALLOW_COPY_AND_ASSIGN(AbortTask);
310 };
311
312 class TaskAbortTest : public sigslot::has_slots<> {
313 public:
314 TaskAbortTest() {}
315
316 // no need to delete any tasks; the task runner owns them
317 ~TaskAbortTest() {}
318
319 void Start() {
320 Task *abort_task = new AbortTask(&task_runner_);
321 abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
322 abort_task->Start();
323
324 // run the task
325 task_runner_.RunTasks();
326 }
327
328 private:
329 void OnTimeout() {
330 FAIL() << "Task timed out instead of aborting.";
331 }
332
333 MyTaskRunner task_runner_;
334 RTC_DISALLOW_COPY_AND_ASSIGN(TaskAbortTest);
335 };
336
337 TEST(start_task_test, Abort) {
338 TaskAbortTest abort_test;
339 abort_test.Start();
340 }
341
342 // Test for aborting a task to verify that it does the Wake operation
343 // which gets it deleted.
344
345 class SetBoolOnDeleteTask : public Task {
346 public:
347 SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted)
348 : Task(parent),
349 set_when_deleted_(set_when_deleted) {
350 EXPECT_TRUE(NULL != set_when_deleted);
351 EXPECT_FALSE(*set_when_deleted);
352 }
353
354 virtual ~SetBoolOnDeleteTask() {
355 *set_when_deleted_ = true;
356 }
357
358 virtual int ProcessStart() {
359 return STATE_BLOCKED;
360 }
361
362 private:
363 bool* set_when_deleted_;
364 RTC_DISALLOW_COPY_AND_ASSIGN(SetBoolOnDeleteTask);
365 };
366
367 class AbortShouldWakeTest : public sigslot::has_slots<> {
368 public:
369 AbortShouldWakeTest() {}
370
371 // no need to delete any tasks; the task runner owns them
372 ~AbortShouldWakeTest() {}
373
374 void Start() {
375 bool task_deleted = false;
376 Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted);
377 task_to_abort->Start();
378
379 // Task::Abort() should call TaskRunner::WakeTasks(). WakeTasks calls
380 // TaskRunner::RunTasks() immediately which should delete the task.
381 task_to_abort->Abort();
382 EXPECT_TRUE(task_deleted);
383
384 if (!task_deleted) {
385 // avoid a crash (due to referencing a local variable)
386 // if the test fails.
387 task_runner_.RunTasks();
388 }
389 }
390
391 private:
392 void OnTimeout() {
393 FAIL() << "Task timed out instead of aborting.";
394 }
395
396 MyTaskRunner task_runner_;
397 RTC_DISALLOW_COPY_AND_ASSIGN(AbortShouldWakeTest);
398 };
399
400 TEST(start_task_test, AbortShouldWake) {
401 AbortShouldWakeTest abort_should_wake_test;
402 abort_should_wake_test.Start();
403 }
404
405 // Validate that TaskRunner's OnTimeoutChange gets called appropriately
406 // * When a task calls UpdateTaskTimeout
407 // * When the next timeout task time, times out
408 class TimeoutChangeTest : public sigslot::has_slots<> {
409 public:
410 TimeoutChangeTest()
411 : task_count_(arraysize(stuck_tasks_)) {}
412
413 // no need to delete any tasks; the task runner owns them
414 ~TimeoutChangeTest() {}
415
416 void Start() {
417 for (int i = 0; i < task_count_; ++i) {
418 stuck_tasks_[i] = new StuckTask(&task_runner_);
419 stuck_tasks_[i]->set_timeout_seconds(i + 2);
420 stuck_tasks_[i]->SignalTimeoutId.connect(this,
421 &TimeoutChangeTest::OnTimeoutId);
422 }
423
424 for (int i = task_count_ - 1; i >= 0; --i) {
425 stuck_tasks_[i]->Start();
426 }
427 task_runner_.clear_timeout_change();
428
429 // At this point, our timeouts are set as follows
430 // task[0] is 2 seconds, task[1] at 3 seconds, etc.
431
432 stuck_tasks_[0]->set_timeout_seconds(2);
433 // Now, task[0] is 2 seconds, task[1] at 3 seconds...
434 // so timeout change shouldn't be called.
435 EXPECT_FALSE(task_runner_.timeout_change());
436 task_runner_.clear_timeout_change();
437
438 stuck_tasks_[0]->set_timeout_seconds(1);
439 // task[0] is 1 seconds, task[1] at 3 seconds...
440 // The smallest timeout got smaller so timeout change be called.
441 EXPECT_TRUE(task_runner_.timeout_change());
442 task_runner_.clear_timeout_change();
443
444 stuck_tasks_[1]->set_timeout_seconds(2);
445 // task[0] is 1 seconds, task[1] at 2 seconds...
446 // The smallest timeout is still 1 second so no timeout change.
447 EXPECT_FALSE(task_runner_.timeout_change());
448 task_runner_.clear_timeout_change();
449
450 while (task_count_ > 0) {
451 int previous_count = task_count_;
452 task_runner_.PollTasks();
453 if (previous_count != task_count_) {
454 // We only get here when a task times out. When that
455 // happens, the timeout change should get called because
456 // the smallest timeout is now in the past.
457 EXPECT_TRUE(task_runner_.timeout_change());
458 task_runner_.clear_timeout_change();
459 }
460 Thread::Current()->socketserver()->Wait(500, false);
461 }
462 }
463
464 private:
465 void OnTimeoutId(const int id) {
466 for (size_t i = 0; i < arraysize(stuck_tasks_); ++i) {
467 if (stuck_tasks_[i] && stuck_tasks_[i]->unique_id() == id) {
468 task_count_--;
469 stuck_tasks_[i] = NULL;
470 break;
471 }
472 }
473 }
474
475 MyTaskRunner task_runner_;
476 StuckTask* (stuck_tasks_[3]);
477 int task_count_;
478 RTC_DISALLOW_COPY_AND_ASSIGN(TimeoutChangeTest);
479 };
480
481 TEST(start_task_test, TimeoutChange) {
482 TimeoutChangeTest timeout_change_test;
483 timeout_change_test.Start();
484 }
485
486 class DeleteTestTaskRunner : public TaskRunner {
487 public:
488 DeleteTestTaskRunner() {
489 }
490 virtual void WakeTasks() { }
491 virtual int64_t CurrentTime() { return GetCurrentTime(); }
492 private:
493 RTC_DISALLOW_COPY_AND_ASSIGN(DeleteTestTaskRunner);
494 };
495
496 TEST(unstarted_task_test, DeleteTask) {
497 // This test ensures that we don't
498 // crash if a task is deleted without running it.
499 DeleteTestTaskRunner task_runner;
500 HappyTask* happy_task = new HappyTask(&task_runner);
501 happy_task->Start();
502
503 // try deleting the task directly
504 HappyTask* child_happy_task = new HappyTask(happy_task);
505 delete child_happy_task;
506
507 // run the unblocked tasks
508 task_runner.RunTasks();
509 }
510
511 TEST(unstarted_task_test, DoNotDeleteTask1) {
512 // This test ensures that we don't
513 // crash if a task runner is deleted without
514 // running a certain task.
515 DeleteTestTaskRunner task_runner;
516 HappyTask* happy_task = new HappyTask(&task_runner);
517 happy_task->Start();
518
519 HappyTask* child_happy_task = new HappyTask(happy_task);
520 child_happy_task->Start();
521
522 // Never run the tasks
523 }
524
525 TEST(unstarted_task_test, DoNotDeleteTask2) {
526 // This test ensures that we don't
527 // crash if a taskrunner is delete with a
528 // task that has never been started.
529 DeleteTestTaskRunner task_runner;
530 HappyTask* happy_task = new HappyTask(&task_runner);
531 happy_task->Start();
532
533 // Do not start the task.
534 // Note: this leaks memory, so don't do this.
535 // Instead, always run your tasks or delete them.
536 new HappyTask(happy_task);
537
538 // run the unblocked tasks
539 task_runner.RunTasks();
540 }
541
542 } // namespace rtc
OLDNEW
« no previous file with comments | « third_party/libjingle_xmpp/task_runner/task.cc ('k') | third_party/libjingle_xmpp/task_runner/taskparent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698