OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Support for writing Dart unit tests. | 6 * Support for writing Dart unit tests. |
7 * | 7 * |
8 * For information on installing and importing this library, see the | 8 * For information on installing and importing this library, see the |
9 * [unittest package on pub.dartlang.org] | 9 * [unittest package on pub.dartlang.org] |
10 * (http://pub.dartlang.org/packages/unittest). | 10 * (http://pub.dartlang.org/packages/unittest). |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 _testCases.clear(); | 292 _testCases.clear(); |
293 } | 293 } |
294 ++_soloNestingLevel; | 294 ++_soloNestingLevel; |
295 try { | 295 try { |
296 test(spec, body); | 296 test(spec, body); |
297 } finally { | 297 } finally { |
298 --_soloNestingLevel; | 298 --_soloNestingLevel; |
299 } | 299 } |
300 } | 300 } |
301 | 301 |
| 302 /** Sentinel value for [_SpreadArgsHelper]. */ |
| 303 class _Sentinel { |
| 304 const _Sentinel(); |
| 305 } |
| 306 |
302 /** | 307 /** |
303 * Indicate that [callback] is expected to be called a [count] number of times | 308 * Indicate that [callback] is expected to be called a [count] number of times |
304 * (by default 1). The unittest framework will wait for the callback to run the | 309 * (by default 1). The unittest framework will wait for the callback to run the |
305 * specified [count] times before it continues with the following test. Using | 310 * specified [count] times before it continues with the following test. Using |
306 * [expectAsync0] will also ensure that errors that occur within [callback] are | 311 * [expectAsync0] will also ensure that errors that occur within [callback] are |
307 * tracked and reported. [callback] should take 0 positional arguments (named | 312 * tracked and reported. [callback] should take 0 positional arguments (named |
308 * arguments are not supported). [id] can be used to provide more | 313 * arguments are not supported). [id] can be used to provide more |
309 * descriptive error messages if the callback is called more often than | 314 * descriptive error messages if the callback is called more often than |
310 * expected. [max] can be used to specify an upper bound on the number of | 315 * expected. [max] can be used to specify an upper bound on the number of |
311 * calls; if this is exceeded the test will fail (or be marked as in error if | 316 * calls; if this is exceeded the test will fail (or be marked as in error if |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 | 364 |
360 /** | 365 /** |
361 * Like [expectAsyncUntil0] but [callback] should take 2 positional arguments. | 366 * Like [expectAsyncUntil0] but [callback] should take 2 positional arguments. |
362 */ | 367 */ |
363 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | 368 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
364 Function expectAsyncUntil2(Function callback, Function isDone, {String id}) { | 369 Function expectAsyncUntil2(Function callback, Function isDone, {String id}) { |
365 return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke2; | 370 return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke2; |
366 } | 371 } |
367 | 372 |
368 /** | 373 /** |
369 * *Deprecated* | 374 * Wraps the [callback] in a new function and returns that function. The new |
370 * | 375 * function will be able to handle exceptions by directing them to the correct |
371 * All tests are now run an isolated [Zone]. | 376 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that |
372 * | 377 * might optionally be called but may never be called during the test. |
373 * You can safely remove calls to this method. | 378 * [callback] should take 0 positional arguments (named arguments are not |
| 379 * supported). [id] can be used to identify the callback in error |
| 380 * messages (for example if it is called after the test case is complete). |
374 */ | 381 */ |
375 @deprecated | 382 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
376 Function protectAsync0(Function callback, {String id}) { | 383 Function protectAsync0(Function callback, {String id}) { |
377 return callback; | 384 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke0; |
378 } | 385 } |
379 | 386 |
380 /** | 387 /** |
381 * *Deprecated* | 388 * Like [protectAsync0] but [callback] should take 1 positional argument. |
382 * | |
383 * All tests are now run an isolated [Zone]. | |
384 * | |
385 * You can safely remove calls to this method. | |
386 */ | 389 */ |
387 @deprecated | 390 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
388 Function protectAsync1(Function callback, {String id}) { | 391 Function protectAsync1(Function callback, {String id}) { |
389 return callback; | 392 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke1; |
390 } | 393 } |
391 | 394 |
392 /** | 395 /** |
393 * *Deprecated* | 396 * Like [protectAsync0] but [callback] should take 2 positional arguments. |
394 * | |
395 * All tests are now run an isolated [Zone]. | |
396 * | |
397 * You can safely remove calls to this method. | |
398 */ | 397 */ |
399 @deprecated | 398 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
400 Function protectAsync2(Function callback, {String id}) { | 399 Function protectAsync2(Function callback, {String id}) { |
401 return callback; | 400 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
402 } | 401 } |
403 | 402 |
404 /** | 403 /** |
405 * Creates a new named group of tests. Calls to group() or test() within the | 404 * Creates a new named group of tests. Calls to group() or test() within the |
406 * body of the function passed to this will inherit this group's description. | 405 * body of the function passed to this will inherit this group's description. |
407 */ | 406 */ |
408 void group(String description, void body()) { | 407 void group(String description, void body()) { |
409 ensureInitialized(); | 408 ensureInitialized(); |
410 _requireNotRunning(); | 409 _requireNotRunning(); |
411 _currentContext = new _GroupContext(_currentContext, description); | 410 _currentContext = new _GroupContext(_currentContext, description); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 void handleExternalError(e, String message, [stack]) { | 476 void handleExternalError(e, String message, [stack]) { |
478 var msg = '$message\nCaught $e'; | 477 var msg = '$message\nCaught $e'; |
479 | 478 |
480 if (currentTestCase != null) { | 479 if (currentTestCase != null) { |
481 currentTestCase._error(msg, stack); | 480 currentTestCase._error(msg, stack); |
482 } else { | 481 } else { |
483 _uncaughtErrorMessage = "$msg: $stack"; | 482 _uncaughtErrorMessage = "$msg: $stack"; |
484 } | 483 } |
485 } | 484 } |
486 | 485 |
| 486 void rerunTests() { |
| 487 _uncaughtErrorMessage = null; |
| 488 _initialized = true; // We don't want to reset the test array. |
| 489 runTests(); |
| 490 } |
| 491 |
487 /** | 492 /** |
488 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a | 493 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a |
489 * predicate function. This is different to enabling/disabling tests | 494 * predicate function. This is different to enabling/disabling tests |
490 * in that it removes the tests completely. | 495 * in that it removes the tests completely. |
491 */ | 496 */ |
492 void filterTests(testFilter) { | 497 void filterTests(testFilter) { |
493 var filterFunction; | 498 var filterFunction; |
494 if (testFilter is String) { | 499 if (testFilter is String) { |
495 RegExp re = new RegExp(testFilter); | 500 RegExp re = new RegExp(testFilter); |
496 filterFunction = (t) => re.hasMatch(t.description); | 501 filterFunction = (t) => re.hasMatch(t.description); |
497 } else if (testFilter is RegExp) { | 502 } else if (testFilter is RegExp) { |
498 filterFunction = (t) => testFilter.hasMatch(t.description); | 503 filterFunction = (t) => testFilter.hasMatch(t.description); |
499 } else if (testFilter is Function) { | 504 } else if (testFilter is Function) { |
500 filterFunction = testFilter; | 505 filterFunction = testFilter; |
501 } | 506 } |
502 _testCases.retainWhere(filterFunction); | 507 _testCases.retainWhere(filterFunction); |
503 } | 508 } |
504 | 509 |
505 /** Runs all queued tests, one at a time. */ | 510 /** Runs all queued tests, one at a time. */ |
506 void runTests() { | 511 void runTests() { |
507 _requireNotRunning(); | 512 _requireNotRunning(); |
508 _ensureInitialized(false); | 513 _ensureInitialized(false); |
509 _currentTestCaseIndex = 0; | 514 _currentTestCaseIndex = 0; |
510 _config.onStart(); | 515 _config.onStart(); |
511 _runTest(); | 516 _runTest(); |
512 } | 517 } |
513 | 518 |
514 /** | 519 /** |
515 * *Deprecated* | 520 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 521 * passed to the corresponding test. |
516 * | 522 * |
517 * All tests are now run an isolated [Zone]. | 523 * The value returned by [tryBody] (if any) is returned by [guardAsync]. |
518 * | |
519 * You can safely remove calls to this method. | |
520 */ | 524 */ |
521 @deprecated | |
522 guardAsync(Function tryBody) { | 525 guardAsync(Function tryBody) { |
523 return tryBody(); | 526 return _guardAsync(tryBody, null, currentTestCase); |
| 527 } |
| 528 |
| 529 _guardAsync(Function tryBody, Function finallyBody, TestCase testCase) { |
| 530 assert(testCase != null); |
| 531 try { |
| 532 return tryBody(); |
| 533 } catch (e, trace) { |
| 534 _registerException(testCase, e, trace); |
| 535 } finally { |
| 536 if (finallyBody != null) finallyBody(); |
| 537 } |
524 } | 538 } |
525 | 539 |
526 /** | 540 /** |
527 * Registers that an exception was caught for the current test. | 541 * Registers that an exception was caught for the current test. |
528 */ | 542 */ |
529 void registerException(e, [trace]) { | 543 void registerException(e, [trace]) { |
530 _registerException(currentTestCase, e, trace); | 544 _registerException(currentTestCase, e, trace); |
531 } | 545 } |
532 | 546 |
533 /** | 547 /** |
(...skipping 10 matching lines...) Expand all Loading... |
544 | 558 |
545 /** | 559 /** |
546 * Runs the next test. | 560 * Runs the next test. |
547 */ | 561 */ |
548 void _runTest() { | 562 void _runTest() { |
549 if (_currentTestCaseIndex >= testCases.length) { | 563 if (_currentTestCaseIndex >= testCases.length) { |
550 assert(_currentTestCaseIndex == testCases.length); | 564 assert(_currentTestCaseIndex == testCases.length); |
551 _completeTests(); | 565 _completeTests(); |
552 } else { | 566 } else { |
553 var testCase = testCases[_currentTestCaseIndex]; | 567 var testCase = testCases[_currentTestCaseIndex]; |
554 Future f = runZoned(testCase._run, onError: (error, stack) { | 568 Future f = _guardAsync(testCase._run, null, testCase); |
555 // TODO(kevmoo) Do a better job of flagging these are async errors. | |
556 // https://code.google.com/p/dart/issues/detail?id=16530 | |
557 _registerException(testCase, error, stack); | |
558 }); | |
559 | |
560 var timeout = unittestConfiguration.timeout; | 569 var timeout = unittestConfiguration.timeout; |
561 | 570 |
562 Timer timer; | 571 Timer timer; |
563 if (timeout != null) { | 572 if (timeout != null) { |
564 try { | 573 try { |
565 timer = new Timer(timeout, () { | 574 timer = new Timer(timeout, () { |
566 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); | 575 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); |
567 _nextTestCase(); | 576 _nextTestCase(); |
568 }); | 577 }); |
569 } on UnsupportedError catch (e) { | 578 } on UnsupportedError catch (e) { |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
702 | 711 |
703 if (!filterStacks) return trace; | 712 if (!filterStacks) return trace; |
704 | 713 |
705 // Format the stack trace by removing everything above TestCase._runTest, | 714 // Format the stack trace by removing everything above TestCase._runTest, |
706 // which is usually going to be irrelevant. Also fold together unittest and | 715 // which is usually going to be irrelevant. Also fold together unittest and |
707 // core library calls so only the function the user called is visible. | 716 // core library calls so only the function the user called is visible. |
708 return new Trace(trace.frames.takeWhile((frame) { | 717 return new Trace(trace.frames.takeWhile((frame) { |
709 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; | 718 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; |
710 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); | 719 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); |
711 } | 720 } |
OLD | NEW |