|
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 |