Chromium Code Reviews| 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 int _currentTestCaseIndex = -1; |
|
Siggi Cherem (dart-lang)
2014/01/30 23:39:01
There is a lot of implicit state encoded in 'curre
kevmoo
2014/01/30 23:42:04
I introduced the currentIndex model a while back.
Siggi Cherem (dart-lang)
2014/01/30 23:46:33
I'm not convinced :), but lets at least add some d
kevmoo
2014/01/31 02:27:37
Done.
| |
| 283 | 283 |
| 284 /** [TestCase] currently being executed. */ | 284 /** [TestCase] currently being executed. */ |
| 285 TestCase get currentTestCase => | 285 TestCase get currentTestCase => |
| 286 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 286 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 287 ? testCases[_currentTestCaseIndex] | 287 ? testCases[_currentTestCaseIndex] |
| 288 : null; | 288 : null; |
| 289 | 289 |
| 290 /** Whether the framework is in an initialized state. */ | 290 /** Whether the framework is in an initialized state. */ |
| 291 bool _initialized = false; | 291 bool _initialized = false; |
| 292 | 292 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 306 const FAIL = 'fail'; | 306 const FAIL = 'fail'; |
| 307 /// Result string for an test case with an error. | 307 /// Result string for an test case with an error. |
| 308 const ERROR = 'error'; | 308 const ERROR = 'error'; |
| 309 | 309 |
| 310 /** | 310 /** |
| 311 * Creates a new test case with the given description and body. The | 311 * Creates a new test case with the given description and body. The |
| 312 * description will include the descriptions of any surrounding group() | 312 * description will include the descriptions of any surrounding group() |
| 313 * calls. | 313 * calls. |
| 314 */ | 314 */ |
| 315 void test(String spec, TestFunction body) { | 315 void test(String spec, TestFunction body) { |
| 316 _requireNotRunning(); | |
| 316 ensureInitialized(); | 317 ensureInitialized(); |
| 317 if (!_soloTestSeen || _soloNestingLevel > 0) { | 318 if (!_soloTestSeen || _soloNestingLevel > 0) { |
| 318 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), | 319 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), |
| 319 body); | 320 body); |
| 320 _testCases.add(testcase); | 321 _testCases.add(testcase); |
| 321 } | 322 } |
| 322 } | 323 } |
| 323 | 324 |
| 324 /** Convenience function for skipping a test. */ | 325 /** Convenience function for skipping a test. */ |
| 325 void skip_test(String spec, TestFunction body){} | 326 void skip_test(String spec, TestFunction body){} |
| 326 | 327 |
| 327 /** | 328 /** |
| 328 * Creates a new test case with the given description and body. The | 329 * Creates a new test case with the given description and body. The |
| 329 * description will include the descriptions of any surrounding group() | 330 * description will include the descriptions of any surrounding group() |
| 330 * calls. | 331 * calls. |
| 331 * | 332 * |
| 332 * If we use [solo_test] (or [solo_group]) instead of test, then all non-solo | 333 * 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 | 334 * 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 | 335 * 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 | 336 * [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 | 337 * 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 | 338 * file, all tests that are not inside a [solo_group] will be disabled unless |
| 338 * they are [solo_test]s. | 339 * they are [solo_test]s. |
| 339 * | 340 * |
| 340 * [skip_test] and [skip_group] take precedence over soloing, by virtue of the | 341 * [skip_test] and [skip_group] take precedence over soloing, by virtue of the |
| 341 * fact that they are effectively no-ops. | 342 * fact that they are effectively no-ops. |
| 342 */ | 343 */ |
| 343 void solo_test(String spec, TestFunction body) { | 344 void solo_test(String spec, TestFunction body) { |
| 345 _requireNotRunning(); | |
| 344 ensureInitialized(); | 346 ensureInitialized(); |
| 345 if (!_soloTestSeen) { | 347 if (!_soloTestSeen) { |
| 346 _soloTestSeen = true; | 348 _soloTestSeen = true; |
| 347 // This is the first solo-ed test. Discard all tests up to now. | 349 // This is the first solo-ed test. Discard all tests up to now. |
| 348 _testCases.clear(); | 350 _testCases.clear(); |
| 349 } | 351 } |
| 350 ++_soloNestingLevel; | 352 ++_soloNestingLevel; |
| 351 try { | 353 try { |
| 352 test(spec, body); | 354 test(spec, body); |
| 353 } finally { | 355 } finally { |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 577 Function protectAsync2(Function callback, {String id}) { | 579 Function protectAsync2(Function callback, {String id}) { |
| 578 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; | 580 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
| 579 } | 581 } |
| 580 | 582 |
| 581 /** | 583 /** |
| 582 * Creates a new named group of tests. Calls to group() or test() within the | 584 * 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. | 585 * body of the function passed to this will inherit this group's description. |
| 584 */ | 586 */ |
| 585 void group(String description, void body()) { | 587 void group(String description, void body()) { |
| 586 ensureInitialized(); | 588 ensureInitialized(); |
| 589 _requireNotRunning(); | |
| 587 _currentContext = new _GroupContext(_currentContext, description); | 590 _currentContext = new _GroupContext(_currentContext, description); |
| 588 try { | 591 try { |
| 589 body(); | 592 body(); |
| 590 } catch (e, trace) { | 593 } catch (e, trace) { |
| 591 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | 594 var stack = (trace == null) ? '' : ': ${trace.toString()}'; |
| 592 _uncaughtErrorMessage = "${e.toString()}$stack"; | 595 _uncaughtErrorMessage = "${e.toString()}$stack"; |
| 593 } finally { | 596 } finally { |
| 594 // Now that the group is over, restore the previous one. | 597 // Now that the group is over, restore the previous one. |
| 595 _currentContext = _currentContext.parent; | 598 _currentContext = _currentContext.parent; |
| 596 } | 599 } |
| 597 } | 600 } |
| 598 | 601 |
| 599 /** Like [skip_test], but for groups. */ | 602 /** Like [skip_test], but for groups. */ |
| 600 void skip_group(String description, void body()) {} | 603 void skip_group(String description, void body()) {} |
| 601 | 604 |
| 602 /** Like [solo_test], but for groups. */ | 605 /** Like [solo_test], but for groups. */ |
| 603 void solo_group(String description, void body()) { | 606 void solo_group(String description, void body()) { |
| 607 _requireNotRunning(); | |
| 604 ensureInitialized(); | 608 ensureInitialized(); |
| 605 if (!_soloTestSeen) { | 609 if (!_soloTestSeen) { |
| 606 _soloTestSeen = true; | 610 _soloTestSeen = true; |
| 607 // This is the first solo-ed group. Discard all tests up to now. | 611 // This is the first solo-ed group. Discard all tests up to now. |
| 608 _testCases.clear(); | 612 _testCases.clear(); |
| 609 } | 613 } |
| 610 ++_soloNestingLevel; | 614 ++_soloNestingLevel; |
| 611 try { | 615 try { |
| 612 group(description, body); | 616 group(description, body); |
| 613 } finally { | 617 } finally { |
| 614 --_soloNestingLevel; | 618 --_soloNestingLevel; |
| 615 } | 619 } |
| 616 } | 620 } |
| 617 | 621 |
| 618 /** | 622 /** |
| 619 * Register a [setUp] function for a test [group]. This function will | 623 * Register a [setUp] function for a test [group]. This function will |
| 620 * be called before each test in the group is run. | 624 * be called before each test in the group is run. |
| 621 * [setUp] and [tearDown] should be called within the [group] before any | 625 * [setUp] and [tearDown] should be called within the [group] before any |
| 622 * calls to [test]. The [setupTest] function can be asynchronous; in this | 626 * calls to [test]. The [setupTest] function can be asynchronous; in this |
| 623 * case it must return a [Future]. | 627 * case it must return a [Future]. |
| 624 */ | 628 */ |
| 625 void setUp(Function setupTest) { | 629 void setUp(Function setupTest) { |
| 630 _requireNotRunning(); | |
| 626 _currentContext.testSetup = setupTest; | 631 _currentContext.testSetup = setupTest; |
| 627 } | 632 } |
| 628 | 633 |
| 629 /** | 634 /** |
| 630 * Register a [tearDown] function for a test [group]. This function will | 635 * 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 | 636 * 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. | 637 * are nested only the most locally scoped [teardownTest] function will be run. |
| 633 * [setUp] and [tearDown] should be called within the [group] before any | 638 * [setUp] and [tearDown] should be called within the [group] before any |
| 634 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 639 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 635 * case it must return a [Future]. | 640 * case it must return a [Future]. |
| 636 */ | 641 */ |
| 637 void tearDown(Function teardownTest) { | 642 void tearDown(Function teardownTest) { |
| 643 _requireNotRunning(); | |
| 638 _currentContext.testTeardown = teardownTest; | 644 _currentContext.testTeardown = teardownTest; |
| 639 } | 645 } |
| 640 | 646 |
| 641 /** Advance to the next test case. */ | 647 /** Advance to the next test case. */ |
| 642 void _nextTestCase() { | 648 void _nextTestCase() { |
| 643 _currentTestCaseIndex++; | 649 _currentTestCaseIndex++; |
| 644 _runTest(); | 650 _runTest(); |
| 645 } | 651 } |
| 646 | 652 |
| 647 /** Handle errors that happen outside the tests. */ | 653 /** Handle errors that happen outside the tests. */ |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 676 } else if (testFilter is RegExp) { | 682 } else if (testFilter is RegExp) { |
| 677 filterFunction = (t) => testFilter.hasMatch(t.description); | 683 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 678 } else if (testFilter is Function) { | 684 } else if (testFilter is Function) { |
| 679 filterFunction = testFilter; | 685 filterFunction = testFilter; |
| 680 } | 686 } |
| 681 _testCases.retainWhere(filterFunction); | 687 _testCases.retainWhere(filterFunction); |
| 682 } | 688 } |
| 683 | 689 |
| 684 /** Runs all queued tests, one at a time. */ | 690 /** Runs all queued tests, one at a time. */ |
| 685 void runTests() { | 691 void runTests() { |
| 692 _requireNotRunning(); | |
| 686 _ensureInitialized(false); | 693 _ensureInitialized(false); |
| 687 _currentTestCaseIndex = 0; | 694 _currentTestCaseIndex = 0; |
| 688 _config.onStart(); | 695 _config.onStart(); |
| 689 _runTest(); | 696 _runTest(); |
| 690 } | 697 } |
| 691 | 698 |
| 692 /** | 699 /** |
| 693 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is | 700 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 694 * passed to the corresponding test. | 701 * passed to the corresponding test. |
| 695 * | 702 * |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 727 } else { | 734 } else { |
| 728 testCase.error(message, trace); | 735 testCase.error(message, trace); |
| 729 } | 736 } |
| 730 } | 737 } |
| 731 | 738 |
| 732 /** | 739 /** |
| 733 * Runs the next test. | 740 * Runs the next test. |
| 734 */ | 741 */ |
| 735 void _runTest() { | 742 void _runTest() { |
| 736 if (_currentTestCaseIndex >= testCases.length) { | 743 if (_currentTestCaseIndex >= testCases.length) { |
| 744 assert(_currentTestCaseIndex == testCases.length); | |
| 737 _completeTests(); | 745 _completeTests(); |
| 738 } else { | 746 } else { |
| 739 final testCase = testCases[_currentTestCaseIndex]; | 747 var testCase = testCases[_currentTestCaseIndex]; |
| 740 var f = _guardAsync(testCase._run, null, testCase); | 748 Future f = _guardAsync(testCase._run, null, testCase); |
|
Siggi Cherem (dart-lang)
2014/01/30 23:44:30
Future -> var
kevmoo
2014/01/31 02:27:37
This one is intentional. _guardAsync return dynami
| |
| 749 var timeout = unittestConfiguration.timeout; | |
| 750 | |
| 741 Timer timer; | 751 Timer timer; |
| 742 final Duration timeout = unittestConfiguration.timeout; | |
| 743 if (timeout != null) { | 752 if (timeout != null) { |
| 744 try { | 753 try { |
| 745 timer = new Timer(timeout, () { | 754 timer = new Timer(timeout, () { |
| 746 testCase.error("Test timed out after ${timeout.inSeconds} seconds."); | 755 testCase.error("Test timed out after ${timeout.inSeconds} seconds."); |
| 747 _nextTestCase(); | 756 _nextTestCase(); |
| 748 }); | 757 }); |
| 749 } on UnsupportedError catch (e) { | 758 } on UnsupportedError catch (e) { |
| 750 if (e.message != "Timer greater than 0.") rethrow; | 759 if (e.message != "Timer greater than 0.") rethrow; |
| 751 // Support running on d8 and jsshell which don't support timers. | 760 // Support running on d8 and jsshell which don't support timers. |
| 752 } | 761 } |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 775 switch (t.result) { | 784 switch (t.result) { |
| 776 case PASS: passed++; break; | 785 case PASS: passed++; break; |
| 777 case FAIL: failed++; break; | 786 case FAIL: failed++; break; |
| 778 case ERROR: errors++; break; | 787 case ERROR: errors++; break; |
| 779 } | 788 } |
| 780 } | 789 } |
| 781 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 790 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 782 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 791 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 783 _uncaughtErrorMessage == null); | 792 _uncaughtErrorMessage == null); |
| 784 _initialized = false; | 793 _initialized = false; |
| 794 _currentTestCaseIndex = -1; | |
| 785 } | 795 } |
| 786 | 796 |
| 787 String _fullSpec(String spec) { | 797 String _fullSpec(String spec) { |
| 788 var group = '${_currentContext.fullName}'; | 798 var group = '${_currentContext.fullName}'; |
| 789 if (spec == null) return group; | 799 if (spec == null) return group; |
| 790 return group != '' ? '$group$groupSep$spec' : spec; | 800 return group != '' ? '$group$groupSep$spec' : spec; |
| 791 } | 801 } |
| 792 | 802 |
| 793 /** | 803 /** |
| 794 * Lazily initializes the test library if not already initialized. | 804 * 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. | 861 * Useful to disable when debugging unittest or matcher customizations. |
| 852 */ | 862 */ |
| 853 bool formatStacks = true; | 863 bool formatStacks = true; |
| 854 | 864 |
| 855 /** | 865 /** |
| 856 * A flag that controls whether we try to filter out irrelevant frames from | 866 * A flag that controls whether we try to filter out irrelevant frames from |
| 857 * the stack trace. Requires formatStacks to be set. | 867 * the stack trace. Requires formatStacks to be set. |
| 858 */ | 868 */ |
| 859 bool filterStacks = true; | 869 bool filterStacks = true; |
| 860 | 870 |
| 871 void _requireNotRunning() { | |
| 872 if(_currentTestCaseIndex != -1) { | |
| 873 throw new StateError('Not allowed when tests are running.'); | |
| 874 } | |
| 875 } | |
| 876 | |
| 861 /** | 877 /** |
| 862 * Returns a Trace object from a StackTrace object or a String, or the | 878 * Returns a Trace object from a StackTrace object or a String, or the |
| 863 * unchanged input if formatStacks is false; | 879 * unchanged input if formatStacks is false; |
| 864 */ | 880 */ |
| 865 Trace _getTrace(stack) { | 881 Trace _getTrace(stack) { |
| 866 Trace trace; | 882 Trace trace; |
| 867 if (stack == null || !formatStacks) return null; | 883 if (stack == null || !formatStacks) return null; |
| 868 if (stack is String) { | 884 if (stack is String) { |
| 869 trace = new Trace.parse(stack); | 885 trace = new Trace.parse(stack); |
| 870 } else if (stack is StackTrace) { | 886 } else if (stack is StackTrace) { |
| 871 trace = new Trace.from(stack); | 887 trace = new Trace.from(stack); |
| 872 } else { | 888 } else { |
| 873 throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); | 889 throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); |
| 874 } | 890 } |
| 875 | 891 |
| 876 if (!filterStacks) return trace; | 892 if (!filterStacks) return trace; |
| 877 | 893 |
| 878 // Format the stack trace by removing everything above TestCase._runTest, | 894 // Format the stack trace by removing everything above TestCase._runTest, |
| 879 // which is usually going to be irrelevant. Also fold together unittest and | 895 // 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. | 896 // core library calls so only the function the user called is visible. |
| 881 return new Trace(trace.frames.takeWhile((frame) { | 897 return new Trace(trace.frames.takeWhile((frame) { |
| 882 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; | 898 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; |
| 883 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); | 899 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); |
| 884 } | 900 } |
| OLD | NEW |