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 |