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