| 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 * ## 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 Loading... |
| 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 |
| 208 /** Separator used between group names and test names. */ | 214 /** Separator used between group names and test names. */ |
| 209 String groupSep = ' '; | 215 String groupSep = ' '; |
| 210 | 216 |
| 211 final List<TestCase> _testCases = new List<TestCase>(); | 217 final List<TestCase> _testCases = new List<TestCase>(); |
| 212 | 218 |
| 213 /** Tests executed in this suite. */ | 219 /** Tests executed in this suite. */ |
| 214 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); | 220 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); |
| 215 | 221 |
| 216 /** | 222 /** Setup function called before each test in a group */ |
| 217 * Setup and teardown functions for a group and its parents, the latter | 223 Function _testSetup; |
| 218 * for chaining. | |
| 219 */ | |
| 220 class GroupContext { | |
| 221 /** Setup function called before each test in a group. */ | |
| 222 Function testSetup = null; | |
| 223 | 224 |
| 224 /** Teardown function called after each test in a group. */ | 225 /** Teardown function called after each test in a group */ |
| 225 Function testTeardown = null; | 226 Function _testTeardown; |
| 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(); | |
| 239 | 227 |
| 240 int _currentTestCaseIndex = 0; | 228 int _currentTestCaseIndex = 0; |
| 241 | 229 |
| 242 /** [TestCase] currently being executed. */ | 230 /** [TestCase] currently being executed. */ |
| 243 TestCase get currentTestCase => | 231 TestCase get currentTestCase => |
| 244 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 232 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 245 ? testCases[_currentTestCaseIndex] | 233 ? testCases[_currentTestCaseIndex] |
| 246 : null; | 234 : null; |
| 247 | 235 |
| 248 /** Whether the framework is in an initialized state. */ | 236 /** Whether the framework is in an initialized state. */ |
| (...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 601 Function protectAsync2(Function callback, {String id}) { | 589 Function protectAsync2(Function callback, {String id}) { |
| 602 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; | 590 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
| 603 } | 591 } |
| 604 | 592 |
| 605 /** | 593 /** |
| 606 * Creates a new named group of tests. Calls to group() or test() within the | 594 * Creates a new named group of tests. Calls to group() or test() within the |
| 607 * body of the function passed to this will inherit this group's description. | 595 * body of the function passed to this will inherit this group's description. |
| 608 */ | 596 */ |
| 609 void group(String description, void body()) { | 597 void group(String description, void body()) { |
| 610 ensureInitialized(); | 598 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 | |
| 622 // Concatenate the new group. | 599 // Concatenate the new group. |
| 623 if (oldContext.groupName != '') { | 600 final parentGroup = _currentGroup; |
| 601 if (_currentGroup != '') { |
| 624 // Add a space. | 602 // Add a space. |
| 625 _currentContext.groupName = '${oldContext.groupName}$groupSep$description'; | 603 _currentGroup = '$_currentGroup$groupSep$description'; |
| 626 } else { | 604 } else { |
| 627 // The first group. | 605 // The first group. |
| 628 _currentContext.groupName = description; | 606 _currentGroup = description; |
| 629 } | 607 } |
| 630 | 608 |
| 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; |
| 631 | 613 |
| 632 try { | 614 try { |
| 615 _testSetup = null; |
| 616 _testTeardown = null; |
| 633 body(); | 617 body(); |
| 634 } catch (e, trace) { | 618 } catch (e, trace) { |
| 635 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | 619 var stack = (trace == null) ? '' : ': ${trace.toString()}'; |
| 636 _uncaughtErrorMessage = "${e.toString()}$stack"; | 620 _uncaughtErrorMessage = "${e.toString()}$stack"; |
| 637 } finally { | 621 } finally { |
| 638 // Now that the group is over, restore the previous one. | 622 // Now that the group is over, restore the previous one. |
| 639 _currentContext = oldContext; | 623 _currentGroup = parentGroup; |
| 624 _testSetup = parentSetup; |
| 625 _testTeardown = parentTeardown; |
| 640 } | 626 } |
| 641 } | 627 } |
| 642 | 628 |
| 643 /** | 629 /** |
| 644 * Register a [setUp] function for a test [group]. This function will | 630 * Register a [setUp] function for a test [group]. This function will |
| 645 * be called before each test in the group is run. | 631 * be called before each test in the group is run. Note that if groups |
| 632 * are nested only the most locally scoped [setUpTest] function will be run. |
| 646 * [setUp] and [tearDown] should be called within the [group] before any | 633 * [setUp] and [tearDown] should be called within the [group] before any |
| 647 * calls to [test]. The [setupTest] function can be asynchronous; in this | 634 * calls to [test]. The [setupTest] function can be asynchronous; in this |
| 648 * case it must return a [Future]. | 635 * case it must return a [Future]. |
| 649 */ | 636 */ |
| 650 void setUp(Function setupTest) { | 637 void setUp(Function setupTest) { |
| 651 var parent = _currentContext.parentSetup; | 638 _testSetup = setupTest; |
| 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 }; | |
| 660 } | 639 } |
| 661 | 640 |
| 662 /** | 641 /** |
| 663 * Register a [tearDown] function for a test [group]. This function will | 642 * Register a [tearDown] function for a test [group]. This function will |
| 664 * be called after each test in the group is run. Note that if groups | 643 * be called after each test in the group is run. Note that if groups |
| 665 * are nested only the most locally scoped [teardownTest] function will be run. | 644 * are nested only the most locally scoped [teardownTest] function will be run. |
| 666 * [setUp] and [tearDown] should be called within the [group] before any | 645 * [setUp] and [tearDown] should be called within the [group] before any |
| 667 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 646 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 668 * case it must return a [Future]. | 647 * case it must return a [Future]. |
| 669 */ | 648 */ |
| 670 void tearDown(Function teardownTest) { | 649 void tearDown(Function teardownTest) { |
| 671 var parent = _currentContext.parentTeardown; | 650 _testTeardown = teardownTest; |
| 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 }; | |
| 684 } | 651 } |
| 685 | 652 |
| 686 /** Advance to the next test case. */ | 653 /** Advance to the next test case. */ |
| 687 void _nextTestCase() { | 654 void _nextTestCase() { |
| 688 _defer(() { | 655 _defer(() { |
| 689 _currentTestCaseIndex++; | 656 _currentTestCaseIndex++; |
| 690 _nextBatch(); | 657 _nextBatch(); |
| 691 }); | 658 }); |
| 692 } | 659 } |
| 693 | 660 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 } else if (testFilter is Function) { | 705 } else if (testFilter is Function) { |
| 739 filterFunction = testFilter; | 706 filterFunction = testFilter; |
| 740 } | 707 } |
| 741 _testCases.retainWhere(filterFunction); | 708 _testCases.retainWhere(filterFunction); |
| 742 } | 709 } |
| 743 | 710 |
| 744 /** Runs all queued tests, one at a time. */ | 711 /** Runs all queued tests, one at a time. */ |
| 745 void runTests() { | 712 void runTests() { |
| 746 _ensureInitialized(false); | 713 _ensureInitialized(false); |
| 747 _currentTestCaseIndex = 0; | 714 _currentTestCaseIndex = 0; |
| 748 _currentContext = new GroupContext(); | 715 _currentGroup = ''; |
| 749 | 716 |
| 750 // If we are soloing a test, remove all the others. | 717 // If we are soloing a test, remove all the others. |
| 751 if (_soloTest != null) { | 718 if (_soloTest != null) { |
| 752 filterTests((t) => t == _soloTest); | 719 filterTests((t) => t == _soloTest); |
| 753 } | 720 } |
| 754 | 721 |
| 755 _config.onStart(); | 722 _config.onStart(); |
| 756 | 723 |
| 757 _defer(() { | 724 _defer(() { |
| 758 _nextBatch(); | 725 _nextBatch(); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 837 case ERROR: errors++; break; | 804 case ERROR: errors++; break; |
| 838 } | 805 } |
| 839 } | 806 } |
| 840 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 807 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 841 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 808 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 842 _uncaughtErrorMessage == null); | 809 _uncaughtErrorMessage == null); |
| 843 _initialized = false; | 810 _initialized = false; |
| 844 } | 811 } |
| 845 | 812 |
| 846 String _fullSpec(String spec) { | 813 String _fullSpec(String spec) { |
| 847 var group = '${_currentContext.groupName}'; | 814 if (spec == null) return '$_currentGroup'; |
| 848 if (spec == null) return group; | 815 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; |
| 849 return group != '' ? '$group$groupSep$spec' : spec; | |
| 850 } | 816 } |
| 851 | 817 |
| 852 /** | 818 /** |
| 853 * Lazily initializes the test library if not already initialized. | 819 * Lazily initializes the test library if not already initialized. |
| 854 */ | 820 */ |
| 855 void ensureInitialized() { | 821 void ensureInitialized() { |
| 856 _ensureInitialized(true); | 822 _ensureInitialized(true); |
| 857 } | 823 } |
| 858 | 824 |
| 859 void _ensureInitialized(bool configAutoStart) { | 825 void _ensureInitialized(bool configAutoStart) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 904 } | 870 } |
| 905 | 871 |
| 906 /** Enable a test by ID. */ | 872 /** Enable a test by ID. */ |
| 907 void enableTest(int testId) => _setTestEnabledState(testId, true); | 873 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 908 | 874 |
| 909 /** Disable a test by ID. */ | 875 /** Disable a test by ID. */ |
| 910 void disableTest(int testId) => _setTestEnabledState(testId, false); | 876 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 911 | 877 |
| 912 /** Signature for a test function. */ | 878 /** Signature for a test function. */ |
| 913 typedef dynamic TestFunction(); | 879 typedef dynamic TestFunction(); |
| OLD | NEW |