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 26 namespace base { 27 28 namespace internal { 29 30 struct 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. 38 class 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 103 void 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 119 template <typename TaskRunnerTestDelegate> 120 class 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 129 TYPED_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. 135 TYPED_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. 157 TYPED_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. 180 TYPED_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. 212 TYPED_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. 238 TYPED_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. 272 TYPED_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. 301 TYPED_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. 329 TYPED_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 (at) 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 364 REGISTER_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