OLD | NEW |
(Empty) | |
| 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. Here's how you use it to test your |
| 7 // implementation. |
| 8 // |
| 9 // See task_runner_test_template.h for a description of how to use the |
| 10 // constructs in this file; these work the same. |
| 11 |
| 12 #ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| 13 #define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| 14 #pragma once |
| 15 |
| 16 #include <cstddef> |
| 17 |
| 18 #include <vector> |
| 19 |
| 20 #include "base/basictypes.h" |
| 21 #include "base/bind.h" |
| 22 #include "base/memory/ref_counted.h" |
| 23 #include "base/sequenced_task_runner.h" |
| 24 #include "base/synchronization/lock.h" |
| 25 #include "base/synchronization/waitable_event.h" |
| 26 #include "base/tracked_objects.h" |
| 27 #include "testing/gtest/include/gtest/gtest.h" |
| 28 |
| 29 namespace base { |
| 30 |
| 31 namespace internal { |
| 32 |
| 33 // Utility class used in the tests below. |
| 34 class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { |
| 35 public: |
| 36 // Keeps track of a task's run status. I.e. whether it has run to completion |
| 37 // and whether its predecessor had run to completion by the time it started |
| 38 // running. |
| 39 class TaskStatus { |
| 40 public: |
| 41 TaskStatus(); |
| 42 TaskStatus(const TaskStatus& ts); |
| 43 TaskStatus& operator=(const TaskStatus& ts); |
| 44 bool operator==(const TaskStatus& ts) const; |
| 45 |
| 46 // Claims ownership of this task status for a task. |
| 47 bool Claim(); |
| 48 // Whether the owning task has started running yet. |
| 49 bool Started() const; |
| 50 // Whether the owning task has run to completion yet. |
| 51 bool Completed() const; |
| 52 // Whether the previous task had started by the time this one did. |
| 53 bool PrevStartedBefore() const; |
| 54 // Whether the previous task (the one which owns the preceding position in |
| 55 // the task status vector) had finished by the time this one started. |
| 56 bool PrevCompletedBefore() const; |
| 57 // Declares that the owning task has started running. |
| 58 void SetStarted(); |
| 59 // Declares that the owning task has run to completion. |
| 60 void SetCompleted(); |
| 61 // Declares that the previous task had started by the time the owning task |
| 62 // started. |
| 63 void SetPrevStartedBefore(bool b); |
| 64 // Declares that the previous task had run to completion by the time the |
| 65 // owning task started. |
| 66 void SetPrevCompletedBefore(bool b); |
| 67 |
| 68 private: |
| 69 // An output operator is required to make a failed gtest assertion print |
| 70 // human-readable information. |
| 71 friend std::ostream& operator<<(std::ostream&, const TaskStatus&); |
| 72 |
| 73 mutable Lock lock_; |
| 74 // Used to prevent task_i from checking task_i-1.Started() before task_i-1 |
| 75 // has had a chance to signal that it has started. There would otherwise |
| 76 // be a race between the top of the task function and the line where it |
| 77 // calls SetStarted(). |
| 78 mutable WaitableEvent start_event_; |
| 79 // Whether this status slot has been claimed by a task yet. |
| 80 bool claimed_; |
| 81 bool started_; |
| 82 bool completed_; |
| 83 bool prev_started_; |
| 84 bool prev_completed_; |
| 85 }; |
| 86 |
| 87 typedef std::vector<TaskStatus> TaskStatuses; |
| 88 |
| 89 SequencedTaskTracker(); |
| 90 |
| 91 // Pre-allocates memory for the non-nestable task statuses. |
| 92 void SetNonNestableTaskCount(std::size_t task_count); |
| 93 |
| 94 // Pre-allocates memory for the nestable task statuses. |
| 95 void SetNestableTaskCount(std::size_t task_count); |
| 96 |
| 97 const TaskStatuses& GetNonNestableTaskStatuses() const; |
| 98 const TaskStatuses& GetNestableTaskStatuses() const; |
| 99 |
| 100 // A fast, non-nestable task. |
| 101 void FastNonNestableTask(int base_status_i); |
| 102 |
| 103 // A fast, nestable task. |
| 104 void FastNestableTask(int parent_task_status_slot); |
| 105 |
| 106 // A slow, non-nestable task (sleeps for 1 second). |
| 107 void SlowNonNestableTask(int base_status_i); |
| 108 |
| 109 // A slow, nestable task (sleeps for 1 second). |
| 110 void SlowNestableTask(int base_status_i); |
| 111 |
| 112 // Posts a fast, non-nestable task from a slow, non-nestable one. |
| 113 void PostFastNonNestableFromSlowNonNestable( |
| 114 scoped_refptr<SequencedTaskRunner> task_runner, |
| 115 const int base_status_i, |
| 116 const int child_count); |
| 117 |
| 118 // Posts a fast, nestable task from a slow, non-nestable one. |
| 119 void PostFastNestableFromSlowNonNestable( |
| 120 scoped_refptr<SequencedTaskRunner> task_runner, |
| 121 const int base_status_i, |
| 122 const int child_count); |
| 123 |
| 124 private: |
| 125 friend class RefCountedThreadSafe<SequencedTaskTracker>; |
| 126 |
| 127 ~SequencedTaskTracker(); |
| 128 |
| 129 // Finds the lowest-numbered non-nestable task status object, beginning at |
| 130 // position |search_from|, which has not yet been claimed. |
| 131 int ClaimNonNestableTaskStatus(const int search_from); |
| 132 |
| 133 // Finds the lowest-numbered nestable task status object, beginning at |
| 134 // position |search_from|, which has not yet been claimed. |
| 135 int ClaimNestableTaskStatus(const int search_from); |
| 136 |
| 137 TaskStatuses nestable_statuses_; |
| 138 TaskStatuses non_nestable_statuses_; |
| 139 |
| 140 DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); |
| 141 }; |
| 142 |
| 143 // An output operator is required to make a failed gtest assertion print |
| 144 // human-readable information. |
| 145 std::ostream& operator<<(std::ostream& os, |
| 146 const SequencedTaskTracker::TaskStatus& ts); |
| 147 |
| 148 } // namespace internal |
| 149 |
| 150 template <typename TaskRunnerTestDelegate> |
| 151 class SequencedTaskRunnerTest : public testing::Test { |
| 152 protected: |
| 153 SequencedTaskRunnerTest() |
| 154 : task_tracker_(new internal::SequencedTaskTracker()) { |
| 155 good_task_status_prototype_.SetStarted(); |
| 156 good_task_status_prototype_.SetPrevStartedBefore(true); |
| 157 good_task_status_prototype_.SetPrevCompletedBefore(true); |
| 158 good_task_status_prototype_.SetCompleted(); |
| 159 } |
| 160 |
| 161 const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; |
| 162 TaskRunnerTestDelegate delegate_; |
| 163 internal::SequencedTaskTracker::TaskStatus good_task_status_prototype_; |
| 164 }; |
| 165 |
| 166 TYPED_TEST_CASE_P(SequencedTaskRunnerTest); |
| 167 |
| 168 // This test posts N non-nestable tasks in sequence, and expects them to run |
| 169 // in FIFO order, with no part of any two tasks' execution overlapping. |
| 170 // |
| 171 // It checks that task0 was done by the time task1 started, and task2 only |
| 172 // started after task1 had executed to completion, etc. |
| 173 // |
| 174 // The first task it starts is a slow one which runs for a second, giving the |
| 175 // subsequent ones plenty of time to run if the implementation fails to |
| 176 // enforce the non-nestable property. |
| 177 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) { |
| 178 const int task_count = 1000; |
| 179 |
| 180 this->task_tracker_->SetNonNestableTaskCount(task_count); |
| 181 internal::SequencedTaskTracker::TaskStatuses expected_statuses( |
| 182 task_count, |
| 183 this->good_task_status_prototype_); |
| 184 |
| 185 this->delegate_.StartTaskRunner(); |
| 186 scoped_refptr<SequencedTaskRunner> task_runner = |
| 187 this->delegate_.GetTaskRunner(); |
| 188 |
| 189 for (int i = 0; i < task_count; ++i) { |
| 190 Closure task; |
| 191 if (i == 0) { |
| 192 task = Bind(&internal::SequencedTaskTracker::SlowNonNestableTask, |
| 193 this->task_tracker_, i); |
| 194 } else { |
| 195 task = Bind(&internal::SequencedTaskTracker::FastNonNestableTask, |
| 196 this->task_tracker_, i); |
| 197 } |
| 198 task_runner->PostNonNestableTask(FROM_HERE, task); |
| 199 } |
| 200 |
| 201 this->delegate_.StopTaskRunner(); |
| 202 |
| 203 EXPECT_EQ(expected_statuses, |
| 204 this->task_tracker_->GetNonNestableTaskStatuses()); |
| 205 } |
| 206 |
| 207 // This test posts N nestable tasks in sequence, and expects them to run in |
| 208 // FIFO order; overlapping execution is allowed. |
| 209 // |
| 210 // It checks only that task_i had started by the time task_i+1 started, for |
| 211 // increasing values of i. Thus it verifies the SequencedTaskRunner guarantee |
| 212 // that nested tasks are run in FIFO order. |
| 213 // |
| 214 // The first task it starts is a slow one which runs for a second, giving the |
| 215 // subsequent ones plenty of time to run in if the implementation fails to |
| 216 // enforce the FIFO property for nestable tasks. |
| 217 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) { |
| 218 const int task_count = 1000; |
| 219 |
| 220 this->task_tracker_->SetNestableTaskCount(task_count); |
| 221 internal::SequencedTaskTracker::TaskStatuses expected_statuses( |
| 222 task_count, |
| 223 this->good_task_status_prototype_); |
| 224 |
| 225 this->delegate_.StartTaskRunner(); |
| 226 scoped_refptr<SequencedTaskRunner> task_runner = |
| 227 this->delegate_.GetTaskRunner(); |
| 228 |
| 229 for (int i = 0; i < task_count; ++i) { |
| 230 Closure task; |
| 231 if (i == 0) { |
| 232 task = Bind(&internal::SequencedTaskTracker::SlowNestableTask, |
| 233 this->task_tracker_, i); |
| 234 } else { |
| 235 task = Bind(&internal::SequencedTaskTracker::FastNestableTask, |
| 236 this->task_tracker_, i); |
| 237 } |
| 238 task_runner->PostTask(FROM_HERE, task); |
| 239 } |
| 240 |
| 241 this->delegate_.StopTaskRunner(); |
| 242 |
| 243 EXPECT_EQ(expected_statuses, |
| 244 this->task_tracker_->GetNestableTaskStatuses()); |
| 245 } |
| 246 |
| 247 // This test posts non-nestable tasks in order of increasing delay, and checks |
| 248 // that that the tasks are run in FIFO order and that there is no execution |
| 249 // overlap whatsoever between any two tasks. |
| 250 // |
| 251 // If there are 10 tasks, task1 is posted first with a delay of zero, followed |
| 252 // by task2 with a delay of, say, 1, and so forth, until task10 is posted with |
| 253 // a delay of 10. It then checks that task2 started after task1, and only |
| 254 // after task1 had finished, and so forth. |
| 255 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { |
| 256 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { |
| 257 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " |
| 258 "non-zero delays; skipping"; |
| 259 return; |
| 260 } |
| 261 |
| 262 const int task_count = 20; |
| 263 const int delay_increment_ms = 50; |
| 264 |
| 265 this->task_tracker_->SetNonNestableTaskCount(task_count); |
| 266 internal::SequencedTaskTracker::TaskStatuses expected_statuses( |
| 267 task_count, |
| 268 this->good_task_status_prototype_); |
| 269 |
| 270 this->delegate_.StartTaskRunner(); |
| 271 scoped_refptr<SequencedTaskRunner> task_runner = |
| 272 this->delegate_.GetTaskRunner(); |
| 273 |
| 274 for (int i = 0; i < task_count; ++i) { |
| 275 Closure task = Bind(&internal::SequencedTaskTracker::FastNonNestableTask, |
| 276 this->task_tracker_, i); |
| 277 task_runner->PostNonNestableDelayedTask( |
| 278 FROM_HERE, |
| 279 task, |
| 280 TimeDelta::FromMilliseconds(delay_increment_ms * i)); |
| 281 } |
| 282 |
| 283 this->delegate_.StopTaskRunner(); |
| 284 |
| 285 EXPECT_EQ(expected_statuses, |
| 286 this->task_tracker_->GetNonNestableTaskStatuses()); |
| 287 } |
| 288 |
| 289 // This test posts a fast, non-nestable task from within each of a number of |
| 290 // slow, non-nestable tasks and checks that they all run in the sequence they |
| 291 // were posted in and that there is no execution overlap whatsoever. |
| 292 TYPED_TEST_P(SequencedTaskRunnerTest, |
| 293 NonNestablePostFromNonNestableTask) { |
| 294 const int parent_count = 1000; |
| 295 const int children_per_parent = 2; |
| 296 const int task_count = parent_count * (children_per_parent + 1); |
| 297 |
| 298 this->task_tracker_->SetNonNestableTaskCount(task_count); |
| 299 internal::SequencedTaskTracker::TaskStatuses expected_statuses( |
| 300 task_count, |
| 301 this->good_task_status_prototype_); |
| 302 |
| 303 this->delegate_.StartTaskRunner(); |
| 304 scoped_refptr<SequencedTaskRunner> task_runner = |
| 305 this->delegate_.GetTaskRunner(); |
| 306 |
| 307 const int end = parent_count; |
| 308 for (int i = 0; i < end; ++i) { |
| 309 Closure task = Bind( |
| 310 &internal::SequencedTaskTracker::PostFastNonNestableFromSlowNonNestable, |
| 311 this->task_tracker_, |
| 312 task_runner, |
| 313 i, |
| 314 children_per_parent); |
| 315 task_runner->PostNonNestableTask(FROM_HERE, task); |
| 316 } |
| 317 |
| 318 this->delegate_.StopTaskRunner(); |
| 319 |
| 320 EXPECT_EQ(expected_statuses, |
| 321 this->task_tracker_->GetNonNestableTaskStatuses()); |
| 322 } |
| 323 |
| 324 // This test posts a number of fast, nestable tasks from within each of a |
| 325 // number of slow, non-nestable tasks and checks that the non-nestable ones |
| 326 // all run in the sequence they were posted in and that there is no execution |
| 327 // overlap whatsoever. |
| 328 TYPED_TEST_P(SequencedTaskRunnerTest, |
| 329 NestablePostFromNonNestableTask) { |
| 330 const int parent_count = 500; |
| 331 const int children_per_parent = 2; |
| 332 |
| 333 this->task_tracker_->SetNonNestableTaskCount(parent_count); |
| 334 internal::SequencedTaskTracker::TaskStatuses expected_non_nestable_statuses( |
| 335 parent_count, |
| 336 this->good_task_status_prototype_); |
| 337 this->task_tracker_->SetNestableTaskCount(children_per_parent * parent_count); |
| 338 internal::SequencedTaskTracker::TaskStatuses expected_nestable_statuses( |
| 339 children_per_parent * parent_count, |
| 340 this->good_task_status_prototype_); |
| 341 |
| 342 this->delegate_.StartTaskRunner(); |
| 343 scoped_refptr<SequencedTaskRunner> task_runner = |
| 344 this->delegate_.GetTaskRunner(); |
| 345 |
| 346 const int end = parent_count; |
| 347 for (int i = 0; i < end; ++i) { |
| 348 Closure task = Bind( |
| 349 &internal::SequencedTaskTracker::PostFastNestableFromSlowNonNestable, |
| 350 this->task_tracker_, |
| 351 task_runner, |
| 352 i, |
| 353 children_per_parent); |
| 354 task_runner->PostNonNestableTask(FROM_HERE, task); |
| 355 } |
| 356 |
| 357 this->delegate_.StopTaskRunner(); |
| 358 |
| 359 EXPECT_EQ(expected_non_nestable_statuses, |
| 360 this->task_tracker_->GetNonNestableTaskStatuses()); |
| 361 EXPECT_EQ(expected_nestable_statuses, |
| 362 this->task_tracker_->GetNestableTaskStatuses()); |
| 363 } |
| 364 |
| 365 // TODO(francoisk777@gmail.com) Add a test, similiar to the above, which which |
| 366 // runs some tasked nestedly (which should be implemented in the test |
| 367 // delegate). Also add, to the the test delegate, a predicate which checks |
| 368 // whether the implementation supports nested tasks. |
| 369 |
| 370 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, |
| 371 SequentialNonNestable, |
| 372 SequentialNestable, |
| 373 SequentialDelayedNonNestable, |
| 374 NonNestablePostFromNonNestableTask, |
| 375 NestablePostFromNonNestableTask); |
| 376 |
| 377 } // namespace base |
| 378 |
| 379 #endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_ |
OLD | NEW |