| OLD | NEW |
| 1 library angular.mock_zone; | 1 library angular.mock_zone; |
| 2 | 2 |
| 3 import 'dart:async' as dart_async; | 3 import 'dart:async' as dart_async; |
| 4 | 4 |
| 5 final _asyncQueue = <Function>[]; | 5 List<Function> _asyncQueue = []; |
| 6 final _timerQueue = <_TimerSpec>[]; | 6 List<_TimerSpec> _timerQueue = []; |
| 7 final _asyncErrors = []; | 7 List _asyncErrors = []; |
| 8 bool _noMoreAsync = false; | 8 bool _noMoreAsync = false; |
| 9 | 9 |
| 10 /** | 10 /** |
| 11 * Runs any queued up async calls and any async calls queued with | 11 * Runs any queued up async calls and any async calls queued with |
| 12 * running microLeap. Example: | 12 * running microLeap. Example: |
| 13 * | 13 * |
| 14 * it('should run async code', async(() { | 14 * it('should run async code', async(() { |
| 15 * var thenRan = false; | 15 * var thenRan = false; |
| 16 * new Future.value('s').then((_) { thenRan = true; }); | 16 * new Future.value('s').then((_) { thenRan = true; }); |
| 17 * expect(thenRan).toBe(false); | 17 * expect(thenRan).toBe(false); |
| 18 * microLeap(); | 18 * microLeap(); |
| 19 * expect(thenRan).toBe(true); | 19 * expect(thenRan).toBe(true); |
| 20 * })); | 20 * })); |
| 21 * | 21 * |
| 22 * it('should run chained thens', async(() { | 22 * it('should run chained thens', async(() { |
| 23 * var log = []; | 23 * var log = []; |
| 24 * new Future.value('s') | 24 * new Future.value('s') |
| 25 * .then((_) { log.add('firstThen'); }) | 25 * .then((_) { log.add('firstThen'); }) |
| 26 * .then((_) { log.add('2ndThen'); }); | 26 * .then((_) { log.add('2ndThen'); }); |
| 27 * expect(log.join(' ')).toEqual(''); | 27 * expect(log.join(' ')).toEqual(''); |
| 28 * microLeap(); | 28 * microLeap(); |
| 29 * expect(log.join(' ')).toEqual('firstThen 2ndThen'); | 29 * expect(log.join(' ')).toEqual('firstThen 2ndThen'); |
| 30 * })); | 30 * })); |
| 31 * | 31 * |
| 32 */ | 32 */ |
| 33 microLeap() { | 33 microLeap() { |
| 34 while (!_asyncQueue.isEmpty) { | 34 while (!_asyncQueue.isEmpty) { |
| 35 // copy the queue as it may change. | 35 // copy the queue as it may change. |
| 36 var toRun = new List.from(_asyncQueue); | 36 var toRun = new List.from(_asyncQueue); |
| 37 _asyncQueue.clear(); | 37 _asyncQueue = []; |
| 38 // TODO: Support the case where multiple exceptions are thrown. | 38 // TODO: Support the case where multiple exceptions are thrown. |
| 39 // e.g. with a throwNextException() method. | 39 // e.g. with a throwNextException() method. |
| 40 assert(_asyncErrors.isEmpty); | 40 assert(_asyncErrors.isEmpty); |
| 41 toRun.forEach((fn) => fn()); | 41 toRun.forEach((fn) => fn()); |
| 42 if (_asyncErrors.isNotEmpty) { | 42 if (!_asyncErrors.isEmpty) { |
| 43 var e = _asyncErrors.removeAt(0); | 43 var e = _asyncErrors.removeAt(0); |
| 44 throw ['Async error', e[0], e[1]]; | 44 throw ['Async error', e[0], e[1]]; |
| 45 } | 45 } |
| 46 } | 46 } |
| 47 } | 47 } |
| 48 | 48 |
| 49 /** | 49 /** |
| 50 * Simulates a clock tick by running any scheduled timers. Can only be used | 50 * Simulates a clock tick by running any scheduled timers. Can only be used |
| 51 * in [async] tests.Clock tick will call [microLeap] to process the microtask | 51 * in [async] tests.Clock tick will call [microLeap] to process the microtask |
| 52 * queue before each timer callback. | 52 * queue before each timer callback. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 70 * new Timer.periodic(new Duration(milliseconds: 10), (_) => timerRan++); | 70 * new Timer.periodic(new Duration(milliseconds: 10), (_) => timerRan++); |
| 71 * | 71 * |
| 72 * clockTick(milliseconds: 9); | 72 * clockTick(milliseconds: 9); |
| 73 * expect(timerRan).toBe(0); | 73 * expect(timerRan).toBe(0); |
| 74 * clockTick(milliseconds: 1); | 74 * clockTick(milliseconds: 1); |
| 75 * expect(timerRan).toBe(1); | 75 * expect(timerRan).toBe(1); |
| 76 * clockTick(milliseconds: 30); | 76 * clockTick(milliseconds: 30); |
| 77 * expect(timerRan).toBe(4); | 77 * expect(timerRan).toBe(4); |
| 78 * })); | 78 * })); |
| 79 */ | 79 */ |
| 80 void clockTick({int days: 0, | 80 clockTick({int days: 0, |
| 81 int hours: 0, | 81 int hours: 0, |
| 82 int minutes: 0, | 82 int minutes: 0, |
| 83 int seconds: 0, | 83 int seconds: 0, |
| 84 int milliseconds: 0, | 84 int milliseconds: 0, |
| 85 int microseconds: 0}) { | 85 int microseconds: 0}) { |
| 86 var tickDuration = new Duration(days: days, hours: hours, minutes: minutes, | 86 var tickDuration = new Duration(days: days, hours: hours, minutes: minutes, |
| 87 seconds: seconds, milliseconds: milliseconds, microseconds: microseconds); | 87 seconds: seconds, milliseconds: milliseconds, microseconds: microseconds); |
| 88 | 88 |
| 89 var queue = _timerQueue; |
| 89 var remainingTimers = []; | 90 var remainingTimers = []; |
| 90 var queue = new List.from(_timerQueue); | 91 _timerQueue = []; |
| 91 _timerQueue.clear(); | 92 queue.forEach((_TimerSpec spec) { |
| 92 queue | 93 if (!spec.isActive) return; // Skip over inactive timers. |
| 93 .where((_TimerSpec spec) => spec.isActive) | 94 if (spec.periodic) { |
| 94 .forEach((_TimerSpec spec) { | 95 // We always add back the periodic timer unless it's cancelled. |
| 95 if (spec.periodic) { | 96 remainingTimers.add(spec); |
| 96 // We always add back the periodic timer unless it's cancelled. | 97 |
| 98 // Ignore ZERO duration ticks for periodic timers. |
| 99 if (tickDuration == Duration.ZERO) return; |
| 100 |
| 101 spec.elapsed += tickDuration; |
| 102 // Run the timer as many times as the timer priod fits into the tick. |
| 103 while (spec.elapsed >= spec.duration) { |
| 104 spec.elapsed -= spec.duration; |
| 105 microLeap(); |
| 106 spec.fn(spec); |
| 107 } |
| 108 } else { |
| 109 spec.duration -= tickDuration; |
| 110 if (spec.duration <= Duration.ZERO) { |
| 111 microLeap(); |
| 112 spec.fn(); |
| 113 } else { |
| 97 remainingTimers.add(spec); | 114 remainingTimers.add(spec); |
| 98 | |
| 99 // Ignore ZERO duration ticks for periodic timers. | |
| 100 if (tickDuration == Duration.ZERO) return; | |
| 101 | |
| 102 spec.elapsed += tickDuration; | |
| 103 // Run the timer as many times as the timer priod fits into the tick. | |
| 104 while (spec.elapsed >= spec.duration) { | |
| 105 spec.elapsed -= spec.duration; | |
| 106 microLeap(); | |
| 107 spec.fn(spec); | |
| 108 } | |
| 109 } else { | |
| 110 spec.duration -= tickDuration; | |
| 111 if (spec.duration <= Duration.ZERO) { | |
| 112 microLeap(); | |
| 113 spec.fn(); | |
| 114 } else { | |
| 115 remainingTimers.add(spec); | |
| 116 } | |
| 117 } | 115 } |
| 118 }); | 116 } |
| 117 }); |
| 119 // Remaining timers should come before anything else scheduled after them. | 118 // Remaining timers should come before anything else scheduled after them. |
| 120 _timerQueue.insertAll(0, remainingTimers); | 119 _timerQueue.insertAll(0, remainingTimers); |
| 121 } | 120 } |
| 122 | 121 |
| 123 /** | 122 /** |
| 124 * Causes scheduleMicrotask calls to throw exceptions. | 123 * Causes scheduleMicrotask calls to throw exceptions. |
| 125 * | 124 * |
| 126 * This function is useful while debugging async tests: the exception | 125 * This function is useful while debugging async tests: the exception |
| 127 * is thrown from the scheduleMicrotask call-site instead later in the test. | 126 * is thrown from the scheduleMicrotask call-site instead later in the test. |
| 128 */ | 127 */ |
| 129 noMoreAsync() { | 128 noMoreAsync() { |
| 130 _noMoreAsync = true; | 129 _noMoreAsync = true; |
| 131 } | 130 } |
| 132 | 131 |
| 133 /** | 132 /** |
| 134 * Captures all scheduleMicrotask calls inside of a function. | 133 * Captures all scheduleMicrotask calls inside of a function. |
| 135 * | 134 * |
| 136 * Typically used within a test: | 135 * Typically used within a test: |
| 137 * | 136 * |
| 138 * it('should be async', async(() { | 137 * it('should be async', async(() { |
| 139 * ... | 138 * ... |
| 140 * })); | 139 * })); |
| 141 */ | 140 */ |
| 142 async(Function fn) => () { | 141 async(Function fn) => |
| 142 () { |
| 143 _noMoreAsync = false; | 143 _noMoreAsync = false; |
| 144 _asyncErrors.clear(); | 144 _asyncErrors = []; |
| 145 _timerQueue.clear(); | 145 _timerQueue = []; |
| 146 var zoneSpec = new dart_async.ZoneSpecification( | 146 var zoneSpec = new dart_async.ZoneSpecification( |
| 147 scheduleMicrotask: (_, __, ___, asyncFn) { | 147 scheduleMicrotask: (_, __, ___, asyncFn) { |
| 148 if (_noMoreAsync) { | 148 if (_noMoreAsync) { |
| 149 throw ['scheduleMicrotask called after noMoreAsync()']; | 149 throw ['scheduleMicrotask called after noMoreAsync()']; |
| 150 } else { | 150 } else { |
| 151 _asyncQueue.add(asyncFn); | 151 _asyncQueue.add(asyncFn); |
| 152 } | 152 } |
| 153 }, | 153 }, |
| 154 createTimer: (_, __, ____, Duration duration, void f()) => | 154 createTimer: (_, __, ____, Duration duration, void f()) => |
| 155 _createTimer(f, duration, false), | 155 _createTimer(f, duration, false), |
| 156 createPeriodicTimer: | 156 createPeriodicTimer: |
| 157 (_, __, ___, Duration period, void f(dart_async.Timer timer)) => | 157 (_, __, ___, Duration period, void f(dart_async.Timer timer)) => |
| 158 _createTimer(f, period, true), | 158 _createTimer(f, period, true), |
| 159 handleUncaughtError: (_, __, ___, e, s) => _asyncErrors.add([e, s]) | 159 handleUncaughtError: (_, __, ___, e, s) => _asyncErrors.add([e, s]) |
| 160 ); | 160 ); |
| 161 dart_async.runZoned(() { | 161 dart_async.runZoned(() { |
| 162 fn(); | 162 fn(); |
| 163 microLeap(); | 163 microLeap(); |
| 164 }, zoneSpecification: zoneSpec); | 164 }, zoneSpecification: zoneSpec); |
| 165 | 165 |
| 166 _asyncErrors.forEach((e) { | 166 _asyncErrors.forEach((e) { |
| 167 throw "During runZoned: ${e[0]}. Stack:\n${e[1]}"; | 167 throw "During runZoned: ${e[0]}. Stack:\n${e[1]}"; |
| 168 }); | 168 }); |
| 169 | 169 |
| 170 var activeTimers = _timerQueue.fold(0, (nb, _TimerSpec spec) { | 170 if (!_timerQueue.isEmpty && _timerQueue.any((_TimerSpec spec) => spec.isActive
)) { |
| 171 return spec.isActive ? nb + 1 : nb; | 171 throw ["${_timerQueue.where((_TimerSpec spec) => spec.isActive).length} " |
| 172 }); | 172 "active timer(s) are still in the queue."]; |
| 173 | |
| 174 if (activeTimers > 0) { | |
| 175 throw ["$activeTimers active timer(s) are still in the queue."]; | |
| 176 } | 173 } |
| 177 }; | 174 }; |
| 178 | 175 |
| 179 _createTimer(Function fn, Duration duration, bool periodic) { | 176 _createTimer(Function fn, Duration duration, bool periodic) { |
| 180 var timer = new _TimerSpec(fn, duration, periodic); | 177 var timer = new _TimerSpec(fn, duration, periodic); |
| 181 _timerQueue.add(timer); | 178 _timerQueue.add(timer); |
| 182 return timer; | 179 return timer; |
| 183 } | 180 } |
| 184 | 181 |
| 185 /** | 182 /** |
| (...skipping 21 matching lines...) Expand all Loading... |
| 207 Duration elapsed = Duration.ZERO; | 204 Duration elapsed = Duration.ZERO; |
| 208 bool periodic; | 205 bool periodic; |
| 209 bool isActive = true; | 206 bool isActive = true; |
| 210 | 207 |
| 211 _TimerSpec(this.fn, this.duration, this.periodic); | 208 _TimerSpec(this.fn, this.duration, this.periodic); |
| 212 | 209 |
| 213 void cancel() { | 210 void cancel() { |
| 214 isActive = false; | 211 isActive = false; |
| 215 } | 212 } |
| 216 } | 213 } |
| OLD | NEW |