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