| 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 | |
| 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 final GroupContext parent; |
| 224 | 222 |
| 225 /** Teardown function called after each test in a group */ | 223 /** Description text of the current test group. */ |
| 226 Function _testTeardown; | 224 final String _name; |
| 225 |
| 226 /** Setup function called before each test in a group. */ |
| 227 Function _testSetup; |
| 228 |
| 229 get testSetup => _testSetup; |
| 230 |
| 231 get parentSetup => (parent == null) ? null : parent.testSetup; |
| 232 |
| 233 set testSetup(Function setup) { |
| 234 var preSetup = parentSetup; |
| 235 if (preSetup == null) { |
| 236 _testSetup = setup; |
| 237 } else { |
| 238 _testSetup = () { |
| 239 var f = preSetup(); |
| 240 if (f is Future) { |
| 241 return f.then((_) => setup()); |
| 242 } else { |
| 243 return setup(); |
| 244 } |
| 245 }; |
| 246 } |
| 247 } |
| 248 |
| 249 /** Teardown function called after each test in a group. */ |
| 250 Function _testTeardown; |
| 251 |
| 252 get testTeardown => _testTeardown; |
| 253 |
| 254 get parentTeardown => (parent == null) ? null : parent.testTeardown; |
| 255 |
| 256 set testTeardown(Function teardown) { |
| 257 var postTeardown = parentTeardown; |
| 258 if (postTeardown == null) { |
| 259 _testTeardown = teardown; |
| 260 } else { |
| 261 _testTeardown = () { |
| 262 var f = teardown(); |
| 263 if (f is Future) { |
| 264 return f.then((_) => postTeardown()); |
| 265 } else { |
| 266 return postTeardown(); |
| 267 } |
| 268 }; |
| 269 } |
| 270 } |
| 271 |
| 272 String get fullName => (parent == null || parent == _rootContext) |
| 273 ? _name |
| 274 : "${parent.fullName}$groupSep$_name"; |
| 275 |
| 276 GroupContext([this.parent, this._name = '']) { |
| 277 _testSetup = parentSetup; |
| 278 _testTeardown = parentTeardown; |
| 279 } |
| 280 } |
| 281 |
| 282 // We use a 'dummy' context for the top level to eliminate null |
| 283 // checks when querying the context. This allows us to easily |
| 284 // support top-level setUp/tearDown functions as well. |
| 285 GroupContext _rootContext = new GroupContext(); |
| 286 GroupContext _currentContext = _rootContext; |
| 227 | 287 |
| 228 int _currentTestCaseIndex = 0; | 288 int _currentTestCaseIndex = 0; |
| 229 | 289 |
| 230 /** [TestCase] currently being executed. */ | 290 /** [TestCase] currently being executed. */ |
| 231 TestCase get currentTestCase => | 291 TestCase get currentTestCase => |
| 232 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 292 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 233 ? testCases[_currentTestCaseIndex] | 293 ? testCases[_currentTestCaseIndex] |
| 234 : null; | 294 : null; |
| 235 | 295 |
| 236 /** Whether the framework is in an initialized state. */ | 296 /** Whether the framework is in an initialized state. */ |
| (...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 Function protectAsync2(Function callback, {String id}) { | 649 Function protectAsync2(Function callback, {String id}) { |
| 590 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; | 650 return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
| 591 } | 651 } |
| 592 | 652 |
| 593 /** | 653 /** |
| 594 * Creates a new named group of tests. Calls to group() or test() within the | 654 * 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. | 655 * body of the function passed to this will inherit this group's description. |
| 596 */ | 656 */ |
| 597 void group(String description, void body()) { | 657 void group(String description, void body()) { |
| 598 ensureInitialized(); | 658 ensureInitialized(); |
| 599 // Concatenate the new group. | 659 _currentContext = new GroupContext(_currentContext, description); |
| 600 final parentGroup = _currentGroup; | |
| 601 if (_currentGroup != '') { | |
| 602 // Add a space. | |
| 603 _currentGroup = '$_currentGroup$groupSep$description'; | |
| 604 } else { | |
| 605 // The first group. | |
| 606 _currentGroup = description; | |
| 607 } | |
| 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; | |
| 613 | |
| 614 try { | 660 try { |
| 615 _testSetup = null; | |
| 616 _testTeardown = null; | |
| 617 body(); | 661 body(); |
| 618 } catch (e, trace) { | 662 } catch (e, trace) { |
| 619 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | 663 var stack = (trace == null) ? '' : ': ${trace.toString()}'; |
| 620 _uncaughtErrorMessage = "${e.toString()}$stack"; | 664 _uncaughtErrorMessage = "${e.toString()}$stack"; |
| 621 } finally { | 665 } finally { |
| 622 // Now that the group is over, restore the previous one. | 666 // Now that the group is over, restore the previous one. |
| 623 _currentGroup = parentGroup; | 667 _currentContext = _currentContext.parent; |
| 624 _testSetup = parentSetup; | |
| 625 _testTeardown = parentTeardown; | |
| 626 } | 668 } |
| 627 } | 669 } |
| 628 | 670 |
| 629 /** | 671 /** |
| 630 * Register a [setUp] function for a test [group]. This function will | 672 * 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 | 673 * 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 | 674 * [setUp] and [tearDown] should be called within the [group] before any |
| 634 * calls to [test]. The [setupTest] function can be asynchronous; in this | 675 * calls to [test]. The [setupTest] function can be asynchronous; in this |
| 635 * case it must return a [Future]. | 676 * case it must return a [Future]. |
| 636 */ | 677 */ |
| 637 void setUp(Function setupTest) { | 678 void setUp(Function setupTest) { |
| 638 _testSetup = setupTest; | 679 _currentContext.testSetup = setupTest; |
| 639 } | 680 } |
| 640 | 681 |
| 641 /** | 682 /** |
| 642 * Register a [tearDown] function for a test [group]. This function will | 683 * 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 | 684 * 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. | 685 * are nested only the most locally scoped [teardownTest] function will be run. |
| 645 * [setUp] and [tearDown] should be called within the [group] before any | 686 * [setUp] and [tearDown] should be called within the [group] before any |
| 646 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 687 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 647 * case it must return a [Future]. | 688 * case it must return a [Future]. |
| 648 */ | 689 */ |
| 649 void tearDown(Function teardownTest) { | 690 void tearDown(Function teardownTest) { |
| 650 _testTeardown = teardownTest; | 691 _currentContext.testTeardown = teardownTest; |
| 651 } | 692 } |
| 652 | 693 |
| 653 /** Advance to the next test case. */ | 694 /** Advance to the next test case. */ |
| 654 void _nextTestCase() { | 695 void _nextTestCase() { |
| 655 _defer(() { | 696 _defer(() { |
| 656 _currentTestCaseIndex++; | 697 _currentTestCaseIndex++; |
| 657 _nextBatch(); | 698 _nextBatch(); |
| 658 }); | 699 }); |
| 659 } | 700 } |
| 660 | 701 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 705 } else if (testFilter is Function) { | 746 } else if (testFilter is Function) { |
| 706 filterFunction = testFilter; | 747 filterFunction = testFilter; |
| 707 } | 748 } |
| 708 _testCases.retainWhere(filterFunction); | 749 _testCases.retainWhere(filterFunction); |
| 709 } | 750 } |
| 710 | 751 |
| 711 /** Runs all queued tests, one at a time. */ | 752 /** Runs all queued tests, one at a time. */ |
| 712 void runTests() { | 753 void runTests() { |
| 713 _ensureInitialized(false); | 754 _ensureInitialized(false); |
| 714 _currentTestCaseIndex = 0; | 755 _currentTestCaseIndex = 0; |
| 715 _currentGroup = ''; | |
| 716 | 756 |
| 717 // If we are soloing a test, remove all the others. | 757 // If we are soloing a test, remove all the others. |
| 718 if (_soloTest != null) { | 758 if (_soloTest != null) { |
| 719 filterTests((t) => t == _soloTest); | 759 filterTests((t) => t == _soloTest); |
| 720 } | 760 } |
| 721 | 761 |
| 722 _config.onStart(); | 762 _config.onStart(); |
| 723 | 763 |
| 724 _defer(() { | 764 _defer(() { |
| 725 _nextBatch(); | 765 _nextBatch(); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 804 case ERROR: errors++; break; | 844 case ERROR: errors++; break; |
| 805 } | 845 } |
| 806 } | 846 } |
| 807 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 847 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 808 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 848 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 809 _uncaughtErrorMessage == null); | 849 _uncaughtErrorMessage == null); |
| 810 _initialized = false; | 850 _initialized = false; |
| 811 } | 851 } |
| 812 | 852 |
| 813 String _fullSpec(String spec) { | 853 String _fullSpec(String spec) { |
| 814 if (spec == null) return '$_currentGroup'; | 854 var group = '${_currentContext.fullName}'; |
| 815 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; | 855 if (spec == null) return group; |
| 856 return group != '' ? '$group$groupSep$spec' : spec; |
| 816 } | 857 } |
| 817 | 858 |
| 818 /** | 859 /** |
| 819 * Lazily initializes the test library if not already initialized. | 860 * Lazily initializes the test library if not already initialized. |
| 820 */ | 861 */ |
| 821 void ensureInitialized() { | 862 void ensureInitialized() { |
| 822 _ensureInitialized(true); | 863 _ensureInitialized(true); |
| 823 } | 864 } |
| 824 | 865 |
| 825 void _ensureInitialized(bool configAutoStart) { | 866 void _ensureInitialized(bool configAutoStart) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 870 } | 911 } |
| 871 | 912 |
| 872 /** Enable a test by ID. */ | 913 /** Enable a test by ID. */ |
| 873 void enableTest(int testId) => _setTestEnabledState(testId, true); | 914 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 874 | 915 |
| 875 /** Disable a test by ID. */ | 916 /** Disable a test by ID. */ |
| 876 void disableTest(int testId) => _setTestEnabledState(testId, false); | 917 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 877 | 918 |
| 878 /** Signature for a test function. */ | 919 /** Signature for a test function. */ |
| 879 typedef dynamic TestFunction(); | 920 typedef dynamic TestFunction(); |
| OLD | NEW |