| OLD | NEW |
| (Empty) | |
| 1 library angular.mock_zone; |
| 2 |
| 3 import 'dart:async' as dart_async; |
| 4 |
| 5 List<Function> _asyncQueue = []; |
| 6 List<_TimerSpec> _timerQueue = []; |
| 7 List _asyncErrors = []; |
| 8 bool _noMoreAsync = false; |
| 9 |
| 10 /** |
| 11 * Runs any queued up async calls and any async calls queued with |
| 12 * running microLeap. Example: |
| 13 * |
| 14 * it('should run async code', async(() { |
| 15 * var thenRan = false; |
| 16 * new Future.value('s').then((_) { thenRan = true; }); |
| 17 * expect(thenRan).toBe(false); |
| 18 * microLeap(); |
| 19 * expect(thenRan).toBe(true); |
| 20 * })); |
| 21 * |
| 22 * it('should run chained thens', async(() { |
| 23 * var log = []; |
| 24 * new Future.value('s') |
| 25 * .then((_) { log.add('firstThen'); }) |
| 26 * .then((_) { log.add('2ndThen'); }); |
| 27 * expect(log.join(' ')).toEqual(''); |
| 28 * microLeap(); |
| 29 * expect(log.join(' ')).toEqual('firstThen 2ndThen'); |
| 30 * })); |
| 31 * |
| 32 */ |
| 33 microLeap() { |
| 34 while (!_asyncQueue.isEmpty) { |
| 35 // copy the queue as it may change. |
| 36 var toRun = new List.from(_asyncQueue); |
| 37 _asyncQueue = []; |
| 38 // TODO: Support the case where multiple exceptions are thrown. |
| 39 // e.g. with a throwNextException() method. |
| 40 assert(_asyncErrors.isEmpty); |
| 41 toRun.forEach((fn) => fn()); |
| 42 if (!_asyncErrors.isEmpty) { |
| 43 var e = _asyncErrors.removeAt(0); |
| 44 throw ['Async error', e[0], e[1]]; |
| 45 } |
| 46 } |
| 47 } |
| 48 |
| 49 /** |
| 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 |
| 52 * queue before each timer callback. |
| 53 * |
| 54 * Note: microtasks scheduled form the last timer are not going to be processed. |
| 55 * |
| 56 * Example: |
| 57 * |
| 58 * it('should run queued timer after sufficient clock ticks', async(() { |
| 59 * bool timerRan = false; |
| 60 * new Timer(new Duration(milliseconds: 10), () => timerRan = true); |
| 61 * |
| 62 * clockTick(milliseconds: 9); |
| 63 * expect(timerRan).toBeFalsy(); |
| 64 * clockTick(milliseconds: 1); |
| 65 * expect(timerRan).toBeTruthy(); |
| 66 * })); |
| 67 * |
| 68 * it('should run periodic timer', async(() { |
| 69 * int timerRan = 0; |
| 70 * new Timer.periodic(new Duration(milliseconds: 10), (_) => timerRan++); |
| 71 * |
| 72 * clockTick(milliseconds: 9); |
| 73 * expect(timerRan).toBe(0); |
| 74 * clockTick(milliseconds: 1); |
| 75 * expect(timerRan).toBe(1); |
| 76 * clockTick(milliseconds: 30); |
| 77 * expect(timerRan).toBe(4); |
| 78 * })); |
| 79 */ |
| 80 clockTick({int days: 0, |
| 81 int hours: 0, |
| 82 int minutes: 0, |
| 83 int seconds: 0, |
| 84 int milliseconds: 0, |
| 85 int microseconds: 0}) { |
| 86 var tickDuration = new Duration(days: days, hours: hours, minutes: minutes, |
| 87 seconds: seconds, milliseconds: milliseconds, microseconds: microseconds); |
| 88 |
| 89 var queue = _timerQueue; |
| 90 var remainingTimers = []; |
| 91 _timerQueue = []; |
| 92 queue.forEach((_TimerSpec spec) { |
| 93 if (!spec.isActive) return; // Skip over inactive timers. |
| 94 if (spec.periodic) { |
| 95 // We always add back the periodic timer unless it's cancelled. |
| 96 remainingTimers.add(spec); |
| 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 { |
| 114 remainingTimers.add(spec); |
| 115 } |
| 116 } |
| 117 }); |
| 118 // Remaining timers should come before anything else scheduled after them. |
| 119 _timerQueue.insertAll(0, remainingTimers); |
| 120 } |
| 121 |
| 122 /** |
| 123 * Causes scheduleMicrotask calls to throw exceptions. |
| 124 * |
| 125 * This function is useful while debugging async tests: the exception |
| 126 * is thrown from the scheduleMicrotask call-site instead later in the test. |
| 127 */ |
| 128 noMoreAsync() { |
| 129 _noMoreAsync = true; |
| 130 } |
| 131 |
| 132 /** |
| 133 * Captures all scheduleMicrotask calls inside of a function. |
| 134 * |
| 135 * Typically used within a test: |
| 136 * |
| 137 * it('should be async', async(() { |
| 138 * ... |
| 139 * })); |
| 140 */ |
| 141 async(Function fn) => |
| 142 () { |
| 143 _noMoreAsync = false; |
| 144 _asyncErrors = []; |
| 145 _timerQueue = []; |
| 146 var zoneSpec = new dart_async.ZoneSpecification( |
| 147 scheduleMicrotask: (_, __, ___, asyncFn) { |
| 148 if (_noMoreAsync) { |
| 149 throw ['scheduleMicrotask called after noMoreAsync()']; |
| 150 } else { |
| 151 _asyncQueue.add(asyncFn); |
| 152 } |
| 153 }, |
| 154 createTimer: (_, __, ____, Duration duration, void f()) => |
| 155 _createTimer(f, duration, false), |
| 156 createPeriodicTimer: |
| 157 (_, __, ___, Duration period, void f(dart_async.Timer timer)) => |
| 158 _createTimer(f, period, true), |
| 159 handleUncaughtError: (_, __, ___, e, s) => _asyncErrors.add([e, s]) |
| 160 ); |
| 161 dart_async.runZoned(() { |
| 162 fn(); |
| 163 microLeap(); |
| 164 }, zoneSpecification: zoneSpec); |
| 165 |
| 166 _asyncErrors.forEach((e) { |
| 167 throw "During runZoned: ${e[0]}. Stack:\n${e[1]}"; |
| 168 }); |
| 169 |
| 170 if (!_timerQueue.isEmpty && _timerQueue.any((_TimerSpec spec) => spec.isActive
)) { |
| 171 throw ["${_timerQueue.where((_TimerSpec spec) => spec.isActive).length} " |
| 172 "active timer(s) are still in the queue."]; |
| 173 } |
| 174 }; |
| 175 |
| 176 _createTimer(Function fn, Duration duration, bool periodic) { |
| 177 var timer = new _TimerSpec(fn, duration, periodic); |
| 178 _timerQueue.add(timer); |
| 179 return timer; |
| 180 } |
| 181 |
| 182 /** |
| 183 * Enforces synchronous code. Any calls to scheduleMicrotask inside of 'sync' |
| 184 * will throw an exception. |
| 185 */ |
| 186 sync(Function fn) => () { |
| 187 dart_async.runZoned(fn, zoneSpecification: new dart_async.ZoneSpecification( |
| 188 scheduleMicrotask: (_, __, ___, asyncFn) { |
| 189 throw ['scheduleMicrotask called from sync function.']; |
| 190 }, |
| 191 createTimer: (_, __, ____, Duration duration, void f()) { |
| 192 throw ['Timer created from sync function.']; |
| 193 }, |
| 194 createPeriodicTimer: |
| 195 (_, __, ___, Duration period, void f(dart_async.Timer timer)) { |
| 196 throw ['periodic Timer created from sync function.']; |
| 197 } |
| 198 )); |
| 199 }; |
| 200 |
| 201 class _TimerSpec implements dart_async.Timer { |
| 202 Function fn; |
| 203 Duration duration; |
| 204 Duration elapsed = Duration.ZERO; |
| 205 bool periodic; |
| 206 bool isActive = true; |
| 207 |
| 208 _TimerSpec(this.fn, this.duration, this.periodic); |
| 209 |
| 210 void cancel() { |
| 211 isActive = false; |
| 212 } |
| 213 } |
| OLD | NEW |