sequenced_task_runner_test_template.h revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This class defines tests that implementations of SequencedTaskRunner should 6// pass in order to be conformant. See task_runner_test_template.h for a 7// description of how to use the constructs in this file; these work the same. 8 9#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ 10#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ 11 12#include <cstddef> 13#include <iosfwd> 14#include <vector> 15 16#include "base/basictypes.h" 17#include "base/bind.h" 18#include "base/callback.h" 19#include "base/memory/ref_counted.h" 20#include "base/sequenced_task_runner.h" 21#include "base/synchronization/condition_variable.h" 22#include "base/synchronization/lock.h" 23#include "base/time/time.h" 24#include "testing/gtest/include/gtest/gtest.h" 25 26namespace base { 27 28namespace internal { 29 30struct TaskEvent { 31 enum Type { POST, START, END }; 32 TaskEvent(int i, Type type); 33 int i; 34 Type type; 35}; 36 37// Utility class used in the tests below. 38class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { 39 public: 40 SequencedTaskTracker(); 41 42 // Posts the non-nestable task |task|, and records its post event. 43 void PostWrappedNonNestableTask( 44 const scoped_refptr<SequencedTaskRunner>& task_runner, 45 const Closure& task); 46 47 // Posts the nestable task |task|, and records its post event. 48 void PostWrappedNestableTask( 49 const scoped_refptr<SequencedTaskRunner>& task_runner, 50 const Closure& task); 51 52 // Posts the delayed non-nestable task |task|, and records its post event. 53 void PostWrappedDelayedNonNestableTask( 54 const scoped_refptr<SequencedTaskRunner>& task_runner, 55 const Closure& task, 56 TimeDelta delay); 57 58 // Posts |task_count| non-nestable tasks. 59 void PostNonNestableTasks( 60 const scoped_refptr<SequencedTaskRunner>& task_runner, 61 int task_count); 62 63 const std::vector<TaskEvent>& GetTaskEvents() const; 64 65 // Returns after the tracker observes a total of |count| task completions. 66 void WaitForCompletedTasks(int count); 67 68 private: 69 friend class RefCountedThreadSafe<SequencedTaskTracker>; 70 71 ~SequencedTaskTracker(); 72 73 // A task which runs |task|, recording the start and end events. 74 void RunTask(const Closure& task, int task_i); 75 76 // Records a post event for task |i|. The owner is expected to be holding 77 // |lock_| (unlike |TaskStarted| and |TaskEnded|). 78 void TaskPosted(int i); 79 80 // Records a start event for task |i|. 81 void TaskStarted(int i); 82 83 // Records a end event for task |i|. 84 void TaskEnded(int i); 85 86 // Protects events_, next_post_i_, task_end_count_ and task_end_cv_. 87 Lock lock_; 88 89 // The events as they occurred for each task (protected by lock_). 90 std::vector<TaskEvent> events_; 91 92 // The ordinal to be used for the next task-posting task (protected by 93 // lock_). 94 int next_post_i_; 95 96 // The number of task end events we've received. 97 int task_end_count_; 98 ConditionVariable task_end_cv_; 99 100 DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); 101}; 102 103void PrintTo(const TaskEvent& event, std::ostream* os); 104 105// Checks the non-nestable task invariants for all tasks in |events|. 106// 107// The invariants are: 108// 1) Events started and ended in the same order that they were posted. 109// 2) Events for an individual tasks occur in the order {POST, START, END}, 110// and there is only one instance of each event type for a task. 111// 3) The only events between a task's START and END events are the POSTs of 112// other tasks. I.e. tasks were run sequentially, not interleaved. 113::testing::AssertionResult CheckNonNestableInvariants( 114 const std::vector<TaskEvent>& events, 115 int task_count); 116 117} // namespace internal 118 119template <typename TaskRunnerTestDelegate> 120class SequencedTaskRunnerTest : public testing::Test { 121 protected: 122 SequencedTaskRunnerTest() 123 : task_tracker_(new internal::SequencedTaskTracker()) {} 124 125 const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; 126 TaskRunnerTestDelegate delegate_; 127}; 128 129TYPED_TEST_CASE_P(SequencedTaskRunnerTest); 130 131// This test posts N non-nestable tasks in sequence, and expects them to run 132// in FIFO order, with no part of any two tasks' execution 133// overlapping. I.e. that each task starts only after the previously-posted 134// one has finished. 135TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) { 136 const int kTaskCount = 1000; 137 138 this->delegate_.StartTaskRunner(); 139 const scoped_refptr<SequencedTaskRunner> task_runner = 140 this->delegate_.GetTaskRunner(); 141 142 this->task_tracker_->PostWrappedNonNestableTask( 143 task_runner, Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1))); 144 for (int i = 1; i < kTaskCount; ++i) { 145 this->task_tracker_->PostWrappedNonNestableTask(task_runner, Closure()); 146 } 147 148 this->delegate_.StopTaskRunner(); 149 150 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 151 kTaskCount)); 152} 153 154// This test posts N nestable tasks in sequence. It has the same expectations 155// as SequentialNonNestable because even though the tasks are nestable, they 156// will not be run nestedly in this case. 157TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) { 158 const int kTaskCount = 1000; 159 160 this->delegate_.StartTaskRunner(); 161 const scoped_refptr<SequencedTaskRunner> task_runner = 162 this->delegate_.GetTaskRunner(); 163 164 this->task_tracker_->PostWrappedNestableTask( 165 task_runner, 166 Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1))); 167 for (int i = 1; i < kTaskCount; ++i) { 168 this->task_tracker_->PostWrappedNestableTask(task_runner, Closure()); 169 } 170 171 this->delegate_.StopTaskRunner(); 172 173 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 174 kTaskCount)); 175} 176 177// This test posts non-nestable tasks in order of increasing delay, and checks 178// that that the tasks are run in FIFO order and that there is no execution 179// overlap whatsoever between any two tasks. 180TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { 181 // TODO(akalin): Remove this check (http://crbug.com/149144). 182 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { 183 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " 184 "non-zero delays; skipping"; 185 return; 186 } 187 188 const int kTaskCount = 20; 189 const int kDelayIncrementMs = 50; 190 191 this->delegate_.StartTaskRunner(); 192 const scoped_refptr<SequencedTaskRunner> task_runner = 193 this->delegate_.GetTaskRunner(); 194 195 for (int i = 0; i < kTaskCount; ++i) { 196 this->task_tracker_->PostWrappedDelayedNonNestableTask( 197 task_runner, 198 Closure(), 199 TimeDelta::FromMilliseconds(kDelayIncrementMs * i)); 200 } 201 202 this->task_tracker_->WaitForCompletedTasks(kTaskCount); 203 this->delegate_.StopTaskRunner(); 204 205 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 206 kTaskCount)); 207} 208 209// This test posts a fast, non-nestable task from within each of a number of 210// slow, non-nestable tasks and checks that they all run in the sequence they 211// were posted in and that there is no execution overlap whatsoever. 212TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) { 213 const int kParentCount = 10; 214 const int kChildrenPerParent = 10; 215 216 this->delegate_.StartTaskRunner(); 217 const scoped_refptr<SequencedTaskRunner> task_runner = 218 this->delegate_.GetTaskRunner(); 219 220 for (int i = 0; i < kParentCount; ++i) { 221 Closure task = Bind( 222 &internal::SequencedTaskTracker::PostNonNestableTasks, 223 this->task_tracker_, 224 task_runner, 225 kChildrenPerParent); 226 this->task_tracker_->PostWrappedNonNestableTask(task_runner, task); 227 } 228 229 this->delegate_.StopTaskRunner(); 230 231 EXPECT_TRUE(CheckNonNestableInvariants( 232 this->task_tracker_->GetTaskEvents(), 233 kParentCount * (kChildrenPerParent + 1))); 234} 235 236// This test posts a delayed task, and checks that the task is run later than 237// the specified time. 238TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) { 239 // TODO(akalin): Remove this check (http://crbug.com/149144). 240 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { 241 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " 242 "non-zero delays; skipping"; 243 return; 244 } 245 246 const int kTaskCount = 1; 247 const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); 248 249 this->delegate_.StartTaskRunner(); 250 const scoped_refptr<SequencedTaskRunner> task_runner = 251 this->delegate_.GetTaskRunner(); 252 253 Time time_before_run = Time::Now(); 254 this->task_tracker_->PostWrappedDelayedNonNestableTask( 255 task_runner, Closure(), kDelay); 256 this->task_tracker_->WaitForCompletedTasks(kTaskCount); 257 this->delegate_.StopTaskRunner(); 258 Time time_after_run = Time::Now(); 259 260 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 261 kTaskCount)); 262 EXPECT_LE(kDelay, time_after_run - time_before_run); 263} 264 265// This test posts two tasks with the same delay, and checks that the tasks are 266// run in the order in which they were posted. 267// 268// NOTE: This is actually an approximate test since the API only takes a 269// "delay" parameter, so we are not exactly simulating two tasks that get 270// posted at the exact same time. It would be nice if the API allowed us to 271// specify the desired run time. 272TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) { 273 // TODO(akalin): Remove this check (http://crbug.com/149144). 274 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { 275 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " 276 "non-zero delays; skipping"; 277 return; 278 } 279 280 const int kTaskCount = 2; 281 const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); 282 283 this->delegate_.StartTaskRunner(); 284 const scoped_refptr<SequencedTaskRunner> task_runner = 285 this->delegate_.GetTaskRunner(); 286 287 this->task_tracker_->PostWrappedDelayedNonNestableTask( 288 task_runner, Closure(), kDelay); 289 this->task_tracker_->PostWrappedDelayedNonNestableTask( 290 task_runner, Closure(), kDelay); 291 this->task_tracker_->WaitForCompletedTasks(kTaskCount); 292 this->delegate_.StopTaskRunner(); 293 294 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 295 kTaskCount)); 296} 297 298// This test posts a normal task and a delayed task, and checks that the 299// delayed task runs after the normal task even if the normal task takes 300// a long time to run. 301TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) { 302 // TODO(akalin): Remove this check (http://crbug.com/149144). 303 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { 304 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " 305 "non-zero delays; skipping"; 306 return; 307 } 308 309 const int kTaskCount = 2; 310 311 this->delegate_.StartTaskRunner(); 312 const scoped_refptr<SequencedTaskRunner> task_runner = 313 this->delegate_.GetTaskRunner(); 314 315 this->task_tracker_->PostWrappedNonNestableTask( 316 task_runner, base::Bind(&PlatformThread::Sleep, 317 TimeDelta::FromMilliseconds(50))); 318 this->task_tracker_->PostWrappedDelayedNonNestableTask( 319 task_runner, Closure(), TimeDelta::FromMilliseconds(10)); 320 this->task_tracker_->WaitForCompletedTasks(kTaskCount); 321 this->delegate_.StopTaskRunner(); 322 323 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 324 kTaskCount)); 325} 326 327// Test that a pile of normal tasks and a delayed task run in the 328// time-to-run order. 329TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) { 330 // TODO(akalin): Remove this check (http://crbug.com/149144). 331 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { 332 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " 333 "non-zero delays; skipping"; 334 return; 335 } 336 337 const int kTaskCount = 11; 338 339 this->delegate_.StartTaskRunner(); 340 const scoped_refptr<SequencedTaskRunner> task_runner = 341 this->delegate_.GetTaskRunner(); 342 343 for (int i = 0; i < kTaskCount - 1; i++) { 344 this->task_tracker_->PostWrappedNonNestableTask( 345 task_runner, base::Bind(&PlatformThread::Sleep, 346 TimeDelta::FromMilliseconds(50))); 347 } 348 this->task_tracker_->PostWrappedDelayedNonNestableTask( 349 task_runner, Closure(), TimeDelta::FromMilliseconds(10)); 350 this->task_tracker_->WaitForCompletedTasks(kTaskCount); 351 this->delegate_.StopTaskRunner(); 352 353 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), 354 kTaskCount)); 355} 356 357 358// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs 359// some tasked nestedly (which should be implemented in the test 360// delegate). Also add, to the the test delegate, a predicate which checks 361// whether the implementation supports nested tasks. 362// 363 364REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, 365 SequentialNonNestable, 366 SequentialNestable, 367 SequentialDelayedNonNestable, 368 NonNestablePostFromNonNestableTask, 369 DelayedTaskBasic, 370 DelayedTasksSameDelay, 371 DelayedTaskAfterLongTask, 372 DelayedTaskAfterManyLongTasks); 373 374} // namespace base 375 376#endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_ 377