Chromium Code Reviews
|
| 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 | |
|
akalin
2012/03/20 22:16:08
Remove 2nd sentence, merge paragraph below with th
Francois
2012/03/26 09:33:21
Done.
| |
| 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 | |
|
akalin
2012/03/20 22:16:08
remove extra newline
Francois
2012/03/26 09:33:21
Done.
| |
| 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/tracked_objects.h" | |
| 26 #include "testing/gtest/include/gtest/gtest.h" | |
| 27 | |
| 28 namespace base { | |
| 29 | |
| 30 namespace internal { | |
| 31 | |
| 32 // Utility class used in the tests below. | |
| 33 class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { | |
| 34 public: | |
| 35 struct TaskPhase { | |
|
akalin
2012/03/20 22:16:08
I think TaskEvent is a better name
Francois
2012/03/26 09:33:21
Done.
| |
| 36 enum Type { POST, START, END }; | |
| 37 TaskPhase(int i, Type type) : i_(i), type_(type) {} | |
| 38 bool operator==(const TaskPhase& phase) { | |
|
akalin
2012/03/20 22:16:08
prefer
bool Equals(const TaskPhase& phase) const
Francois
2012/03/26 09:33:21
Will do next time, but it isn't required anymore,
| |
| 39 return (i_ == phase.i_ && type_ == phase.type_); | |
| 40 } | |
| 41 int i_; | |
|
akalin
2012/03/20 22:16:08
typically public member variables don't have an ap
Francois
2012/03/26 09:33:21
Done.
| |
| 42 Type type_; | |
| 43 }; | |
| 44 | |
| 45 SequencedTaskTracker(); | |
| 46 | |
| 47 int GetNextPostOrdinal(); | |
|
akalin
2012/03/20 22:16:08
shouldn't need this function (see comments below)
Francois
2012/03/26 09:33:21
Done.
| |
| 48 | |
| 49 void TaskPosted(int i); | |
|
akalin
2012/03/20 22:16:08
these functions shouldn't be exposed. Instead, yo
Francois
2012/03/26 09:33:21
Done. And DoNothing isn't required if null tasks a
| |
| 50 void TaskStarted(int i); | |
| 51 void TaskEnded(int i); | |
| 52 | |
| 53 const std::vector<TaskPhase>& GetTaskPhases() const; | |
| 54 | |
| 55 void FastTask(int i); | |
| 56 void SlowTask(int i); | |
| 57 | |
| 58 // Task which posts a fast, non-nestable task. | |
| 59 void PostFastNonNestableFromSlowNonNestable( | |
| 60 scoped_refptr<SequencedTaskRunner> task_runner, | |
| 61 const int base_status_i, | |
| 62 const int child_count); | |
| 63 | |
| 64 private: | |
| 65 friend class RefCountedThreadSafe<SequencedTaskTracker>; | |
| 66 | |
| 67 ~SequencedTaskTracker(); | |
| 68 | |
| 69 public: | |
|
akalin
2012/03/20 22:16:08
these shouldn't be public
Francois
2012/03/26 09:33:21
Done.
| |
| 70 std::vector<TaskPhase> phases_; | |
| 71 mutable Lock phase_lock_; | |
|
akalin
2012/03/20 22:16:08
add a comment saying that phase_lock_ protects pha
Francois
2012/03/26 09:33:21
Done.
| |
| 72 | |
| 73 private: | |
| 74 int next_post_i_; | |
| 75 | |
| 76 DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); | |
| 77 }; | |
| 78 | |
| 79 } // namespace internal | |
| 80 | |
| 81 template <typename TaskRunnerTestDelegate> | |
| 82 class SequencedTaskRunnerTest : public testing::Test { | |
| 83 protected: | |
| 84 SequencedTaskRunnerTest() | |
| 85 : task_tracker_(new internal::SequencedTaskTracker()) {} | |
| 86 | |
| 87 // Checks that each phase type occurs in the same order as the tasks were | |
| 88 // posted, and that the phases for each given task occur in the expected | |
| 89 // order (POST, START, END). | |
| 90 ::testing::AssertionResult TaskPhasesInOrder(int task_count); | |
| 91 | |
| 92 const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; | |
| 93 TaskRunnerTestDelegate delegate_; | |
| 94 }; | |
| 95 | |
| 96 TYPED_TEST_CASE_P(SequencedTaskRunnerTest); | |
| 97 | |
| 98 // This test posts N non-nestable tasks in sequence, and expects them to run | |
| 99 // in FIFO order, with no part of any two tasks' execution | |
| 100 // overlapping. I.e. that each task starts only after the previously-posted | |
| 101 // one has finished. | |
| 102 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) { | |
| 103 const int task_count = 1000; | |
| 104 | |
| 105 this->delegate_.StartTaskRunner(); | |
| 106 scoped_refptr<SequencedTaskRunner> task_runner = | |
| 107 this->delegate_.GetTaskRunner(); | |
| 108 | |
| 109 for (int i = 0; i < task_count; ++i) { | |
| 110 Closure task; | |
| 111 if (i == 0) { | |
|
akalin
2012/03/20 22:16:08
with the comments above, you can move the i=0 case
Francois
2012/03/26 09:33:21
Done.
| |
| 112 task = Bind(&internal::SequencedTaskTracker::SlowTask, | |
| 113 this->task_tracker_, i); | |
| 114 } else { | |
| 115 task = Bind(&internal::SequencedTaskTracker::FastTask, | |
| 116 this->task_tracker_, i); | |
| 117 } | |
| 118 this->task_tracker_->TaskPosted(i); | |
| 119 task_runner->PostNonNestableTask(FROM_HERE, task); | |
| 120 } | |
| 121 | |
| 122 this->delegate_.StopTaskRunner(); | |
| 123 | |
| 124 EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); | |
| 125 } | |
| 126 | |
| 127 // This test posts N nestable tasks in sequence. It has the same expectations | |
| 128 // as SequentialNonNestable because even though the tasks are nestable, they | |
| 129 // will not be run nestedly in this case. | |
| 130 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) { | |
| 131 const int task_count = 1000; | |
| 132 | |
| 133 this->delegate_.StartTaskRunner(); | |
| 134 scoped_refptr<SequencedTaskRunner> task_runner = | |
| 135 this->delegate_.GetTaskRunner(); | |
| 136 | |
| 137 for (int i = 0; i < task_count; ++i) { | |
| 138 Closure task; | |
| 139 if (i == 0) { | |
| 140 task = Bind(&internal::SequencedTaskTracker::SlowTask, | |
| 141 this->task_tracker_, i); | |
| 142 } else { | |
| 143 task = Bind(&internal::SequencedTaskTracker::FastTask, | |
| 144 this->task_tracker_, i); | |
| 145 } | |
| 146 this->task_tracker_->TaskPosted(i); | |
| 147 task_runner->PostTask(FROM_HERE, task); | |
| 148 } | |
| 149 | |
| 150 this->delegate_.StopTaskRunner(); | |
| 151 | |
| 152 EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); | |
| 153 } | |
| 154 | |
| 155 // This test posts non-nestable tasks in order of increasing delay, and checks | |
| 156 // that that the tasks are run in FIFO order and that there is no execution | |
| 157 // overlap whatsoever between any two tasks. | |
| 158 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { | |
| 159 if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { | |
| 160 DLOG(INFO) << "This SequencedTaskRunner doesn't handle " | |
| 161 "non-zero delays; skipping"; | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 const int task_count = 20; | |
| 166 const int delay_increment_ms = 50; | |
| 167 | |
| 168 this->delegate_.StartTaskRunner(); | |
| 169 scoped_refptr<SequencedTaskRunner> task_runner = | |
| 170 this->delegate_.GetTaskRunner(); | |
| 171 | |
| 172 for (int i = 0; i < task_count; ++i) { | |
| 173 Closure task = Bind(&internal::SequencedTaskTracker::FastTask, | |
| 174 this->task_tracker_, i); | |
| 175 this->task_tracker_->TaskPosted(i); | |
| 176 task_runner->PostNonNestableDelayedTask( | |
| 177 FROM_HERE, | |
| 178 task, | |
| 179 TimeDelta::FromMilliseconds(delay_increment_ms * i)); | |
| 180 } | |
| 181 | |
| 182 this->delegate_.StopTaskRunner(); | |
| 183 | |
| 184 EXPECT_TRUE(this->TaskPhasesInOrder(task_count)); | |
| 185 } | |
| 186 | |
| 187 // This test posts a fast, non-nestable task from within each of a number of | |
| 188 // slow, non-nestable tasks and checks that they all run in the sequence they | |
| 189 // were posted in and that there is no execution overlap whatsoever. | |
| 190 TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) { | |
| 191 const int parent_count = 10; | |
| 192 const int children_per_parent = 10; | |
| 193 | |
| 194 this->delegate_.StartTaskRunner(); | |
| 195 scoped_refptr<SequencedTaskRunner> task_runner = | |
| 196 this->delegate_.GetTaskRunner(); | |
| 197 | |
| 198 const int end = parent_count; | |
| 199 for (int i = 0; i < end; ++i) { | |
| 200 AutoLock phase_lock(this->task_tracker_->phase_lock_); | |
| 201 const int post_i = this->task_tracker_->GetNextPostOrdinal(); | |
| 202 Closure task = Bind( | |
| 203 &internal::SequencedTaskTracker::PostFastNonNestableFromSlowNonNestable, | |
| 204 this->task_tracker_, | |
| 205 task_runner, | |
| 206 post_i, | |
| 207 children_per_parent); | |
| 208 this->task_tracker_->phases_.push_back( | |
| 209 internal::SequencedTaskTracker::TaskPhase( | |
| 210 post_i, internal::SequencedTaskTracker::TaskPhase::POST)); | |
| 211 task_runner->PostNonNestableTask(FROM_HERE, task); | |
| 212 } | |
| 213 | |
| 214 this->delegate_.StopTaskRunner(); | |
| 215 | |
| 216 EXPECT_TRUE(this->TaskPhasesInOrder(parent_count * | |
| 217 (children_per_parent + 1))); | |
| 218 } | |
| 219 | |
| 220 // TODO(francoisk777@gmail.com) Add a test, similiar to the above, which which | |
| 221 // runs some tasked nestedly (which should be implemented in the test | |
| 222 // delegate). Also add, to the the test delegate, a predicate which checks | |
| 223 // whether the implementation supports nested tasks. | |
| 224 // | |
| 225 | |
| 226 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, | |
| 227 SequentialNonNestable, | |
| 228 SequentialNestable, | |
| 229 SequentialDelayedNonNestable, | |
| 230 NonNestablePostFromNonNestableTask | |
| 231 ); | |
| 232 | |
| 233 template <typename TaskRunnerTestDelegate> | |
|
akalin
2012/03/20 22:16:08
the code below should probably go before the unit
Francois
2012/03/26 09:33:21
Done.
| |
| 234 ::testing::AssertionResult | |
| 235 SequencedTaskRunnerTest<TaskRunnerTestDelegate>::TaskPhasesInOrder( | |
|
akalin
2012/03/20 22:16:08
this function does a bit too much -- it's complica
Francois
2012/03/26 09:33:21
Done.
| |
| 236 int task_count) { | |
| 237 typedef internal::SequencedTaskTracker::TaskPhase TaskPhase; | |
| 238 | |
| 239 std::vector<TaskPhase> phases = task_tracker_->GetTaskPhases(); | |
| 240 | |
| 241 if (phases.size() != task_count * 3U) { | |
| 242 return ::testing::AssertionFailure() | |
| 243 << "Expecting " << (task_count * 3) << " task phases, but found only" | |
| 244 << phases.size(); | |
| 245 } | |
| 246 | |
| 247 // Stores the order of phases for each task. | |
| 248 std::vector<std::vector<TaskPhase::Type> > phase_orders(task_count); | |
| 249 | |
| 250 int expected_post_task = 0; | |
| 251 int expected_start_task = 0; | |
| 252 int expected_end_task = 0; | |
| 253 | |
| 254 std::vector<TaskPhase>::const_iterator phase; | |
| 255 for (phase = phases.begin(); phase != phases.end(); ++phase) { | |
| 256 switch (phase->type_) { | |
| 257 case TaskPhase::POST: | |
| 258 if (phase->i_ != expected_post_task) { | |
| 259 return ::testing::AssertionFailure() | |
| 260 << "POST phase for task " << phase->i_ | |
| 261 << " is out of order; expecting one for task " | |
| 262 << expected_post_task << ", instead"; | |
| 263 } | |
| 264 phase_orders[expected_post_task].push_back(TaskPhase::POST); | |
| 265 ++expected_post_task; | |
| 266 break; | |
| 267 case TaskPhase::START: | |
| 268 if (phase->i_ != expected_start_task) { | |
| 269 return ::testing::AssertionFailure() | |
| 270 << "START phase for task " << phase->i_ | |
| 271 << " is out of order; expecting one for task " | |
| 272 << expected_start_task << ", instead"; | |
| 273 } | |
| 274 phase_orders[expected_start_task].push_back(TaskPhase::START); | |
| 275 ++expected_start_task; | |
| 276 break; | |
| 277 case TaskPhase::END: | |
| 278 if (phase->i_ != expected_end_task) { | |
| 279 return ::testing::AssertionFailure() | |
| 280 << "END phase for task " << phase->i_ | |
| 281 << " is out of order; expecting one for task " | |
| 282 << expected_end_task << ", instead"; | |
| 283 } | |
| 284 if (phase_orders[expected_end_task].size() != 2) { | |
| 285 return ::testing::AssertionFailure() | |
| 286 << "POST and/or START phase(s) for task " << phase->i_ | |
| 287 << " did not occur before its END phase"; | |
| 288 } | |
| 289 if (phase_orders[expected_end_task][1] != TaskPhase::START) { | |
| 290 return ::testing::AssertionFailure() | |
| 291 << "START phase for task " << phase->i_ | |
| 292 << " did not occur before its END phase"; | |
| 293 } | |
| 294 if (phase_orders[expected_end_task][0] != TaskPhase::POST) { | |
| 295 return ::testing::AssertionFailure() | |
| 296 << "POST phase for task " << phase->i_ | |
| 297 << " did not occur before its START and END phases"; | |
| 298 } | |
| 299 ++expected_end_task; | |
| 300 break; | |
| 301 } | |
| 302 } | |
| 303 return ::testing::AssertionSuccess(); | |
| 304 } | |
| 305 | |
| 306 } // namespace base | |
| 307 | |
| 308 #endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_ | |
| OLD | NEW |