OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // dart2js chain tests are separated out because dart2js stack traces are |
| 6 // inconsistent due to inlining and browser differences. These tests don't |
| 7 // assert anything about the content of the traces, just the number of traces in |
| 8 // a chain. |
| 9 @TestOn('js') |
| 10 |
| 11 import 'dart:async'; |
| 12 |
| 13 import 'package:stack_trace/stack_trace.dart'; |
| 14 import 'package:test/test.dart'; |
| 15 |
| 16 import 'utils.dart'; |
| 17 |
| 18 void main() { |
| 19 group('capture() with onError catches exceptions', () { |
| 20 test('thrown synchronously', () async { |
| 21 var chain = await captureFuture(() => throw 'error'); |
| 22 expect(chain.traces, hasLength(1)); |
| 23 }); |
| 24 |
| 25 test('thrown in a microtask', () async { |
| 26 var chain = await captureFuture(() => inMicrotask(() => throw 'error')); |
| 27 expect(chain.traces, hasLength(2)); |
| 28 }); |
| 29 |
| 30 test('thrown in a one-shot timer', () async { |
| 31 var chain = await captureFuture( |
| 32 () => inOneShotTimer(() => throw 'error')); |
| 33 expect(chain.traces, hasLength(2)); |
| 34 }); |
| 35 |
| 36 test('thrown in a periodic timer', () async { |
| 37 var chain = await captureFuture( |
| 38 () => inPeriodicTimer(() => throw 'error')); |
| 39 expect(chain.traces, hasLength(2)); |
| 40 }); |
| 41 |
| 42 test('thrown in a nested series of asynchronous operations', () async { |
| 43 var chain = await captureFuture(() { |
| 44 inPeriodicTimer(() { |
| 45 inOneShotTimer(() => inMicrotask(() => throw 'error')); |
| 46 }); |
| 47 }); |
| 48 |
| 49 expect(chain.traces, hasLength(4)); |
| 50 }); |
| 51 |
| 52 test('thrown in a long future chain', () async { |
| 53 var chain = await captureFuture(() => inFutureChain(() => throw 'error')); |
| 54 |
| 55 // Despite many asynchronous operations, there's only one level of |
| 56 // nested calls, so there should be only two traces in the chain. This |
| 57 // is important; programmers expect stack trace memory consumption to be |
| 58 // O(depth of program), not O(length of program). |
| 59 expect(chain.traces, hasLength(2)); |
| 60 }); |
| 61 |
| 62 test('thrown in new Future()', () async { |
| 63 var chain = await captureFuture(() => inNewFuture(() => throw 'error')); |
| 64 expect(chain.traces, hasLength(3)); |
| 65 }); |
| 66 |
| 67 test('thrown in new Future.sync()', () async { |
| 68 var chain = await captureFuture(() { |
| 69 inMicrotask(() => inSyncFuture(() => throw 'error')); |
| 70 }); |
| 71 |
| 72 expect(chain.traces, hasLength(3)); |
| 73 }); |
| 74 |
| 75 test('multiple times', () { |
| 76 var completer = new Completer(); |
| 77 var first = true; |
| 78 |
| 79 Chain.capture(() { |
| 80 inMicrotask(() => throw 'first error'); |
| 81 inPeriodicTimer(() => throw 'second error'); |
| 82 }, onError: (error, chain) { |
| 83 try { |
| 84 if (first) { |
| 85 expect(error, equals('first error')); |
| 86 expect(chain.traces, hasLength(2)); |
| 87 first = false; |
| 88 } else { |
| 89 expect(error, equals('second error')); |
| 90 expect(chain.traces, hasLength(2)); |
| 91 completer.complete(); |
| 92 } |
| 93 } catch (error, stackTrace) { |
| 94 completer.completeError(error, stackTrace); |
| 95 } |
| 96 }); |
| 97 |
| 98 return completer.future; |
| 99 }); |
| 100 |
| 101 test('passed to a completer', () async { |
| 102 var trace = new Trace.current(); |
| 103 var chain = await captureFuture(() { |
| 104 inMicrotask(() => completerErrorFuture(trace)); |
| 105 }); |
| 106 |
| 107 expect(chain.traces, hasLength(3)); |
| 108 |
| 109 // The first trace is the trace that was manually reported for the |
| 110 // error. |
| 111 expect(chain.traces.first.toString(), equals(trace.toString())); |
| 112 }); |
| 113 |
| 114 test('passed to a completer with no stack trace', () async { |
| 115 var chain = await captureFuture(() { |
| 116 inMicrotask(() => completerErrorFuture()); |
| 117 }); |
| 118 |
| 119 expect(chain.traces, hasLength(2)); |
| 120 }); |
| 121 |
| 122 test('passed to a stream controller', () async { |
| 123 var trace = new Trace.current(); |
| 124 var chain = await captureFuture(() { |
| 125 inMicrotask(() => controllerErrorStream(trace).listen(null)); |
| 126 }); |
| 127 |
| 128 expect(chain.traces, hasLength(3)); |
| 129 expect(chain.traces.first.toString(), equals(trace.toString())); |
| 130 }); |
| 131 |
| 132 test('passed to a stream controller with no stack trace', () async { |
| 133 var chain = await captureFuture(() { |
| 134 inMicrotask(() => controllerErrorStream().listen(null)); |
| 135 }); |
| 136 |
| 137 expect(chain.traces, hasLength(2)); |
| 138 }); |
| 139 |
| 140 test('and relays them to the parent zone', () { |
| 141 var completer = new Completer(); |
| 142 |
| 143 runZoned(() { |
| 144 Chain.capture(() { |
| 145 inMicrotask(() => throw 'error'); |
| 146 }, onError: (error, chain) { |
| 147 expect(error, equals('error')); |
| 148 expect(chain.traces, hasLength(2)); |
| 149 throw error; |
| 150 }); |
| 151 }, onError: (error, chain) { |
| 152 try { |
| 153 expect(error, equals('error')); |
| 154 expect(chain, new isInstanceOf<Chain>()); |
| 155 expect(chain.traces, hasLength(2)); |
| 156 completer.complete(); |
| 157 } catch (error, stackTrace) { |
| 158 completer.completeError(error, stackTrace); |
| 159 } |
| 160 }); |
| 161 |
| 162 return completer.future; |
| 163 }); |
| 164 }); |
| 165 |
| 166 test('capture() without onError passes exceptions to parent zone', () { |
| 167 var completer = new Completer(); |
| 168 |
| 169 runZoned(() { |
| 170 Chain.capture(() => inMicrotask(() => throw 'error')); |
| 171 }, onError: (error, chain) { |
| 172 try { |
| 173 expect(error, equals('error')); |
| 174 expect(chain, new isInstanceOf<Chain>()); |
| 175 expect(chain.traces, hasLength(2)); |
| 176 completer.complete(); |
| 177 } catch (error, stackTrace) { |
| 178 completer.completeError(error, stackTrace); |
| 179 } |
| 180 }); |
| 181 |
| 182 return completer.future; |
| 183 }); |
| 184 |
| 185 group('current() within capture()', () { |
| 186 test('called in a microtask', () async { |
| 187 var completer = new Completer(); |
| 188 Chain.capture(() { |
| 189 inMicrotask(() => completer.complete(new Chain.current())); |
| 190 }); |
| 191 |
| 192 var chain = await completer.future; |
| 193 expect(chain.traces, hasLength(2)); |
| 194 }); |
| 195 |
| 196 test('called in a one-shot timer', () async { |
| 197 var completer = new Completer(); |
| 198 Chain.capture(() { |
| 199 inOneShotTimer(() => completer.complete(new Chain.current())); |
| 200 }); |
| 201 |
| 202 var chain = await completer.future; |
| 203 expect(chain.traces, hasLength(2)); |
| 204 }); |
| 205 |
| 206 test('called in a periodic timer', () async { |
| 207 var completer = new Completer(); |
| 208 Chain.capture(() { |
| 209 inPeriodicTimer(() => completer.complete(new Chain.current())); |
| 210 }); |
| 211 |
| 212 var chain = await completer.future; |
| 213 expect(chain.traces, hasLength(2)); |
| 214 }); |
| 215 |
| 216 test('called in a nested series of asynchronous operations', () async { |
| 217 var completer = new Completer(); |
| 218 Chain.capture(() { |
| 219 inPeriodicTimer(() { |
| 220 inOneShotTimer(() { |
| 221 inMicrotask(() => completer.complete(new Chain.current())); |
| 222 }); |
| 223 }); |
| 224 }); |
| 225 |
| 226 var chain = await completer.future; |
| 227 expect(chain.traces, hasLength(4)); |
| 228 }); |
| 229 |
| 230 test('called in a long future chain', () async { |
| 231 var completer = new Completer(); |
| 232 Chain.capture(() { |
| 233 inFutureChain(() => completer.complete(new Chain.current())); |
| 234 }); |
| 235 |
| 236 var chain = await completer.future; |
| 237 expect(chain.traces, hasLength(2)); |
| 238 }); |
| 239 }); |
| 240 |
| 241 test('current() outside of capture() returns a chain wrapping the current ' |
| 242 'trace', () { |
| 243 // The test runner runs all tests with chains enabled, so to test without we |
| 244 // have to do some zone munging. |
| 245 return runZoned(() async { |
| 246 var completer = new Completer(); |
| 247 inMicrotask(() => completer.complete(new Chain.current())); |
| 248 |
| 249 var chain = await completer.future; |
| 250 // Since the chain wasn't loaded within [Chain.capture], the full stack |
| 251 // chain isn't available and it just returns the current stack when |
| 252 // called. |
| 253 expect(chain.traces, hasLength(1)); |
| 254 }, zoneValues: {#stack_trace.stack_zone.spec: null}); |
| 255 }); |
| 256 |
| 257 group('forTrace() within capture()', () { |
| 258 test('called for a stack trace from a microtask', () async { |
| 259 var chain = await Chain.capture(() { |
| 260 return chainForTrace(inMicrotask, () => throw 'error'); |
| 261 }); |
| 262 |
| 263 // Because [chainForTrace] has to set up a future chain to capture the |
| 264 // stack trace while still showing it to the zone specification, it adds |
| 265 // an additional level of async nesting and so an additional trace. |
| 266 expect(chain.traces, hasLength(3)); |
| 267 }); |
| 268 |
| 269 test('called for a stack trace from a one-shot timer', () async { |
| 270 var chain = await Chain.capture(() { |
| 271 return chainForTrace(inOneShotTimer, () => throw 'error'); |
| 272 }); |
| 273 |
| 274 expect(chain.traces, hasLength(3)); |
| 275 }); |
| 276 |
| 277 test('called for a stack trace from a periodic timer', () async { |
| 278 var chain = await Chain.capture(() { |
| 279 return chainForTrace(inPeriodicTimer, () => throw 'error'); |
| 280 }); |
| 281 |
| 282 expect(chain.traces, hasLength(3)); |
| 283 }); |
| 284 |
| 285 test('called for a stack trace from a nested series of asynchronous ' |
| 286 'operations', () async { |
| 287 var chain = await Chain.capture(() { |
| 288 return chainForTrace((callback) { |
| 289 inPeriodicTimer(() => inOneShotTimer(() => inMicrotask(callback))); |
| 290 }, () => throw 'error'); |
| 291 }); |
| 292 |
| 293 expect(chain.traces, hasLength(5)); |
| 294 }); |
| 295 |
| 296 test('called for a stack trace from a long future chain', () async { |
| 297 var chain = await Chain.capture(() { |
| 298 return chainForTrace(inFutureChain, () => throw 'error'); |
| 299 }); |
| 300 |
| 301 expect(chain.traces, hasLength(3)); |
| 302 }); |
| 303 |
| 304 test('called for an unregistered stack trace returns a chain wrapping that ' |
| 305 'trace', () { |
| 306 var trace; |
| 307 var chain = Chain.capture(() { |
| 308 try { |
| 309 throw 'error'; |
| 310 } catch (_, stackTrace) { |
| 311 trace = stackTrace; |
| 312 return new Chain.forTrace(stackTrace); |
| 313 } |
| 314 }); |
| 315 |
| 316 expect(chain.traces, hasLength(1)); |
| 317 expect(chain.traces.first.toString(), |
| 318 equals(new Trace.from(trace).toString())); |
| 319 }); |
| 320 }); |
| 321 |
| 322 test('forTrace() outside of capture() returns a chain wrapping the given ' |
| 323 'trace', () { |
| 324 var trace; |
| 325 var chain = Chain.capture(() { |
| 326 try { |
| 327 throw 'error'; |
| 328 } catch (_, stackTrace) { |
| 329 trace = stackTrace; |
| 330 return new Chain.forTrace(stackTrace); |
| 331 } |
| 332 }); |
| 333 |
| 334 expect(chain.traces, hasLength(1)); |
| 335 expect(chain.traces.first.toString(), |
| 336 equals(new Trace.from(trace).toString())); |
| 337 }); |
| 338 } |
OLD | NEW |