Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1091)

Side by Side Diff: pkg/unittest/lib/unittest.dart

Issue 14092004: Chaining of setup/teardown. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/unittest/lib/src/test_case.dart ('k') | pkg/unittest/test/unittest_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 * ## Installing ## 8 * ## Installing ##
9 * 9 *
10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml` 10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 } 198 }
199 } 199 }
200 200
201 /** 201 /**
202 * Can be called by tests to log status. Tests should use this 202 * Can be called by tests to log status. Tests should use this
203 * instead of [print]. 203 * instead of [print].
204 */ 204 */
205 void logMessage(String message) => 205 void logMessage(String message) =>
206 _config.onLogMessage(currentTestCase, message); 206 _config.onLogMessage(currentTestCase, message);
207 207
208 /**
209 * Description text of the current test group. If multiple groups are nested,
210 * this will contain all of their text concatenated.
211 */
212 String _currentGroup = '';
213
214 /** Separator used between group names and test names. */ 208 /** Separator used between group names and test names. */
215 String groupSep = ' '; 209 String groupSep = ' ';
216 210
217 final List<TestCase> _testCases = new List<TestCase>(); 211 final List<TestCase> _testCases = new List<TestCase>();
218 212
219 /** Tests executed in this suite. */ 213 /** Tests executed in this suite. */
220 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); 214 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases);
221 215
222 /** Setup function called before each test in a group */ 216 /**
223 Function _testSetup; 217 * Setup and teardown functions for a group and its parents, the latter
218 * for chaining.
219 */
220 class GroupContext {
221 /** Setup function called before each test in a group. */
222 Function testSetup = null;
224 223
225 /** Teardown function called after each test in a group */ 224 /** Teardown function called after each test in a group. */
226 Function _testTeardown; 225 Function testTeardown = null;
226
227 /** Setup and teardown functions of parent group, for chaining. */
228 Function parentSetup = null;
229 Function parentTeardown = null;
230
231 /**
232 * Description text of the current test group. If multiple groups are nested,
233 * this will contain all of their text concatenated.
234 */
235 String groupName = '';
236 }
237
238 GroupContext _currentContext = new GroupContext();
227 239
228 int _currentTestCaseIndex = 0; 240 int _currentTestCaseIndex = 0;
229 241
230 /** [TestCase] currently being executed. */ 242 /** [TestCase] currently being executed. */
231 TestCase get currentTestCase => 243 TestCase get currentTestCase =>
232 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) 244 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length)
233 ? testCases[_currentTestCaseIndex] 245 ? testCases[_currentTestCaseIndex]
234 : null; 246 : null;
235 247
236 /** Whether the framework is in an initialized state. */ 248 /** Whether the framework is in an initialized state. */
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after
589 Function protectAsync2(Function callback, {String id}) { 601 Function protectAsync2(Function callback, {String id}) {
590 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; 602 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2;
591 } 603 }
592 604
593 /** 605 /**
594 * Creates a new named group of tests. Calls to group() or test() within the 606 * Creates a new named group of tests. Calls to group() or test() within the
595 * body of the function passed to this will inherit this group's description. 607 * body of the function passed to this will inherit this group's description.
596 */ 608 */
597 void group(String description, void body()) { 609 void group(String description, void body()) {
598 ensureInitialized(); 610 ensureInitialized();
611 // Groups can be nested, so we need to preserve the current
612 // settings for test setup/teardown. We use a local copy here so we
613 // can nest multiple levels; we also have the global parent variables
614 // which are used for chaining.
615 var oldContext = _currentContext;
616 _currentContext = new GroupContext();
617 _currentContext.testSetup = _currentContext.parentSetup =
618 oldContext.testSetup;
619 _currentContext.testTeardown = _currentContext.parentTeardown =
620 oldContext.testTeardown;
621
599 // Concatenate the new group. 622 // Concatenate the new group.
600 final parentGroup = _currentGroup; 623 if (oldContext.groupName != '') {
601 if (_currentGroup != '') {
602 // Add a space. 624 // Add a space.
603 _currentGroup = '$_currentGroup$groupSep$description'; 625 _currentContext.groupName = '${oldContext.groupName}$groupSep$description';
604 } else { 626 } else {
605 // The first group. 627 // The first group.
606 _currentGroup = description; 628 _currentContext.groupName = description;
607 } 629 }
608 630
609 // Groups can be nested, so we need to preserve the current
610 // settings for test setup/teardown.
611 Function parentSetup = _testSetup;
612 Function parentTeardown = _testTeardown;
613 631
614 try { 632 try {
615 _testSetup = null;
616 _testTeardown = null;
617 body(); 633 body();
618 } catch (e, trace) { 634 } catch (e, trace) {
619 var stack = (trace == null) ? '' : ': ${trace.toString()}'; 635 var stack = (trace == null) ? '' : ': ${trace.toString()}';
620 _uncaughtErrorMessage = "${e.toString()}$stack"; 636 _uncaughtErrorMessage = "${e.toString()}$stack";
621 } finally { 637 } finally {
622 // Now that the group is over, restore the previous one. 638 // Now that the group is over, restore the previous one.
623 _currentGroup = parentGroup; 639 _currentContext = oldContext;
624 _testSetup = parentSetup;
625 _testTeardown = parentTeardown;
626 } 640 }
627 } 641 }
628 642
629 /** 643 /**
630 * Register a [setUp] function for a test [group]. This function will 644 * Register a [setUp] function for a test [group]. This function will
631 * be called before each test in the group is run. Note that if groups 645 * be called before each test in the group is run.
632 * are nested only the most locally scoped [setUpTest] function will be run.
633 * [setUp] and [tearDown] should be called within the [group] before any 646 * [setUp] and [tearDown] should be called within the [group] before any
634 * calls to [test]. The [setupTest] function can be asynchronous; in this 647 * calls to [test]. The [setupTest] function can be asynchronous; in this
635 * case it must return a [Future]. 648 * case it must return a [Future].
636 */ 649 */
637 void setUp(Function setupTest) { 650 void setUp(Function setupTest) {
638 _testSetup = setupTest; 651 var parent = _currentContext.parentSetup;
652 _currentContext.testSetup = () {
653 var f = parent == null ? null : parent();
654 if (f is Future) {
655 return f.then((_) => setupTest());
656 } else {
657 return setupTest();
658 }
659 };
639 } 660 }
640 661
641 /** 662 /**
642 * Register a [tearDown] function for a test [group]. This function will 663 * Register a [tearDown] function for a test [group]. This function will
643 * be called after each test in the group is run. Note that if groups 664 * be called after each test in the group is run. Note that if groups
644 * are nested only the most locally scoped [teardownTest] function will be run. 665 * are nested only the most locally scoped [teardownTest] function will be run.
645 * [setUp] and [tearDown] should be called within the [group] before any 666 * [setUp] and [tearDown] should be called within the [group] before any
646 * calls to [test]. The [teardownTest] function can be asynchronous; in this 667 * calls to [test]. The [teardownTest] function can be asynchronous; in this
647 * case it must return a [Future]. 668 * case it must return a [Future].
648 */ 669 */
649 void tearDown(Function teardownTest) { 670 void tearDown(Function teardownTest) {
650 _testTeardown = teardownTest; 671 var parent = _currentContext.parentTeardown;
672 _currentContext.testTeardown = () {
673 var f = teardownTest();
674 if (parent == null) return f;
675 if (f is Future) {
676 // TODO(gram): as _parentTeardown is a global, do we need
677 // to first take a local copy so the value is fixed at the
678 // point that tearDown is called?
679 return f.then((_) => parent());
680 } else {
681 return parent();
682 }
683 };
651 } 684 }
652 685
653 /** Advance to the next test case. */ 686 /** Advance to the next test case. */
654 void _nextTestCase() { 687 void _nextTestCase() {
655 _defer(() { 688 _defer(() {
656 _currentTestCaseIndex++; 689 _currentTestCaseIndex++;
657 _nextBatch(); 690 _nextBatch();
658 }); 691 });
659 } 692 }
660 693
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
705 } else if (testFilter is Function) { 738 } else if (testFilter is Function) {
706 filterFunction = testFilter; 739 filterFunction = testFilter;
707 } 740 }
708 _testCases.retainWhere(filterFunction); 741 _testCases.retainWhere(filterFunction);
709 } 742 }
710 743
711 /** Runs all queued tests, one at a time. */ 744 /** Runs all queued tests, one at a time. */
712 void runTests() { 745 void runTests() {
713 _ensureInitialized(false); 746 _ensureInitialized(false);
714 _currentTestCaseIndex = 0; 747 _currentTestCaseIndex = 0;
715 _currentGroup = ''; 748 _currentContext = new GroupContext();
716 749
717 // If we are soloing a test, remove all the others. 750 // If we are soloing a test, remove all the others.
718 if (_soloTest != null) { 751 if (_soloTest != null) {
719 filterTests((t) => t == _soloTest); 752 filterTests((t) => t == _soloTest);
720 } 753 }
721 754
722 _config.onStart(); 755 _config.onStart();
723 756
724 _defer(() { 757 _defer(() {
725 _nextBatch(); 758 _nextBatch();
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
804 case ERROR: errors++; break; 837 case ERROR: errors++; break;
805 } 838 }
806 } 839 }
807 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); 840 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage);
808 _config.onDone(passed > 0 && failed == 0 && errors == 0 && 841 _config.onDone(passed > 0 && failed == 0 && errors == 0 &&
809 _uncaughtErrorMessage == null); 842 _uncaughtErrorMessage == null);
810 _initialized = false; 843 _initialized = false;
811 } 844 }
812 845
813 String _fullSpec(String spec) { 846 String _fullSpec(String spec) {
814 if (spec == null) return '$_currentGroup'; 847 var group = '${_currentContext.groupName}';
815 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; 848 if (spec == null) return group;
849 return group != '' ? '$group$groupSep$spec' : spec;
816 } 850 }
817 851
818 /** 852 /**
819 * Lazily initializes the test library if not already initialized. 853 * Lazily initializes the test library if not already initialized.
820 */ 854 */
821 void ensureInitialized() { 855 void ensureInitialized() {
822 _ensureInitialized(true); 856 _ensureInitialized(true);
823 } 857 }
824 858
825 void _ensureInitialized(bool configAutoStart) { 859 void _ensureInitialized(bool configAutoStart) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
870 } 904 }
871 905
872 /** Enable a test by ID. */ 906 /** Enable a test by ID. */
873 void enableTest(int testId) => _setTestEnabledState(testId, true); 907 void enableTest(int testId) => _setTestEnabledState(testId, true);
874 908
875 /** Disable a test by ID. */ 909 /** Disable a test by ID. */
876 void disableTest(int testId) => _setTestEnabledState(testId, false); 910 void disableTest(int testId) => _setTestEnabledState(testId, false);
877 911
878 /** Signature for a test function. */ 912 /** Signature for a test function. */
879 typedef dynamic TestFunction(); 913 typedef dynamic TestFunction();
OLDNEW
« no previous file with comments | « pkg/unittest/lib/src/test_case.dart ('k') | pkg/unittest/test/unittest_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698