| 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 * A library for writing dart unit tests. | 6 * A library for writing dart unit tests. |
| 7 * | 7 * |
| 8 * To import this library, install the | 8 * To import this library, install the |
| 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub | 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub |
| 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) | 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 204 // TODO(nweiz): present an unmodifiable view of this once issue 8321 is fixed. | 204 // TODO(nweiz): present an unmodifiable view of this once issue 8321 is fixed. |
| 205 /** Tests executed in this suite. */ | 205 /** Tests executed in this suite. */ |
| 206 final List<TestCase> testCases = new List<TestCase>(); | 206 final List<TestCase> testCases = new List<TestCase>(); |
| 207 | 207 |
| 208 /** Setup function called before each test in a group */ | 208 /** Setup function called before each test in a group */ |
| 209 Function _testSetup; | 209 Function _testSetup; |
| 210 | 210 |
| 211 /** Teardown function called after each test in a group */ | 211 /** Teardown function called after each test in a group */ |
| 212 Function _testTeardown; | 212 Function _testTeardown; |
| 213 | 213 |
| 214 TestCase _currentTestCase = null; | 214 int _currentTestCaseIndex = 0; |
| 215 | 215 |
| 216 /** [TestCase] currently being executed. */ | 216 /** [TestCase] currently being executed. */ |
| 217 TestCase get currentTestCase => _currentTestCase; | 217 TestCase get currentTestCase => |
| 218 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 219 ? testCases[_currentTestCaseIndex] |
| 220 : null; |
| 218 | 221 |
| 219 /** Whether the framework is in an initialized state. */ | 222 /** Whether the framework is in an initialized state. */ |
| 220 bool _initialized = false; | 223 bool _initialized = false; |
| 221 | 224 |
| 222 String _uncaughtErrorMessage = null; | 225 String _uncaughtErrorMessage = null; |
| 223 | 226 |
| 224 /** Test case result strings. */ | 227 /** Test case result strings. */ |
| 225 // TODO(gram) we should change these constants to use a different string | 228 // TODO(gram) we should change these constants to use a different string |
| 226 // (so that writing 'FAIL' in the middle of a test doesn't | 229 // (so that writing 'FAIL' in the middle of a test doesn't |
| 227 // imply that the test fails). We can't do it without also changing | 230 // imply that the test fails). We can't do it without also changing |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 } | 285 } |
| 283 | 286 |
| 284 /** Simulates spread arguments using named arguments. */ | 287 /** Simulates spread arguments using named arguments. */ |
| 285 // TODO(sigmund): remove this class and simply use a closure with named | 288 // TODO(sigmund): remove this class and simply use a closure with named |
| 286 // arguments (if still applicable). | 289 // arguments (if still applicable). |
| 287 class _SpreadArgsHelper { | 290 class _SpreadArgsHelper { |
| 288 final Function callback; | 291 final Function callback; |
| 289 final int minExpectedCalls; | 292 final int minExpectedCalls; |
| 290 final int maxExpectedCalls; | 293 final int maxExpectedCalls; |
| 291 final Function isDone; | 294 final Function isDone; |
| 292 final TestCase testCase; | 295 final int testNum; |
| 293 final String id; | 296 final String id; |
| 294 int actualCalls = 0; | 297 int actualCalls = 0; |
| 298 TestCase testCase; |
| 295 bool complete; | 299 bool complete; |
| 296 static const sentinel = const _Sentinel(); | 300 static const sentinel = const _Sentinel(); |
| 297 | 301 |
| 298 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, | 302 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, |
| 299 Function isDone, String id) | 303 Function isDone, String id) |
| 300 : this.callback = callback, | 304 : this.callback = callback, |
| 301 minExpectedCalls = minExpected, | 305 minExpectedCalls = minExpected, |
| 302 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) | 306 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
| 303 ? minExpected | 307 ? minExpected |
| 304 : maxExpected, | 308 : maxExpected, |
| 305 this.isDone = isDone, | 309 this.isDone = isDone, |
| 306 this.testCase = currentTestCase, | 310 testNum = _currentTestCaseIndex, |
| 307 this.id = _makeCallbackId(id, callback) { | 311 this.id = _makeCallbackId(id, callback) { |
| 308 if(testCase == null) { | 312 ensureInitialized(); |
| 309 throw new StateError("No valid test, did you forget to run your test " | 313 if (!(_currentTestCaseIndex >= 0 && |
| 310 "inside a call to test()?"); | 314 _currentTestCaseIndex < testCases.length && |
| 315 testCases[_currentTestCaseIndex] != null)) { |
| 316 print("No valid test, did you forget to run your test inside a call " |
| 317 "to test()?"); |
| 311 } | 318 } |
| 312 | 319 assert(_currentTestCaseIndex >= 0 && |
| 320 _currentTestCaseIndex < testCases.length && |
| 321 testCases[_currentTestCaseIndex] != null); |
| 322 testCase = testCases[_currentTestCaseIndex]; |
| 313 if (isDone != null || minExpected > 0) { | 323 if (isDone != null || minExpected > 0) { |
| 314 testCase._callbackFunctionsOutstanding++; | 324 testCase._callbackFunctionsOutstanding++; |
| 315 complete = false; | 325 complete = false; |
| 316 } else { | 326 } else { |
| 317 complete = true; | 327 complete = true; |
| 318 } | 328 } |
| 319 } | 329 } |
| 320 | 330 |
| 321 static _makeCallbackId(String id, Function callback) { | 331 static _makeCallbackId(String id, Function callback) { |
| 322 // Try to create a reasonable id. | 332 // Try to create a reasonable id. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 return callback(arg0, arg1, arg2); | 396 return callback(arg0, arg1, arg2); |
| 387 } else if (arg4 == sentinel) { | 397 } else if (arg4 == sentinel) { |
| 388 return callback(arg0, arg1, arg2, arg3); | 398 return callback(arg0, arg1, arg2, arg3); |
| 389 } else { | 399 } else { |
| 390 testCase.error( | 400 testCase.error( |
| 391 'unittest lib does not support callbacks with more than' | 401 'unittest lib does not support callbacks with more than' |
| 392 ' 4 arguments.', | 402 ' 4 arguments.', |
| 393 ''); | 403 ''); |
| 394 } | 404 } |
| 395 }, | 405 }, |
| 396 after, testCase); | 406 after, testNum); |
| 397 } | 407 } |
| 398 | 408 |
| 399 invoke0() { | 409 invoke0() { |
| 400 return _guardAsync( | 410 return _guardAsync( |
| 401 () { | 411 () { |
| 402 if (shouldCallBack()) { | 412 if (shouldCallBack()) { |
| 403 return callback(); | 413 return callback(); |
| 404 } | 414 } |
| 405 }, | 415 }, |
| 406 after, testCase); | 416 after, testNum); |
| 407 } | 417 } |
| 408 | 418 |
| 409 invoke1(arg1) { | 419 invoke1(arg1) { |
| 410 return _guardAsync( | 420 return _guardAsync( |
| 411 () { | 421 () { |
| 412 if (shouldCallBack()) { | 422 if (shouldCallBack()) { |
| 413 return callback(arg1); | 423 return callback(arg1); |
| 414 } | 424 } |
| 415 }, | 425 }, |
| 416 after, testCase); | 426 after, testNum); |
| 417 } | 427 } |
| 418 | 428 |
| 419 invoke2(arg1, arg2) { | 429 invoke2(arg1, arg2) { |
| 420 return _guardAsync( | 430 return _guardAsync( |
| 421 () { | 431 () { |
| 422 if (shouldCallBack()) { | 432 if (shouldCallBack()) { |
| 423 return callback(arg1, arg2); | 433 return callback(arg1, arg2); |
| 424 } | 434 } |
| 425 }, | 435 }, |
| 426 after, testCase); | 436 after, testNum); |
| 427 } | 437 } |
| 428 } | 438 } |
| 429 | 439 |
| 430 /** | 440 /** |
| 431 * Indicate that [callback] is expected to be called a [count] number of times | 441 * Indicate that [callback] is expected to be called a [count] number of times |
| 432 * (by default 1). The unittest framework will wait for the callback to run the | 442 * (by default 1). The unittest framework will wait for the callback to run the |
| 433 * specified [count] times before it continues with the following test. Using | 443 * specified [count] times before it continues with the following test. Using |
| 444 * [_expectAsync] will also ensure that errors that occur within [callback] are |
| 445 * tracked and reported. [callback] should take between 0 and 4 positional |
| 446 * arguments (named arguments are not supported here). [id] can be used |
| 447 * to provide more descriptive error messages if the callback is called more |
| 448 * often than expected. |
| 449 */ |
| 450 Function _expectAsync(Function callback, |
| 451 {int count: 1, int max: 0, String id}) { |
| 452 return new _SpreadArgsHelper(callback, count, max, null, id).invoke; |
| 453 } |
| 454 |
| 455 /** |
| 456 * Indicate that [callback] is expected to be called a [count] number of times |
| 457 * (by default 1). The unittest framework will wait for the callback to run the |
| 458 * specified [count] times before it continues with the following test. Using |
| 434 * [expectAsync0] will also ensure that errors that occur within [callback] are | 459 * [expectAsync0] will also ensure that errors that occur within [callback] are |
| 435 * tracked and reported. [callback] should take 0 positional arguments (named | 460 * tracked and reported. [callback] should take 0 positional arguments (named |
| 436 * arguments are not supported). [id] can be used to provide more | 461 * arguments are not supported). [id] can be used to provide more |
| 437 * descriptive error messages if the callback is called more often than | 462 * descriptive error messages if the callback is called more often than |
| 438 * expected. [max] can be used to specify an upper bound on the number of | 463 * expected. [max] can be used to specify an upper bound on the number of |
| 439 * calls; if this is exceeded the test will fail (or be marked as in error if | 464 * calls; if this is exceeded the test will fail (or be marked as in error if |
| 440 * it was already complete). A value of 0 for [max] (the default) will set | 465 * it was already complete). A value of 0 for [max] (the default) will set |
| 441 * the upper bound to the same value as [count]; i.e. the callback should be | 466 * the upper bound to the same value as [count]; i.e. the callback should be |
| 442 * called exactly [count] times. A value of -1 for [max] will mean no upper | 467 * called exactly [count] times. A value of -1 for [max] will mean no upper |
| 443 * bound. | 468 * bound. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 457 | 482 |
| 458 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */ | 483 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */ |
| 459 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | 484 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
| 460 Function expectAsync2(Function callback, | 485 Function expectAsync2(Function callback, |
| 461 {int count: 1, int max: 0, String id}) { | 486 {int count: 1, int max: 0, String id}) { |
| 462 return new _SpreadArgsHelper(callback, count, max, null, id).invoke2; | 487 return new _SpreadArgsHelper(callback, count, max, null, id).invoke2; |
| 463 } | 488 } |
| 464 | 489 |
| 465 /** | 490 /** |
| 466 * Indicate that [callback] is expected to be called until [isDone] returns | 491 * Indicate that [callback] is expected to be called until [isDone] returns |
| 492 * true. The unittest framework checks [isDone] after each callback and only |
| 493 * when it returns true will it continue with the following test. Using |
| 494 * [expectAsyncUntil] will also ensure that errors that occur within |
| 495 * [callback] are tracked and reported. [callback] should take between 0 and |
| 496 * 4 positional arguments (named arguments are not supported). [id] can be |
| 497 * used to identify the callback in error messages (for example if it is called |
| 498 * after the test case is complete). |
| 499 */ |
| 500 Function _expectAsyncUntil(Function callback, Function isDone, {String id}) { |
| 501 return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke; |
| 502 } |
| 503 |
| 504 /** |
| 505 * Indicate that [callback] is expected to be called until [isDone] returns |
| 467 * true. The unittest framework check [isDone] after each callback and only | 506 * true. The unittest framework check [isDone] after each callback and only |
| 468 * when it returns true will it continue with the following test. Using | 507 * when it returns true will it continue with the following test. Using |
| 469 * [expectAsyncUntil0] will also ensure that errors that occur within | 508 * [expectAsyncUntil0] will also ensure that errors that occur within |
| 470 * [callback] are tracked and reported. [callback] should take 0 positional | 509 * [callback] are tracked and reported. [callback] should take 0 positional |
| 471 * arguments (named arguments are not supported). [id] can be used to | 510 * arguments (named arguments are not supported). [id] can be used to |
| 472 * identify the callback in error messages (for example if it is called | 511 * identify the callback in error messages (for example if it is called |
| 473 * after the test case is complete). | 512 * after the test case is complete). |
| 474 */ | 513 */ |
| 475 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | 514 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
| 476 Function expectAsyncUntil0(Function callback, Function isDone, {String id}) { | 515 Function expectAsyncUntil0(Function callback, Function isDone, {String id}) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 491 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | 530 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
| 492 Function expectAsyncUntil2(Function callback, Function isDone, {String id}) { | 531 Function expectAsyncUntil2(Function callback, Function isDone, {String id}) { |
| 493 return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke2; | 532 return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke2; |
| 494 } | 533 } |
| 495 | 534 |
| 496 /** | 535 /** |
| 497 * Wraps the [callback] in a new function and returns that function. The new | 536 * Wraps the [callback] in a new function and returns that function. The new |
| 498 * function will be able to handle exceptions by directing them to the correct | 537 * function will be able to handle exceptions by directing them to the correct |
| 499 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that | 538 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that |
| 500 * might optionally be called but may never be called during the test. | 539 * might optionally be called but may never be called during the test. |
| 540 * [callback] should take between 0 and 4 positional arguments (named arguments |
| 541 * are not supported). [id] can be used to identify the callback in error |
| 542 * messages (for example if it is called after the test case is complete). |
| 543 */ |
| 544 Function _protectAsync(Function callback, {String id}) { |
| 545 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke; |
| 546 } |
| 547 |
| 548 /** |
| 549 * Wraps the [callback] in a new function and returns that function. The new |
| 550 * function will be able to handle exceptions by directing them to the correct |
| 551 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that |
| 552 * might optionally be called but may never be called during the test. |
| 501 * [callback] should take 0 positional arguments (named arguments are not | 553 * [callback] should take 0 positional arguments (named arguments are not |
| 502 * supported). [id] can be used to identify the callback in error | 554 * supported). [id] can be used to identify the callback in error |
| 503 * messages (for example if it is called after the test case is complete). | 555 * messages (for example if it is called after the test case is complete). |
| 504 */ | 556 */ |
| 505 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | 557 // TODO(sigmund): deprecate this API when issue 2706 is fixed. |
| 506 Function protectAsync0(Function callback, {String id}) { | 558 Function protectAsync0(Function callback, {String id}) { |
| 507 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke0; | 559 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke0; |
| 508 } | 560 } |
| 509 | 561 |
| 510 /** | 562 /** |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 | 613 |
| 562 /** | 614 /** |
| 563 * Register a [setUp] function for a test [group]. This function will | 615 * Register a [setUp] function for a test [group]. This function will |
| 564 * be called before each test in the group is run. Note that if groups | 616 * be called before each test in the group is run. Note that if groups |
| 565 * are nested only the most locally scoped [setUpTest] function will be run. | 617 * are nested only the most locally scoped [setUpTest] function will be run. |
| 566 * [setUp] and [tearDown] should be called within the [group] before any | 618 * [setUp] and [tearDown] should be called within the [group] before any |
| 567 * calls to [test]. The [setupTest] function can be asynchronous; in this | 619 * calls to [test]. The [setupTest] function can be asynchronous; in this |
| 568 * case it must return a [Future]. | 620 * case it must return a [Future]. |
| 569 */ | 621 */ |
| 570 void setUp(Function setupTest) { | 622 void setUp(Function setupTest) { |
| 571 _requireNotRunning(); | |
| 572 _testSetup = setupTest; | 623 _testSetup = setupTest; |
| 573 } | 624 } |
| 574 | 625 |
| 575 /** | 626 /** |
| 576 * Register a [tearDown] function for a test [group]. This function will | 627 * Register a [tearDown] function for a test [group]. This function will |
| 577 * be called after each test in the group is run. Note that if groups | 628 * be called after each test in the group is run. Note that if groups |
| 578 * are nested only the most locally scoped [teardownTest] function will be run. | 629 * are nested only the most locally scoped [teardownTest] function will be run. |
| 579 * [setUp] and [tearDown] should be called within the [group] before any | 630 * [setUp] and [tearDown] should be called within the [group] before any |
| 580 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 631 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 581 * case it must return a [Future]. | 632 * case it must return a [Future]. |
| 582 */ | 633 */ |
| 583 void tearDown(Function teardownTest) { | 634 void tearDown(Function teardownTest) { |
| 584 _requireNotRunning(); | |
| 585 _testTeardown = teardownTest; | 635 _testTeardown = teardownTest; |
| 586 } | 636 } |
| 587 | 637 |
| 638 /** Advance to the next test case. */ |
| 639 void _nextTestCase() { |
| 640 _defer(() { |
| 641 _currentTestCaseIndex++; |
| 642 _nextBatch(); |
| 643 }); |
| 644 } |
| 645 |
| 588 /** | 646 /** |
| 589 * Utility function that can be used to notify the test framework that an | 647 * Utility function that can be used to notify the test framework that an |
| 590 * error was caught outside of this library. | 648 * error was caught outside of this library. |
| 591 */ | 649 */ |
| 592 void _reportTestError(String msg, String trace) { | 650 void _reportTestError(String msg, String trace) { |
| 593 if (currentTestCase != null) { | 651 if (_currentTestCaseIndex < testCases.length) { |
| 594 currentTestCase.error(msg, trace); | 652 final testCase = testCases[_currentTestCaseIndex]; |
| 653 testCase.error(msg, trace); |
| 595 } else { | 654 } else { |
| 596 _uncaughtErrorMessage = "$msg: $trace"; | 655 _uncaughtErrorMessage = "$msg: $trace"; |
| 597 } | 656 } |
| 598 } | 657 } |
| 599 | 658 |
| 659 /** |
| 660 * Runs [callback] at the end of the event loop. Note that we don't wrap |
| 661 * the callback in guardAsync; this is for test framework functions which |
| 662 * should not be throwing unexpected exceptions that end up failing test |
| 663 * cases! Furthermore, we need the final exception to be thrown but not |
| 664 * caught by the test framework if any test cases failed. However, tests |
| 665 * that make use of a similar defer function *should* wrap the callback |
| 666 * (as we do in unitttest_test.dart). |
| 667 */ |
| 668 _defer(void callback()) { |
| 669 (new Future.value()).then((_) => callback()); |
| 670 } |
| 671 |
| 600 void rerunTests() { | 672 void rerunTests() { |
| 601 assert(_uncaughtErrorMessage == null); | 673 _uncaughtErrorMessage = null; |
| 674 _initialized = true; // We don't want to reset the test array. |
| 602 runTests(); | 675 runTests(); |
| 603 } | 676 } |
| 604 | 677 |
| 605 /** | 678 /** |
| 606 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a | 679 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a |
| 607 * predicate function. This is different to enabling/disabling tests | 680 * predicate function. This is different to enabling/disabling tests |
| 608 * in that it removes the tests completely. | 681 * in that it removes the tests completely. |
| 609 */ | 682 */ |
| 610 void filterTests(testFilter) { | 683 void filterTests(testFilter) { |
| 611 _requireNotRunning(); | 684 var filterFunction; |
| 612 Function filterFunction; | |
| 613 if (testFilter is String) { | 685 if (testFilter is String) { |
| 614 RegExp re = new RegExp(testFilter); | 686 RegExp re = new RegExp(testFilter); |
| 615 filterFunction = (t) => re.hasMatch(t.description); | 687 filterFunction = (t) => re.hasMatch(t.description); |
| 616 } else if (testFilter is RegExp) { | 688 } else if (testFilter is RegExp) { |
| 617 filterFunction = (t) => testFilter.hasMatch(t.description); | 689 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 618 } else if (testFilter is Function) { | 690 } else if (testFilter is Function) { |
| 619 filterFunction = testFilter; | 691 filterFunction = testFilter; |
| 620 } | 692 } |
| 621 testCases.retainWhere(filterFunction); | 693 testCases.retainWhere(filterFunction); |
| 622 } | 694 } |
| 623 | 695 |
| 624 /** Runs all queued tests, one at a time. */ | 696 /** Runs all queued tests, one at a time. */ |
| 625 void runTests() { | 697 void runTests() { |
| 626 _ensureInitialized(false); | 698 _ensureInitialized(false); |
| 627 assert(_currentTestCase == null); | 699 _currentTestCaseIndex = 0; |
| 628 | |
| 629 _currentGroup = ''; | 700 _currentGroup = ''; |
| 630 | 701 |
| 631 // If we are soloing a test, remove all the others. | 702 // If we are soloing a test, remove all the others. |
| 632 if (_soloTest != null) { | 703 if (_soloTest != null) { |
| 633 filterTests((t) => t == _soloTest); | 704 filterTests((t) => t == _soloTest); |
| 634 } | 705 } |
| 635 | 706 |
| 636 _config.onStart(); | 707 _config.onStart(); |
| 637 | 708 |
| 638 new Future(_nextBatch); | 709 _defer(() { |
| 710 _nextBatch(); |
| 711 }); |
| 639 } | 712 } |
| 640 | 713 |
| 641 /** | 714 /** |
| 642 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is | 715 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 643 * passed to the corresponding test. | 716 * passed to the corresponding test. |
| 644 * | 717 * |
| 645 * The value returned by [tryBody] (if any) is returned by [guardAsync]. | 718 * The value returned by [tryBody] (if any) is returned by [guardAsync]. |
| 646 */ | 719 */ |
| 647 guardAsync(Function tryBody) { | 720 guardAsync(Function tryBody) { |
| 648 return _guardAsync(tryBody, null, currentTestCase); | 721 return _guardAsync(tryBody, null, _currentTestCaseIndex); |
| 649 } | 722 } |
| 650 | 723 |
| 651 _guardAsync(Function tryBody, Function finallyBody, TestCase testCase) { | 724 _guardAsync(Function tryBody, Function finallyBody, int testNum) { |
| 652 assert(testCase != null); | 725 assert(testNum >= 0); |
| 653 try { | 726 try { |
| 654 return tryBody(); | 727 return tryBody(); |
| 655 } catch (e, trace) { | 728 } catch (e, trace) { |
| 656 _registerException(testCase, e, trace); | 729 _registerException(testNum, e, trace); |
| 657 } finally { | 730 } finally { |
| 658 if (finallyBody != null) finallyBody(); | 731 if (finallyBody != null) finallyBody(); |
| 659 } | 732 } |
| 660 } | 733 } |
| 661 | 734 |
| 662 /** | 735 /** |
| 663 * Registers that an exception was caught for the current test. | 736 * Registers that an exception was caught for the current test. |
| 664 */ | 737 */ |
| 665 void registerException(e, [trace]) { | 738 void registerException(e, [trace]) { |
| 666 _registerException(currentTestCase, e, trace); | 739 _registerException(_currentTestCaseIndex, e, trace); |
| 667 } | 740 } |
| 668 | 741 |
| 669 /** | 742 /** |
| 670 * Registers that an exception was caught for the current test. | 743 * Registers that an exception was caught for the current test. |
| 671 */ | 744 */ |
| 672 void _registerException(TestCase testCase, e, [trace]) { | 745 void _registerException(testNum, e, [trace]) { |
| 673 assert(testCase != null); | |
| 674 trace = trace == null ? '' : trace.toString(); | 746 trace = trace == null ? '' : trace.toString(); |
| 675 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 747 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 676 if (testCase.result == null) { | 748 if (testCases[testNum].result == null) { |
| 677 testCase.fail(message, trace); | 749 testCases[testNum].fail(message, trace); |
| 678 } else { | 750 } else { |
| 679 testCase.error(message, trace); | 751 testCases[testNum].error(message, trace); |
| 680 } | 752 } |
| 681 } | 753 } |
| 682 | 754 |
| 683 /** | 755 /** |
| 684 * Executes tests in order starting with the provided index. | 756 * Runs a batch of tests, yielding whenever an asynchronous test starts |
| 685 * If a test is synchronous, the following test is executed immediately | 757 * running. Tests will resume executing when such asynchronous test calls |
| 686 * For asynchronous tests, a Future is returned which completes with _nextBatch | 758 * [done] or if it fails with an exception. |
| 687 * starting at the next index. | |
| 688 * Future.forEach is explicitly not used because it slows down synchronous tests | |
| 689 * noticeably | |
| 690 */ | 759 */ |
| 691 Future _nextBatch([int index = 0]) { | 760 void _nextBatch() { |
| 692 for(int i = index; i < testCases.length; i++) { | 761 while (true) { |
| 693 _currentTestCase = testCases[i]; | 762 if (_currentTestCaseIndex >= testCases.length) { |
| 694 | 763 _completeTests(); |
| 695 Future f = guardAsync(_currentTestCase._run); | 764 break; |
| 696 | |
| 697 if(f != null) { | |
| 698 return f.then((_) => _nextBatch(i + 1)); | |
| 699 } | 765 } |
| 766 final testCase = testCases[_currentTestCaseIndex]; |
| 767 var f = _guardAsync(testCase._run, null, _currentTestCaseIndex); |
| 768 if (f != null) { |
| 769 f.whenComplete(() { |
| 770 _nextTestCase(); // Schedule the next test. |
| 771 }); |
| 772 break; |
| 773 } |
| 774 _currentTestCaseIndex++; |
| 700 } | 775 } |
| 701 _currentTestCase = null; | |
| 702 return new Future(_completeTests); | |
| 703 } | 776 } |
| 704 | 777 |
| 705 /** Publish results on the page and notify controller. */ | 778 /** Publish results on the page and notify controller. */ |
| 706 void _completeTests() { | 779 void _completeTests() { |
| 707 assert(_initialized); | 780 if (!_initialized) return; |
| 708 assert(_currentTestCase == null); | |
| 709 | |
| 710 int passed = 0; | 781 int passed = 0; |
| 711 int failed = 0; | 782 int failed = 0; |
| 712 int errors = 0; | 783 int errors = 0; |
| 713 | 784 |
| 714 for (TestCase t in testCases) { | 785 for (TestCase t in testCases) { |
| 715 switch (t.result) { | 786 switch (t.result) { |
| 716 case PASS: passed++; break; | 787 case PASS: passed++; break; |
| 717 case FAIL: failed++; break; | 788 case FAIL: failed++; break; |
| 718 case ERROR: errors++; break; | 789 case ERROR: errors++; break; |
| 719 } | 790 } |
| 720 } | 791 } |
| 721 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 792 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 722 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 793 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 723 _uncaughtErrorMessage == null); | 794 _uncaughtErrorMessage == null); |
| 724 | 795 _initialized = false; |
| 725 _uncaughtErrorMessage = null; | |
| 726 } | 796 } |
| 727 | 797 |
| 728 String _fullSpec(String spec) { | 798 String _fullSpec(String spec) { |
| 729 if (spec == null) return '$_currentGroup'; | 799 if (spec == null) return '$_currentGroup'; |
| 730 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; | 800 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; |
| 731 } | 801 } |
| 732 | 802 |
| 733 /** | 803 /** |
| 734 * Lazily initializes the test library if not already initialized. | 804 * Lazily initializes the test library if not already initialized. |
| 735 */ | 805 */ |
| 736 void ensureInitialized() { | 806 void ensureInitialized() { |
| 737 _ensureInitialized(true); | 807 _ensureInitialized(true); |
| 738 } | 808 } |
| 739 | 809 |
| 740 void _requireNotRunning() { | |
| 741 if(_currentTestCase != null) { | |
| 742 throw new StateError("A forbidden operation occured " | |
| 743 "while tests were running"); | |
| 744 } | |
| 745 } | |
| 746 | |
| 747 void _ensureInitialized(bool configAutoStart) { | 810 void _ensureInitialized(bool configAutoStart) { |
| 748 _requireNotRunning(); | |
| 749 if (_initialized) { | 811 if (_initialized) { |
| 750 return; | 812 return; |
| 751 } | 813 } |
| 752 _initialized = true; | 814 _initialized = true; |
| 753 // Hook our async guard into the matcher library. | 815 // Hook our async guard into the matcher library. |
| 754 wrapAsync = (f, [id]) => expectAsync1(f, id: id); | 816 wrapAsync = (f, [id]) => expectAsync1(f, id: id); |
| 755 | 817 |
| 756 _uncaughtErrorMessage = null; | 818 _uncaughtErrorMessage = null; |
| 757 | 819 |
| 758 if (_config == null) { | 820 if (_config == null) { |
| 759 unittestConfiguration = new Configuration(); | 821 unittestConfiguration = new Configuration(); |
| 760 } | 822 } |
| 761 _config.onInit(); | 823 _config.onInit(); |
| 762 | 824 |
| 763 if (configAutoStart && _config.autoStart) { | 825 if (configAutoStart && _config.autoStart) { |
| 764 // Immediately queue the suite up. It will run after a timeout (i.e. after | 826 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 765 // main() has returned). | 827 // main() has returned). |
| 766 new Future(runTests); | 828 _defer(runTests); |
| 767 } | 829 } |
| 768 } | 830 } |
| 769 | 831 |
| 770 /** Select a solo test by ID. */ | 832 /** Select a solo test by ID. */ |
| 771 void setSoloTest(int id) { | 833 void setSoloTest(int id) { |
| 772 for (var i = 0; i < testCases.length; i++) { | 834 for (var i = 0; i < testCases.length; i++) { |
| 773 if (testCases[i].id == id) { | 835 if (testCases[i].id == id) { |
| 774 _soloTest = testCases[i]; | 836 _soloTest = testCases[i]; |
| 775 break; | 837 break; |
| 776 } | 838 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 793 } | 855 } |
| 794 | 856 |
| 795 /** Enable a test by ID. */ | 857 /** Enable a test by ID. */ |
| 796 void enableTest(int testId) => _setTestEnabledState(testId, true); | 858 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 797 | 859 |
| 798 /** Disable a test by ID. */ | 860 /** Disable a test by ID. */ |
| 799 void disableTest(int testId) => _setTestEnabledState(testId, false); | 861 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 800 | 862 |
| 801 /** Signature for a test function. */ | 863 /** Signature for a test function. */ |
| 802 typedef dynamic TestFunction(); | 864 typedef dynamic TestFunction(); |
| OLD | NEW |