| 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 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 _testTeardown = parentTeardown; | 272 _testTeardown = parentTeardown; |
| 273 } | 273 } |
| 274 } | 274 } |
| 275 | 275 |
| 276 // We use a 'dummy' context for the top level to eliminate null | 276 // We use a 'dummy' context for the top level to eliminate null |
| 277 // checks when querying the context. This allows us to easily | 277 // checks when querying the context. This allows us to easily |
| 278 // support top-level setUp/tearDown functions as well. | 278 // support top-level setUp/tearDown functions as well. |
| 279 final _rootContext = new _GroupContext(); | 279 final _rootContext = new _GroupContext(); |
| 280 _GroupContext _currentContext = _rootContext; | 280 _GroupContext _currentContext = _rootContext; |
| 281 | 281 |
| 282 int _currentTestCaseIndex = 0; | 282 /** |
| 283 * Represents the index of the currently running test case |
| 284 * == -1 implies the test system is not running |
| 285 * == [number of test cases] is a short-lived state flagging that the last test |
| 286 * has completed |
| 287 */ |
| 288 int _currentTestCaseIndex = -1; |
| 283 | 289 |
| 284 /** [TestCase] currently being executed. */ | 290 /** [TestCase] currently being executed. */ |
| 285 TestCase get currentTestCase => | 291 TestCase get currentTestCase => |
| 286 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 292 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 287 ? testCases[_currentTestCaseIndex] | 293 ? testCases[_currentTestCaseIndex] |
| 288 : null; | 294 : null; |
| 289 | 295 |
| 290 /** Whether the framework is in an initialized state. */ | 296 /** Whether the framework is in an initialized state. */ |
| 291 bool _initialized = false; | 297 bool _initialized = false; |
| 292 | 298 |
| 293 String _uncaughtErrorMessage = null; | 299 String _uncaughtErrorMessage = null; |
| 294 | 300 |
| 295 /** Time since we last gave non-sync code a chance to be scheduled. */ | 301 /** Time since we last gave non-sync code a chance to be scheduled. */ |
| 296 var _lastBreath = new DateTime.now().millisecondsSinceEpoch; | 302 int _lastBreath = new DateTime.now().millisecondsSinceEpoch; |
| 297 | 303 |
| 298 /* Test case result strings. */ | 304 /* Test case result strings. */ |
| 299 // TODO(gram) we should change these constants to use a different string | 305 // TODO(gram) we should change these constants to use a different string |
| 300 // (so that writing 'FAIL' in the middle of a test doesn't | 306 // (so that writing 'FAIL' in the middle of a test doesn't |
| 301 // imply that the test fails). We can't do it without also changing | 307 // imply that the test fails). We can't do it without also changing |
| 302 // the testrunner and test.dart though. | 308 // the testrunner and test.dart though. |
| 303 /// Result string for a passing test case. | 309 /// Result string for a passing test case. |
| 304 const PASS = 'pass'; | 310 const PASS = 'pass'; |
| 305 /// Result string for a failing test case. | 311 /// Result string for a failing test case. |
| 306 const FAIL = 'fail'; | 312 const FAIL = 'fail'; |
| 307 /// Result string for an test case with an error. | 313 /// Result string for an test case with an error. |
| 308 const ERROR = 'error'; | 314 const ERROR = 'error'; |
| 309 | 315 |
| 310 /** | 316 /** |
| 311 * Creates a new test case with the given description and body. The | 317 * Creates a new test case with the given description and body. The |
| 312 * description will include the descriptions of any surrounding group() | 318 * description will include the descriptions of any surrounding group() |
| 313 * calls. | 319 * calls. |
| 314 */ | 320 */ |
| 315 void test(String spec, TestFunction body) { | 321 void test(String spec, TestFunction body) { |
| 322 _requireNotRunning(); |
| 316 ensureInitialized(); | 323 ensureInitialized(); |
| 317 if (!_soloTestSeen || _soloNestingLevel > 0) { | 324 if (!_soloTestSeen || _soloNestingLevel > 0) { |
| 318 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), | 325 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), |
| 319 body); | 326 body); |
| 320 _testCases.add(testcase); | 327 _testCases.add(testcase); |
| 321 } | 328 } |
| 322 } | 329 } |
| 323 | 330 |
| 324 /** Convenience function for skipping a test. */ | 331 /** Convenience function for skipping a test. */ |
| 325 void skip_test(String spec, TestFunction body){} | 332 void skip_test(String spec, TestFunction body){} |
| 326 | 333 |
| 327 /** | 334 /** |
| 328 * Creates a new test case with the given description and body. The | 335 * Creates a new test case with the given description and body. The |
| 329 * description will include the descriptions of any surrounding group() | 336 * description will include the descriptions of any surrounding group() |
| 330 * calls. | 337 * calls. |
| 331 * | 338 * |
| 332 * If we use [solo_test] (or [solo_group]) instead of test, then all non-solo | 339 * If we use [solo_test] (or [solo_group]) instead of test, then all non-solo |
| 333 * tests will be disabled. Note that if we use [solo_group], all tests in | 340 * tests will be disabled. Note that if we use [solo_group], all tests in |
| 334 * the group will be enabled, regardless of whether they use [test] or | 341 * the group will be enabled, regardless of whether they use [test] or |
| 335 * [solo_test], or whether they are in a nested [group] vs [solo_group]. Put | 342 * [solo_test], or whether they are in a nested [group] vs [solo_group]. Put |
| 336 * another way, if there are any calls to [solo_test] or [solo_group] in a test | 343 * another way, if there are any calls to [solo_test] or [solo_group] in a test |
| 337 * file, all tests that are not inside a [solo_group] will be disabled unless | 344 * file, all tests that are not inside a [solo_group] will be disabled unless |
| 338 * they are [solo_test]s. | 345 * they are [solo_test]s. |
| 339 * | 346 * |
| 340 * [skip_test] and [skip_group] take precedence over soloing, by virtue of the | 347 * [skip_test] and [skip_group] take precedence over soloing, by virtue of the |
| 341 * fact that they are effectively no-ops. | 348 * fact that they are effectively no-ops. |
| 342 */ | 349 */ |
| 343 void solo_test(String spec, TestFunction body) { | 350 void solo_test(String spec, TestFunction body) { |
| 351 _requireNotRunning(); |
| 344 ensureInitialized(); | 352 ensureInitialized(); |
| 345 if (!_soloTestSeen) { | 353 if (!_soloTestSeen) { |
| 346 _soloTestSeen = true; | 354 _soloTestSeen = true; |
| 347 // This is the first solo-ed test. Discard all tests up to now. | 355 // This is the first solo-ed test. Discard all tests up to now. |
| 348 _testCases.clear(); | 356 _testCases.clear(); |
| 349 } | 357 } |
| 350 ++_soloNestingLevel; | 358 ++_soloNestingLevel; |
| 351 try { | 359 try { |
| 352 test(spec, body); | 360 test(spec, body); |
| 353 } finally { | 361 } finally { |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 577 Function protectAsync2(Function callback, {String id}) { | 585 Function protectAsync2(Function callback, {String id}) { |
| 578 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; | 586 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
| 579 } | 587 } |
| 580 | 588 |
| 581 /** | 589 /** |
| 582 * Creates a new named group of tests. Calls to group() or test() within the | 590 * Creates a new named group of tests. Calls to group() or test() within the |
| 583 * body of the function passed to this will inherit this group's description. | 591 * body of the function passed to this will inherit this group's description. |
| 584 */ | 592 */ |
| 585 void group(String description, void body()) { | 593 void group(String description, void body()) { |
| 586 ensureInitialized(); | 594 ensureInitialized(); |
| 595 _requireNotRunning(); |
| 587 _currentContext = new _GroupContext(_currentContext, description); | 596 _currentContext = new _GroupContext(_currentContext, description); |
| 588 try { | 597 try { |
| 589 body(); | 598 body(); |
| 590 } catch (e, trace) { | 599 } catch (e, trace) { |
| 591 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | 600 var stack = (trace == null) ? '' : ': ${trace.toString()}'; |
| 592 _uncaughtErrorMessage = "${e.toString()}$stack"; | 601 _uncaughtErrorMessage = "${e.toString()}$stack"; |
| 593 } finally { | 602 } finally { |
| 594 // Now that the group is over, restore the previous one. | 603 // Now that the group is over, restore the previous one. |
| 595 _currentContext = _currentContext.parent; | 604 _currentContext = _currentContext.parent; |
| 596 } | 605 } |
| 597 } | 606 } |
| 598 | 607 |
| 599 /** Like [skip_test], but for groups. */ | 608 /** Like [skip_test], but for groups. */ |
| 600 void skip_group(String description, void body()) {} | 609 void skip_group(String description, void body()) {} |
| 601 | 610 |
| 602 /** Like [solo_test], but for groups. */ | 611 /** Like [solo_test], but for groups. */ |
| 603 void solo_group(String description, void body()) { | 612 void solo_group(String description, void body()) { |
| 613 _requireNotRunning(); |
| 604 ensureInitialized(); | 614 ensureInitialized(); |
| 605 if (!_soloTestSeen) { | 615 if (!_soloTestSeen) { |
| 606 _soloTestSeen = true; | 616 _soloTestSeen = true; |
| 607 // This is the first solo-ed group. Discard all tests up to now. | 617 // This is the first solo-ed group. Discard all tests up to now. |
| 608 _testCases.clear(); | 618 _testCases.clear(); |
| 609 } | 619 } |
| 610 ++_soloNestingLevel; | 620 ++_soloNestingLevel; |
| 611 try { | 621 try { |
| 612 group(description, body); | 622 group(description, body); |
| 613 } finally { | 623 } finally { |
| 614 --_soloNestingLevel; | 624 --_soloNestingLevel; |
| 615 } | 625 } |
| 616 } | 626 } |
| 617 | 627 |
| 618 /** | 628 /** |
| 619 * Register a [setUp] function for a test [group]. This function will | 629 * Register a [setUp] function for a test [group]. This function will |
| 620 * be called before each test in the group is run. | 630 * be called before each test in the group is run. |
| 621 * [setUp] and [tearDown] should be called within the [group] before any | 631 * [setUp] and [tearDown] should be called within the [group] before any |
| 622 * calls to [test]. The [setupTest] function can be asynchronous; in this | 632 * calls to [test]. The [setupTest] function can be asynchronous; in this |
| 623 * case it must return a [Future]. | 633 * case it must return a [Future]. |
| 624 */ | 634 */ |
| 625 void setUp(Function setupTest) { | 635 void setUp(Function setupTest) { |
| 636 _requireNotRunning(); |
| 626 _currentContext.testSetup = setupTest; | 637 _currentContext.testSetup = setupTest; |
| 627 } | 638 } |
| 628 | 639 |
| 629 /** | 640 /** |
| 630 * Register a [tearDown] function for a test [group]. This function will | 641 * Register a [tearDown] function for a test [group]. This function will |
| 631 * be called after each test in the group is run. Note that if groups | 642 * be called after each test in the group is run. Note that if groups |
| 632 * are nested only the most locally scoped [teardownTest] function will be run. | 643 * are nested only the most locally scoped [teardownTest] function will be run. |
| 633 * [setUp] and [tearDown] should be called within the [group] before any | 644 * [setUp] and [tearDown] should be called within the [group] before any |
| 634 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 645 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 635 * case it must return a [Future]. | 646 * case it must return a [Future]. |
| 636 */ | 647 */ |
| 637 void tearDown(Function teardownTest) { | 648 void tearDown(Function teardownTest) { |
| 649 _requireNotRunning(); |
| 638 _currentContext.testTeardown = teardownTest; | 650 _currentContext.testTeardown = teardownTest; |
| 639 } | 651 } |
| 640 | 652 |
| 641 /** Advance to the next test case. */ | 653 /** Advance to the next test case. */ |
| 642 void _nextTestCase() { | 654 void _nextTestCase() { |
| 643 _currentTestCaseIndex++; | 655 _currentTestCaseIndex++; |
| 644 _runTest(); | 656 _runTest(); |
| 645 } | 657 } |
| 646 | 658 |
| 647 /** Handle errors that happen outside the tests. */ | 659 /** Handle errors that happen outside the tests. */ |
| (...skipping 28 matching lines...) Expand all Loading... |
| 676 } else if (testFilter is RegExp) { | 688 } else if (testFilter is RegExp) { |
| 677 filterFunction = (t) => testFilter.hasMatch(t.description); | 689 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 678 } else if (testFilter is Function) { | 690 } else if (testFilter is Function) { |
| 679 filterFunction = testFilter; | 691 filterFunction = testFilter; |
| 680 } | 692 } |
| 681 _testCases.retainWhere(filterFunction); | 693 _testCases.retainWhere(filterFunction); |
| 682 } | 694 } |
| 683 | 695 |
| 684 /** Runs all queued tests, one at a time. */ | 696 /** Runs all queued tests, one at a time. */ |
| 685 void runTests() { | 697 void runTests() { |
| 698 _requireNotRunning(); |
| 686 _ensureInitialized(false); | 699 _ensureInitialized(false); |
| 687 _currentTestCaseIndex = 0; | 700 _currentTestCaseIndex = 0; |
| 688 _config.onStart(); | 701 _config.onStart(); |
| 689 _runTest(); | 702 _runTest(); |
| 690 } | 703 } |
| 691 | 704 |
| 692 /** | 705 /** |
| 693 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is | 706 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 694 * passed to the corresponding test. | 707 * passed to the corresponding test. |
| 695 * | 708 * |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 727 } else { | 740 } else { |
| 728 testCase.error(message, trace); | 741 testCase.error(message, trace); |
| 729 } | 742 } |
| 730 } | 743 } |
| 731 | 744 |
| 732 /** | 745 /** |
| 733 * Runs the next test. | 746 * Runs the next test. |
| 734 */ | 747 */ |
| 735 void _runTest() { | 748 void _runTest() { |
| 736 if (_currentTestCaseIndex >= testCases.length) { | 749 if (_currentTestCaseIndex >= testCases.length) { |
| 750 assert(_currentTestCaseIndex == testCases.length); |
| 737 _completeTests(); | 751 _completeTests(); |
| 738 } else { | 752 } else { |
| 739 final testCase = testCases[_currentTestCaseIndex]; | 753 var testCase = testCases[_currentTestCaseIndex]; |
| 740 var f = _guardAsync(testCase._run, null, testCase); | 754 Future f = _guardAsync(testCase._run, null, testCase); |
| 755 var timeout = unittestConfiguration.timeout; |
| 756 |
| 741 Timer timer; | 757 Timer timer; |
| 742 final Duration timeout = unittestConfiguration.timeout; | |
| 743 if (timeout != null) { | 758 if (timeout != null) { |
| 744 try { | 759 try { |
| 745 timer = new Timer(timeout, () { | 760 timer = new Timer(timeout, () { |
| 746 testCase.error("Test timed out after ${timeout.inSeconds} seconds."); | 761 testCase.error("Test timed out after ${timeout.inSeconds} seconds."); |
| 747 _nextTestCase(); | 762 _nextTestCase(); |
| 748 }); | 763 }); |
| 749 } on UnsupportedError catch (e) { | 764 } on UnsupportedError catch (e) { |
| 750 if (e.message != "Timer greater than 0.") rethrow; | 765 if (e.message != "Timer greater than 0.") rethrow; |
| 751 // Support running on d8 and jsshell which don't support timers. | 766 // Support running on d8 and jsshell which don't support timers. |
| 752 } | 767 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 775 switch (t.result) { | 790 switch (t.result) { |
| 776 case PASS: passed++; break; | 791 case PASS: passed++; break; |
| 777 case FAIL: failed++; break; | 792 case FAIL: failed++; break; |
| 778 case ERROR: errors++; break; | 793 case ERROR: errors++; break; |
| 779 } | 794 } |
| 780 } | 795 } |
| 781 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 796 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 782 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 797 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 783 _uncaughtErrorMessage == null); | 798 _uncaughtErrorMessage == null); |
| 784 _initialized = false; | 799 _initialized = false; |
| 800 _currentTestCaseIndex = -1; |
| 785 } | 801 } |
| 786 | 802 |
| 787 String _fullSpec(String spec) { | 803 String _fullSpec(String spec) { |
| 788 var group = '${_currentContext.fullName}'; | 804 var group = '${_currentContext.fullName}'; |
| 789 if (spec == null) return group; | 805 if (spec == null) return group; |
| 790 return group != '' ? '$group$groupSep$spec' : spec; | 806 return group != '' ? '$group$groupSep$spec' : spec; |
| 791 } | 807 } |
| 792 | 808 |
| 793 /** | 809 /** |
| 794 * Lazily initializes the test library if not already initialized. | 810 * Lazily initializes the test library if not already initialized. |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 851 * Useful to disable when debugging unittest or matcher customizations. | 867 * Useful to disable when debugging unittest or matcher customizations. |
| 852 */ | 868 */ |
| 853 bool formatStacks = true; | 869 bool formatStacks = true; |
| 854 | 870 |
| 855 /** | 871 /** |
| 856 * A flag that controls whether we try to filter out irrelevant frames from | 872 * A flag that controls whether we try to filter out irrelevant frames from |
| 857 * the stack trace. Requires formatStacks to be set. | 873 * the stack trace. Requires formatStacks to be set. |
| 858 */ | 874 */ |
| 859 bool filterStacks = true; | 875 bool filterStacks = true; |
| 860 | 876 |
| 877 void _requireNotRunning() { |
| 878 if(_currentTestCaseIndex != -1) { |
| 879 throw new StateError('Not allowed when tests are running.'); |
| 880 } |
| 881 } |
| 882 |
| 861 /** | 883 /** |
| 862 * Returns a Trace object from a StackTrace object or a String, or the | 884 * Returns a Trace object from a StackTrace object or a String, or the |
| 863 * unchanged input if formatStacks is false; | 885 * unchanged input if formatStacks is false; |
| 864 */ | 886 */ |
| 865 Trace _getTrace(stack) { | 887 Trace _getTrace(stack) { |
| 866 Trace trace; | 888 Trace trace; |
| 867 if (stack == null || !formatStacks) return null; | 889 if (stack == null || !formatStacks) return null; |
| 868 if (stack is String) { | 890 if (stack is String) { |
| 869 trace = new Trace.parse(stack); | 891 trace = new Trace.parse(stack); |
| 870 } else if (stack is StackTrace) { | 892 } else if (stack is StackTrace) { |
| 871 trace = new Trace.from(stack); | 893 trace = new Trace.from(stack); |
| 872 } else { | 894 } else { |
| 873 throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); | 895 throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); |
| 874 } | 896 } |
| 875 | 897 |
| 876 if (!filterStacks) return trace; | 898 if (!filterStacks) return trace; |
| 877 | 899 |
| 878 // Format the stack trace by removing everything above TestCase._runTest, | 900 // Format the stack trace by removing everything above TestCase._runTest, |
| 879 // which is usually going to be irrelevant. Also fold together unittest and | 901 // which is usually going to be irrelevant. Also fold together unittest and |
| 880 // core library calls so only the function the user called is visible. | 902 // core library calls so only the function the user called is visible. |
| 881 return new Trace(trace.frames.takeWhile((frame) { | 903 return new Trace(trace.frames.takeWhile((frame) { |
| 882 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; | 904 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; |
| 883 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); | 905 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); |
| 884 } | 906 } |
| OLD | NEW |