OLD | NEW |
1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 21 matching lines...) Expand all Loading... |
32 /// test('testedFunc', () { | 32 /// test('testedFunc', () { |
33 /// new FakeAsync().run((async) { | 33 /// new FakeAsync().run((async) { |
34 /// testedFunc(clock: async.getClock(initialTime)); | 34 /// testedFunc(clock: async.getClock(initialTime)); |
35 /// async.elapse(duration); | 35 /// async.elapse(duration); |
36 /// expect(...) | 36 /// expect(...) |
37 /// }); | 37 /// }); |
38 /// }); | 38 /// }); |
39 abstract class FakeAsync { | 39 abstract class FakeAsync { |
40 factory FakeAsync() = _FakeAsync; | 40 factory FakeAsync() = _FakeAsync; |
41 | 41 |
42 FakeAsync._(); | |
43 | |
44 /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and | 42 /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and |
45 /// [elapseBlocking]. | 43 /// [elapseBlocking]. |
46 /// | 44 /// |
47 /// The returned clock starts at [initialTime], and calls to [elapse] and | 45 /// The returned clock starts at [initialTime], and calls to [elapse] and |
48 /// [elapseBlocking] advance the clock, even if they occured before the call | 46 /// [elapseBlocking] advance the clock, even if they occured before the call |
49 /// to this method. | 47 /// to this method. |
50 /// | 48 /// |
51 /// The clock can be passed as a dependency to the unit under test. | 49 /// The clock can be passed as a dependency to the unit under test. |
52 Clock getClock(DateTime initialTime); | 50 Clock getClock(DateTime initialTime); |
53 | 51 |
(...skipping 28 matching lines...) Expand all Loading... |
82 /// If [duration] is negative, throws an [ArgumentError]. | 80 /// If [duration] is negative, throws an [ArgumentError]. |
83 void elapseBlocking(Duration duration); | 81 void elapseBlocking(Duration duration); |
84 | 82 |
85 /// Runs [callback] in a [Zone] with fake timer and microtask scheduling. | 83 /// Runs [callback] in a [Zone] with fake timer and microtask scheduling. |
86 /// | 84 /// |
87 /// Uses | 85 /// Uses |
88 /// [ZoneSpecification.createTimer], [ZoneSpecification.createPeriodicTimer], | 86 /// [ZoneSpecification.createTimer], [ZoneSpecification.createPeriodicTimer], |
89 /// and [ZoneSpecification.scheduleMicrotask] to store callbacks for later | 87 /// and [ZoneSpecification.scheduleMicrotask] to store callbacks for later |
90 /// execution within the zone via calls to [elapse]. | 88 /// execution within the zone via calls to [elapse]. |
91 /// | 89 /// |
92 /// [callback] is called with `this` as argument. | 90 /// Calls [callback] with `this` as argument and returns the result returned |
93 run(callback(FakeAsync self)); | 91 /// by [callback]. |
| 92 dynamic run(callback(FakeAsync self)); |
94 | 93 |
95 /// Runs all remaining microtasks, including those scheduled as a result of | 94 /// Runs all remaining microtasks, including those scheduled as a result of |
96 /// running them, until there are no more microtasks scheduled. | 95 /// running them, until there are no more microtasks scheduled. |
97 /// | 96 /// |
98 /// Does not run timers. | 97 /// Does not run timers. |
99 void flushMicrotasks(); | 98 void flushMicrotasks(); |
100 | 99 |
101 /// Runs all timers until no timers remain (subject to [flushPeriodicTimers] | 100 /// Runs all timers until no timers remain (subject to [flushPeriodicTimers] |
102 /// option), including those scheduled as a result of running them. | 101 /// option), including those scheduled as a result of running them. |
103 /// | 102 /// |
104 /// [timeout] lets you set the maximum amount of time the flushing will take. | 103 /// [timeout] lets you set the maximum amount of time the flushing will take. |
105 /// Throws a [StateError] if the [timeout] is exceeded. The default timeout | 104 /// Throws a [StateError] if the [timeout] is exceeded. The default timeout |
106 /// is 1 hour. [timeout] is relative to the elapsed time. | 105 /// is 1 hour. [timeout] is relative to the elapsed time. |
107 void flushTimers({Duration timeout: const Duration(hours: 1), | 106 void flushTimers( |
| 107 {Duration timeout: const Duration(hours: 1), |
108 bool flushPeriodicTimers: true}); | 108 bool flushPeriodicTimers: true}); |
109 | 109 |
110 /// The number of created periodic timers that have not been canceled. | 110 /// The number of created periodic timers that have not been canceled. |
111 int get periodicTimerCount; | 111 int get periodicTimerCount; |
112 | 112 |
113 /// The number of pending non periodic timers that have not been canceled. | 113 /// The number of pending non periodic timers that have not been canceled. |
114 int get nonPeriodicTimerCount; | 114 int get nonPeriodicTimerCount; |
115 | 115 |
116 /// The number of pending microtasks. | 116 /// The number of pending microtasks. |
117 int get microtaskCount; | 117 int get microtaskCount; |
118 } | 118 } |
119 | 119 |
120 class _FakeAsync extends FakeAsync { | 120 class _FakeAsync implements FakeAsync { |
121 Duration _elapsed = Duration.ZERO; | 121 Duration _elapsed = Duration.ZERO; |
122 Duration _elapsingTo; | 122 Duration _elapsingTo; |
123 Queue<Function> _microtasks = new Queue(); | 123 Queue<Function> _microtasks = new Queue(); |
124 Set<_FakeTimer> _timers = new Set<_FakeTimer>(); | 124 Set<_FakeTimer> _timers = new Set<_FakeTimer>(); |
125 | 125 |
126 _FakeAsync() : super._() { | |
127 _elapsed; | |
128 } | |
129 | |
130 @override | 126 @override |
131 Clock getClock(DateTime initialTime) => | 127 Clock getClock(DateTime initialTime) => |
132 new Clock(() => initialTime.add(_elapsed)); | 128 new Clock(() => initialTime.add(_elapsed)); |
133 | 129 |
134 @override | 130 @override |
135 void elapse(Duration duration) { | 131 void elapse(Duration duration) { |
136 if (duration.inMicroseconds < 0) { | 132 if (duration.inMicroseconds < 0) { |
137 throw new ArgumentError('Cannot call elapse with negative duration'); | 133 throw new ArgumentError('Cannot call elapse with negative duration'); |
138 } | 134 } |
139 if (_elapsingTo != null) { | 135 if (_elapsingTo != null) { |
(...skipping 15 matching lines...) Expand all Loading... |
155 _elapsingTo = _elapsed; | 151 _elapsingTo = _elapsed; |
156 } | 152 } |
157 } | 153 } |
158 | 154 |
159 @override | 155 @override |
160 void flushMicrotasks() { | 156 void flushMicrotasks() { |
161 _drainMicrotasks(); | 157 _drainMicrotasks(); |
162 } | 158 } |
163 | 159 |
164 @override | 160 @override |
165 void flushTimers({Duration timeout: const Duration(hours: 1), | 161 void flushTimers( |
| 162 {Duration timeout: const Duration(hours: 1), |
166 bool flushPeriodicTimers: true}) { | 163 bool flushPeriodicTimers: true}) { |
167 final absoluteTimeout = _elapsed + timeout; | 164 final absoluteTimeout = _elapsed + timeout; |
168 _drainTimersWhile((_FakeTimer timer) { | 165 _drainTimersWhile((_FakeTimer timer) { |
169 if (timer._nextCall > absoluteTimeout) { | 166 if (timer._nextCall > absoluteTimeout) { |
170 throw new StateError( | 167 throw new StateError( |
171 'Exceeded timeout ${timeout} while flushing timers'); | 168 'Exceeded timeout ${timeout} while flushing timers'); |
172 } | 169 } |
173 if (flushPeriodicTimers) { | 170 if (flushPeriodicTimers) { |
174 return _timers.isNotEmpty; | 171 return _timers.isNotEmpty; |
175 } else { | 172 } else { |
176 // translation: keep draining while non-periodic timers exist | 173 // translation: keep draining while non-periodic timers exist |
177 return _timers.any((_FakeTimer timer) => !timer._isPeriodic); | 174 return _timers.any((_FakeTimer timer) => !timer._isPeriodic); |
178 } | 175 } |
179 }); | 176 }); |
180 } | 177 } |
181 | 178 |
182 @override | 179 @override |
183 run(callback(FakeAsync self)) { | 180 run(callback(FakeAsync self)) { |
184 if (_zone == null) { | 181 if (_zone == null) { |
185 _zone = Zone.current.fork(specification: _zoneSpec); | 182 _zone = Zone.current.fork(specification: _zoneSpec); |
186 } | 183 } |
187 return _zone.runGuarded(() => callback(this)); | 184 return _zone.runGuarded(() => callback(this)); |
188 } | 185 } |
| 186 |
189 Zone _zone; | 187 Zone _zone; |
190 | 188 |
191 @override | 189 @override |
192 int get periodicTimerCount => | 190 int get periodicTimerCount => |
193 _timers.where((_FakeTimer timer) => timer._isPeriodic).length; | 191 _timers.where((_FakeTimer timer) => timer._isPeriodic).length; |
194 | 192 |
195 @override | 193 @override |
196 int get nonPeriodicTimerCount => | 194 int get nonPeriodicTimerCount => |
197 _timers.where((_FakeTimer timer) => !timer._isPeriodic).length; | 195 _timers.where((_FakeTimer timer) => !timer._isPeriodic).length; |
198 | 196 |
199 @override | 197 @override |
200 int get microtaskCount => _microtasks.length; | 198 int get microtaskCount => _microtasks.length; |
201 | 199 |
202 ZoneSpecification get _zoneSpec => new ZoneSpecification( | 200 ZoneSpecification get _zoneSpec => new ZoneSpecification( |
203 createTimer: (_, __, ___, Duration duration, Function callback) { | 201 createTimer: (_, __, ___, Duration duration, Function callback) { |
204 return _createTimer(duration, callback, false); | 202 return _createTimer(duration, callback, false); |
205 }, createPeriodicTimer: (_, __, ___, Duration duration, Function callback) { | 203 }, createPeriodicTimer: |
206 return _createTimer(duration, callback, true); | 204 (_, __, ___, Duration duration, Function callback) { |
207 }, scheduleMicrotask: (_, __, ___, Function microtask) { | 205 return _createTimer(duration, callback, true); |
208 _microtasks.add(microtask); | 206 }, scheduleMicrotask: (_, __, ___, Function microtask) { |
209 }); | 207 _microtasks.add(microtask); |
| 208 }); |
210 | 209 |
211 _drainTimersWhile(bool predicate(_FakeTimer)) { | 210 _drainTimersWhile(bool predicate(_FakeTimer)) { |
212 _drainMicrotasks(); | 211 _drainMicrotasks(); |
213 _FakeTimer next; | 212 _FakeTimer next; |
214 while ((next = _getNextTimer()) != null && predicate(next)) { | 213 while ((next = _getNextTimer()) != null && predicate(next)) { |
215 _runTimer(next); | 214 _runTimer(next); |
216 _drainMicrotasks(); | 215 _drainMicrotasks(); |
217 } | 216 } |
218 } | 217 } |
219 | 218 |
(...skipping 14 matching lines...) Expand all Loading... |
234 (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); | 233 (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); |
235 } | 234 } |
236 | 235 |
237 _runTimer(_FakeTimer timer) { | 236 _runTimer(_FakeTimer timer) { |
238 assert(timer.isActive); | 237 assert(timer.isActive); |
239 _elapseTo(timer._nextCall); | 238 _elapseTo(timer._nextCall); |
240 if (timer._isPeriodic) { | 239 if (timer._isPeriodic) { |
241 timer._callback(timer); | 240 timer._callback(timer); |
242 timer._nextCall += timer._duration; | 241 timer._nextCall += timer._duration; |
243 } else { | 242 } else { |
| 243 _timers.remove(timer); |
244 timer._callback(); | 244 timer._callback(); |
245 _timers.remove(timer); | |
246 } | 245 } |
247 } | 246 } |
248 | 247 |
249 _drainMicrotasks() { | 248 _drainMicrotasks() { |
250 while (_microtasks.isNotEmpty) { | 249 while (_microtasks.isNotEmpty) { |
251 _microtasks.removeFirst()(); | 250 _microtasks.removeFirst()(); |
252 } | 251 } |
253 } | 252 } |
254 | 253 |
255 _hasTimer(_FakeTimer timer) => _timers.contains(timer); | 254 _hasTimer(_FakeTimer timer) => _timers.contains(timer); |
(...skipping 17 matching lines...) Expand all Loading... |
273 | 272 |
274 _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time) | 273 _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time) |
275 : _duration = duration < _minDuration ? _minDuration : duration { | 274 : _duration = duration < _minDuration ? _minDuration : duration { |
276 _nextCall = _time._elapsed + _duration; | 275 _nextCall = _time._elapsed + _duration; |
277 } | 276 } |
278 | 277 |
279 bool get isActive => _time._hasTimer(this); | 278 bool get isActive => _time._hasTimer(this); |
280 | 279 |
281 cancel() => _time._cancelTimer(this); | 280 cancel() => _time._cancelTimer(this); |
282 } | 281 } |
OLD | NEW |