| 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 * To import this library, install the | 8 * To import this library, install the |
| 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub | 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub |
| 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) | 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 /** Setup function called before each test in a group */ | 208 /** Setup function called before each test in a group */ |
| 209 Function _testSetup; | 209 Function _testSetup; |
| 210 | 210 |
| 211 /** Teardown function called after each test in a group */ | 211 /** Teardown function called after each test in a group */ |
| 212 Function _testTeardown; | 212 Function _testTeardown; |
| 213 | 213 |
| 214 int _currentTestCaseIndex = 0; | 214 int _currentTestCaseIndex = 0; |
| 215 | 215 |
| 216 /** [TestCase] currently being executed. */ | 216 /** [TestCase] currently being executed. */ |
| 217 TestCase get currentTestCase => | 217 TestCase get currentTestCase => |
| 218 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < _testCases.length) | 218 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 219 ? _testCases[_currentTestCaseIndex] | 219 ? testCases[_currentTestCaseIndex] |
| 220 : null; | 220 : null; |
| 221 | 221 |
| 222 /** Whether the framework is in an initialized state. */ | 222 /** Whether the framework is in an initialized state. */ |
| 223 bool _initialized = false; | 223 bool _initialized = false; |
| 224 | 224 |
| 225 String _uncaughtErrorMessage = null; | 225 String _uncaughtErrorMessage = null; |
| 226 | 226 |
| 227 /** Test case result strings. */ | 227 /** Test case result strings. */ |
| 228 // TODO(gram) we should change these constants to use a different string | 228 // TODO(gram) we should change these constants to use a different string |
| 229 // (so that writing 'FAIL' in the middle of a test doesn't | 229 // (so that writing 'FAIL' in the middle of a test doesn't |
| (...skipping 15 matching lines...) Expand all Loading... |
| 245 */ | 245 */ |
| 246 Map testState = {}; | 246 Map testState = {}; |
| 247 | 247 |
| 248 /** | 248 /** |
| 249 * Creates a new test case with the given description and body. The | 249 * Creates a new test case with the given description and body. The |
| 250 * description will include the descriptions of any surrounding group() | 250 * description will include the descriptions of any surrounding group() |
| 251 * calls. | 251 * calls. |
| 252 */ | 252 */ |
| 253 void test(String spec, TestFunction body) { | 253 void test(String spec, TestFunction body) { |
| 254 ensureInitialized(); | 254 ensureInitialized(); |
| 255 _testCases.add(new TestCase._internal(_testCases.length + 1, _fullSpec(spec), | 255 testCases.add(new TestCase._internal(testCases.length + 1, _fullSpec(spec), |
| 256 body)); | 256 body)); |
| 257 } | 257 } |
| 258 | 258 |
| 259 /** | 259 /** |
| 260 * Creates a new test case with the given description and body. The | 260 * Creates a new test case with the given description and body. The |
| 261 * description will include the descriptions of any surrounding group() | 261 * description will include the descriptions of any surrounding group() |
| 262 * calls. | 262 * calls. |
| 263 * | 263 * |
| 264 * "solo_" means that this will be the only test that is run. All other tests | 264 * "solo_" means that this will be the only test that is run. All other tests |
| 265 * will be skipped. This is a convenience function to let you quickly isolate | 265 * will be skipped. This is a convenience function to let you quickly isolate |
| 266 * a single test by adding "solo_" before it to temporarily disable all other | 266 * a single test by adding "solo_" before it to temporarily disable all other |
| 267 * tests. | 267 * tests. |
| 268 */ | 268 */ |
| 269 void solo_test(String spec, TestFunction body) { | 269 void solo_test(String spec, TestFunction body) { |
| 270 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, | 270 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, |
| 271 // all of the solo-ed tests and none of the non-solo-ed ones should run. | 271 // all of the solo-ed tests and none of the non-solo-ed ones should run. |
| 272 if (_soloTest != null) { | 272 if (_soloTest != null) { |
| 273 throw new Exception('Only one test can be soloed right now.'); | 273 throw new Exception('Only one test can be soloed right now.'); |
| 274 } | 274 } |
| 275 | 275 |
| 276 ensureInitialized(); | 276 ensureInitialized(); |
| 277 | 277 |
| 278 _soloTest = new TestCase._internal(_testCases.length + 1, _fullSpec(spec), bod
y); | 278 _soloTest = new TestCase._internal(testCases.length + 1, _fullSpec(spec), body
); |
| 279 _testCases.add(_soloTest); | 279 testCases.add(_soloTest); |
| 280 } | 280 } |
| 281 | 281 |
| 282 /** Sentinel value for [_SpreadArgsHelper]. */ | 282 /** Sentinel value for [_SpreadArgsHelper]. */ |
| 283 class _Sentinel { | 283 class _Sentinel { |
| 284 const _Sentinel(); | 284 const _Sentinel(); |
| 285 } | 285 } |
| 286 | 286 |
| 287 /** Simulates spread arguments using named arguments. */ | 287 /** Simulates spread arguments using named arguments. */ |
| 288 // TODO(sigmund): remove this class and simply use a closure with named | 288 // TODO(sigmund): remove this class and simply use a closure with named |
| 289 // arguments (if still applicable). | 289 // arguments (if still applicable). |
| (...skipping 14 matching lines...) Expand all Loading... |
| 304 : this.callback = callback, | 304 : this.callback = callback, |
| 305 minExpectedCalls = minExpected, | 305 minExpectedCalls = minExpected, |
| 306 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) | 306 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
| 307 ? minExpected | 307 ? minExpected |
| 308 : maxExpected, | 308 : maxExpected, |
| 309 this.isDone = isDone, | 309 this.isDone = isDone, |
| 310 testNum = _currentTestCaseIndex, | 310 testNum = _currentTestCaseIndex, |
| 311 this.id = _makeCallbackId(id, callback) { | 311 this.id = _makeCallbackId(id, callback) { |
| 312 ensureInitialized(); | 312 ensureInitialized(); |
| 313 if (!(_currentTestCaseIndex >= 0 && | 313 if (!(_currentTestCaseIndex >= 0 && |
| 314 _currentTestCaseIndex < _testCases.length && | 314 _currentTestCaseIndex < testCases.length && |
| 315 _testCases[_currentTestCaseIndex] != null)) { | 315 testCases[_currentTestCaseIndex] != null)) { |
| 316 print("No valid test, did you forget to run your test inside a call " | 316 print("No valid test, did you forget to run your test inside a call " |
| 317 "to test()?"); | 317 "to test()?"); |
| 318 } | 318 } |
| 319 assert(_currentTestCaseIndex >= 0 && | 319 assert(_currentTestCaseIndex >= 0 && |
| 320 _currentTestCaseIndex < _testCases.length && | 320 _currentTestCaseIndex < testCases.length && |
| 321 _testCases[_currentTestCaseIndex] != null); | 321 testCases[_currentTestCaseIndex] != null); |
| 322 testCase = _testCases[_currentTestCaseIndex]; | 322 testCase = testCases[_currentTestCaseIndex]; |
| 323 if (isDone != null || minExpected > 0) { | 323 if (isDone != null || minExpected > 0) { |
| 324 testCase._callbackFunctionsOutstanding++; | 324 testCase._callbackFunctionsOutstanding++; |
| 325 complete = false; | 325 complete = false; |
| 326 } else { | 326 } else { |
| 327 complete = true; | 327 complete = true; |
| 328 } | 328 } |
| 329 } | 329 } |
| 330 | 330 |
| 331 static _makeCallbackId(String id, Function callback) { | 331 static _makeCallbackId(String id, Function callback) { |
| 332 // Try to create a reasonable id. | 332 // Try to create a reasonable id. |
| (...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 _currentTestCaseIndex++; | 641 _currentTestCaseIndex++; |
| 642 _nextBatch(); | 642 _nextBatch(); |
| 643 }); | 643 }); |
| 644 } | 644 } |
| 645 | 645 |
| 646 /** | 646 /** |
| 647 * Utility function that can be used to notify the test framework that an | 647 * Utility function that can be used to notify the test framework that an |
| 648 * error was caught outside of this library. | 648 * error was caught outside of this library. |
| 649 */ | 649 */ |
| 650 void _reportTestError(String msg, String trace) { | 650 void _reportTestError(String msg, String trace) { |
| 651 if (_currentTestCaseIndex < _testCases.length) { | 651 if (_currentTestCaseIndex < testCases.length) { |
| 652 final testCase = _testCases[_currentTestCaseIndex]; | 652 final testCase = testCases[_currentTestCaseIndex]; |
| 653 testCase.error(msg, trace); | 653 testCase.error(msg, trace); |
| 654 } else { | 654 } else { |
| 655 _uncaughtErrorMessage = "$msg: $trace"; | 655 _uncaughtErrorMessage = "$msg: $trace"; |
| 656 } | 656 } |
| 657 } | 657 } |
| 658 | 658 |
| 659 /** | 659 /** |
| 660 * Runs [callback] at the end of the event loop. Note that we don't wrap | 660 * Runs [callback] at the end of the event loop. Note that we don't wrap |
| 661 * the callback in guardAsync; this is for test framework functions which | 661 * the callback in guardAsync; this is for test framework functions which |
| 662 * should not be throwing unexpected exceptions that end up failing test | 662 * should not be throwing unexpected exceptions that end up failing test |
| (...skipping 20 matching lines...) Expand all Loading... |
| 683 void filterTests(testFilter) { | 683 void filterTests(testFilter) { |
| 684 var filterFunction; | 684 var filterFunction; |
| 685 if (testFilter is String) { | 685 if (testFilter is String) { |
| 686 RegExp re = new RegExp(testFilter); | 686 RegExp re = new RegExp(testFilter); |
| 687 filterFunction = (t) => re.hasMatch(t.description); | 687 filterFunction = (t) => re.hasMatch(t.description); |
| 688 } else if (testFilter is RegExp) { | 688 } else if (testFilter is RegExp) { |
| 689 filterFunction = (t) => testFilter.hasMatch(t.description); | 689 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 690 } else if (testFilter is Function) { | 690 } else if (testFilter is Function) { |
| 691 filterFunction = testFilter; | 691 filterFunction = testFilter; |
| 692 } | 692 } |
| 693 _testCases.retainWhere(filterFunction); | 693 testCases.retainWhere(filterFunction); |
| 694 } | 694 } |
| 695 | 695 |
| 696 /** Runs all queued tests, one at a time. */ | 696 /** Runs all queued tests, one at a time. */ |
| 697 void runTests() { | 697 void runTests() { |
| 698 _ensureInitialized(false); | 698 _ensureInitialized(false); |
| 699 _currentTestCaseIndex = 0; | 699 _currentTestCaseIndex = 0; |
| 700 _currentGroup = ''; | 700 _currentGroup = ''; |
| 701 | 701 |
| 702 // If we are soloing a test, remove all the others. | 702 // If we are soloing a test, remove all the others. |
| 703 if (_soloTest != null) { | 703 if (_soloTest != null) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 void registerException(e, [trace]) { | 738 void registerException(e, [trace]) { |
| 739 _registerException(_currentTestCaseIndex, e, trace); | 739 _registerException(_currentTestCaseIndex, e, trace); |
| 740 } | 740 } |
| 741 | 741 |
| 742 /** | 742 /** |
| 743 * Registers that an exception was caught for the current test. | 743 * Registers that an exception was caught for the current test. |
| 744 */ | 744 */ |
| 745 void _registerException(testNum, e, [trace]) { | 745 void _registerException(testNum, e, [trace]) { |
| 746 trace = trace == null ? '' : trace.toString(); | 746 trace = trace == null ? '' : trace.toString(); |
| 747 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 747 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 748 if (_testCases[testNum].result == null) { | 748 if (testCases[testNum].result == null) { |
| 749 _testCases[testNum].fail(message, trace); | 749 testCases[testNum].fail(message, trace); |
| 750 } else { | 750 } else { |
| 751 _testCases[testNum].error(message, trace); | 751 testCases[testNum].error(message, trace); |
| 752 } | 752 } |
| 753 } | 753 } |
| 754 | 754 |
| 755 /** | 755 /** |
| 756 * Runs a batch of tests, yielding whenever an asynchronous test starts | 756 * Runs a batch of tests, yielding whenever an asynchronous test starts |
| 757 * running. Tests will resume executing when such asynchronous test calls | 757 * running. Tests will resume executing when such asynchronous test calls |
| 758 * [done] or if it fails with an exception. | 758 * [done] or if it fails with an exception. |
| 759 */ | 759 */ |
| 760 void _nextBatch() { | 760 void _nextBatch() { |
| 761 while (true) { | 761 while (true) { |
| 762 if (_currentTestCaseIndex >= _testCases.length) { | 762 if (_currentTestCaseIndex >= testCases.length) { |
| 763 _completeTests(); | 763 _completeTests(); |
| 764 break; | 764 break; |
| 765 } | 765 } |
| 766 final testCase = _testCases[_currentTestCaseIndex]; | 766 final testCase = testCases[_currentTestCaseIndex]; |
| 767 var f = _guardAsync(testCase._run, null, _currentTestCaseIndex); | 767 var f = _guardAsync(testCase._run, null, _currentTestCaseIndex); |
| 768 if (f != null) { | 768 if (f != null) { |
| 769 f.whenComplete(() { | 769 f.whenComplete(() { |
| 770 _nextTestCase(); // Schedule the next test. | 770 _nextTestCase(); // Schedule the next test. |
| 771 }); | 771 }); |
| 772 break; | 772 break; |
| 773 } | 773 } |
| 774 _currentTestCaseIndex++; | 774 _currentTestCaseIndex++; |
| 775 } | 775 } |
| 776 } | 776 } |
| 777 | 777 |
| 778 /** Publish results on the page and notify controller. */ | 778 /** Publish results on the page and notify controller. */ |
| 779 void _completeTests() { | 779 void _completeTests() { |
| 780 if (!_initialized) return; | 780 if (!_initialized) return; |
| 781 int passed = 0; | 781 int passed = 0; |
| 782 int failed = 0; | 782 int failed = 0; |
| 783 int errors = 0; | 783 int errors = 0; |
| 784 | 784 |
| 785 for (TestCase t in _testCases) { | 785 for (TestCase t in testCases) { |
| 786 switch (t.result) { | 786 switch (t.result) { |
| 787 case PASS: passed++; break; | 787 case PASS: passed++; break; |
| 788 case FAIL: failed++; break; | 788 case FAIL: failed++; break; |
| 789 case ERROR: errors++; break; | 789 case ERROR: errors++; break; |
| 790 } | 790 } |
| 791 } | 791 } |
| 792 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 792 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); |
| 793 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 793 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 794 _uncaughtErrorMessage == null); | 794 _uncaughtErrorMessage == null); |
| 795 _initialized = false; | 795 _initialized = false; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 824 | 824 |
| 825 if (configAutoStart && _config.autoStart) { | 825 if (configAutoStart && _config.autoStart) { |
| 826 // Immediately queue the suite up. It will run after a timeout (i.e. after | 826 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 827 // main() has returned). | 827 // main() has returned). |
| 828 _defer(runTests); | 828 _defer(runTests); |
| 829 } | 829 } |
| 830 } | 830 } |
| 831 | 831 |
| 832 /** Select a solo test by ID. */ | 832 /** Select a solo test by ID. */ |
| 833 void setSoloTest(int id) { | 833 void setSoloTest(int id) { |
| 834 for (var i = 0; i < _testCases.length; i++) { | 834 for (var i = 0; i < testCases.length; i++) { |
| 835 if (_testCases[i].id == id) { | 835 if (testCases[i].id == id) { |
| 836 _soloTest = _testCases[i]; | 836 _soloTest = testCases[i]; |
| 837 break; | 837 break; |
| 838 } | 838 } |
| 839 } | 839 } |
| 840 } | 840 } |
| 841 | 841 |
| 842 /** Enable/disable a test by ID. */ | 842 /** Enable/disable a test by ID. */ |
| 843 void _setTestEnabledState(int testId, bool state) { | 843 void _setTestEnabledState(int testId, bool state) { |
| 844 // Try fast path first. | 844 // Try fast path first. |
| 845 if (_testCases.length > testId && _testCases[testId].id == testId) { | 845 if (testCases.length > testId && testCases[testId].id == testId) { |
| 846 _testCases[testId].enabled = state; | 846 testCases[testId].enabled = state; |
| 847 } else { | 847 } else { |
| 848 for (var i = 0; i < _testCases.length; i++) { | 848 for (var i = 0; i < testCases.length; i++) { |
| 849 if (_testCases[i].id == testId) { | 849 if (testCases[i].id == testId) { |
| 850 _testCases[i].enabled = state; | 850 testCases[i].enabled = state; |
| 851 break; | 851 break; |
| 852 } | 852 } |
| 853 } | 853 } |
| 854 } | 854 } |
| 855 } | 855 } |
| 856 | 856 |
| 857 /** Enable a test by ID. */ | 857 /** Enable a test by ID. */ |
| 858 void enableTest(int testId) => _setTestEnabledState(testId, true); | 858 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 859 | 859 |
| 860 /** Disable a test by ID. */ | 860 /** Disable a test by ID. */ |
| 861 void disableTest(int testId) => _setTestEnabledState(testId, false); | 861 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 862 | 862 |
| 863 /** Signature for a test function. */ | 863 /** Signature for a test function. */ |
| 864 typedef dynamic TestFunction(); | 864 typedef dynamic TestFunction(); |
| OLD | NEW |