Chromium Code Reviews| 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 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 | 193 |
| 194 /** Separator used between group names and test names. */ | 194 /** Separator used between group names and test names. */ |
| 195 String groupSep = ' '; | 195 String groupSep = ' '; |
| 196 | 196 |
| 197 final List<TestCase> _testCases = new List<TestCase>(); | 197 final List<TestCase> _testCases = new List<TestCase>(); |
| 198 | 198 |
| 199 /** Tests executed in this suite. */ | 199 /** Tests executed in this suite. */ |
| 200 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); | 200 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); |
| 201 | 201 |
| 202 /** | 202 /** |
| 203 * Interval (in msecs) after which synchronous tests will insert an async | |
| 204 * delay to allow DOM or other updates. | |
| 205 */ | |
| 206 const int BREATH_INTERVAL = 200; | |
| 207 | |
| 208 /** | |
| 203 * The set of tests to run can be restricted by using [solo_test] and | 209 * The set of tests to run can be restricted by using [solo_test] and |
| 204 * [solo_group]. | 210 * [solo_group]. |
| 205 * As groups can be nested we use a counter to keep track of the nest level | 211 * As groups can be nested we use a counter to keep track of the nest level |
| 206 * of soloing, and a flag to tell if we have seen any solo tests. | 212 * of soloing, and a flag to tell if we have seen any solo tests. |
| 207 */ | 213 */ |
| 208 int _soloNestingLevel = 0; | 214 int _soloNestingLevel = 0; |
| 209 bool _soloTestSeen = false; | 215 bool _soloTestSeen = false; |
| 210 | 216 |
| 211 /** | 217 /** |
| 212 * Setup and teardown functions for a group and its parents, the latter | 218 * Setup and teardown functions for a group and its parents, the latter |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 286 TestCase get currentTestCase => | 292 TestCase get currentTestCase => |
| 287 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 293 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) |
| 288 ? testCases[_currentTestCaseIndex] | 294 ? testCases[_currentTestCaseIndex] |
| 289 : null; | 295 : null; |
| 290 | 296 |
| 291 /** Whether the framework is in an initialized state. */ | 297 /** Whether the framework is in an initialized state. */ |
| 292 bool _initialized = false; | 298 bool _initialized = false; |
| 293 | 299 |
| 294 String _uncaughtErrorMessage = null; | 300 String _uncaughtErrorMessage = null; |
| 295 | 301 |
| 302 /** Time since we last gave non-sync code a chance to be scheduled. */ | |
| 303 int _lastBreath = new DateTime.now().millisecondsSinceEpoch; | |
| 304 | |
| 296 /** Test case result strings. */ | 305 /** Test case result strings. */ |
| 297 // TODO(gram) we should change these constants to use a different string | 306 // TODO(gram) we should change these constants to use a different string |
| 298 // (so that writing 'FAIL' in the middle of a test doesn't | 307 // (so that writing 'FAIL' in the middle of a test doesn't |
| 299 // imply that the test fails). We can't do it without also changing | 308 // imply that the test fails). We can't do it without also changing |
| 300 // the testrunner and test.dart though. | 309 // the testrunner and test.dart though. |
| 301 const PASS = 'pass'; | 310 const PASS = 'pass'; |
| 302 const FAIL = 'fail'; | 311 const FAIL = 'fail'; |
| 303 const ERROR = 'error'; | 312 const ERROR = 'error'; |
| 304 | 313 |
| 305 /** | 314 /** |
| (...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 637 * [setUp] and [tearDown] should be called within the [group] before any | 646 * [setUp] and [tearDown] should be called within the [group] before any |
| 638 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 647 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 639 * case it must return a [Future]. | 648 * case it must return a [Future]. |
| 640 */ | 649 */ |
| 641 void tearDown(Function teardownTest) { | 650 void tearDown(Function teardownTest) { |
| 642 _currentContext.testTeardown = teardownTest; | 651 _currentContext.testTeardown = teardownTest; |
| 643 } | 652 } |
| 644 | 653 |
| 645 /** Advance to the next test case. */ | 654 /** Advance to the next test case. */ |
| 646 void _nextTestCase() { | 655 void _nextTestCase() { |
| 647 runAsync(() { | 656 _currentTestCaseIndex++; |
| 648 _currentTestCaseIndex++; | 657 _runTest(); |
| 649 _nextBatch(); | |
| 650 }); | |
| 651 } | 658 } |
| 652 | 659 |
| 653 /** | 660 /** |
| 654 * Utility function that can be used to notify the test framework that an | 661 * Utility function that can be used to notify the test framework that an |
| 655 * error was caught outside of this library. | 662 * error was caught outside of this library. |
| 656 */ | 663 */ |
| 657 void _reportTestError(String msg, trace) { | 664 void _reportTestError(String msg, trace) { |
| 658 if (_currentTestCaseIndex < testCases.length) { | 665 if (_currentTestCaseIndex < testCases.length) { |
| 659 final testCase = testCases[_currentTestCaseIndex]; | 666 final testCase = testCases[_currentTestCaseIndex]; |
| 660 testCase.error(msg, trace); | 667 testCase.error(msg, trace); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 684 } else if (testFilter is Function) { | 691 } else if (testFilter is Function) { |
| 685 filterFunction = testFilter; | 692 filterFunction = testFilter; |
| 686 } | 693 } |
| 687 _testCases.retainWhere(filterFunction); | 694 _testCases.retainWhere(filterFunction); |
| 688 } | 695 } |
| 689 | 696 |
| 690 /** Runs all queued tests, one at a time. */ | 697 /** Runs all queued tests, one at a time. */ |
| 691 void runTests() { | 698 void runTests() { |
| 692 _ensureInitialized(false); | 699 _ensureInitialized(false); |
| 693 _currentTestCaseIndex = 0; | 700 _currentTestCaseIndex = 0; |
| 694 | |
| 695 _config.onStart(); | 701 _config.onStart(); |
| 696 | 702 _runTest(); |
|
Siggi Cherem (dart-lang)
2013/07/25 00:50:42
I think this one needs to still call runAsync?
| |
| 697 runAsync(() { | |
| 698 _nextBatch(); | |
| 699 }); | |
| 700 } | 703 } |
| 701 | 704 |
| 702 /** | 705 /** |
| 703 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is | 706 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 704 * passed to the corresponding test. | 707 * passed to the corresponding test. |
| 705 * | 708 * |
| 706 * The value returned by [tryBody] (if any) is returned by [guardAsync]. | 709 * The value returned by [tryBody] (if any) is returned by [guardAsync]. |
| 707 */ | 710 */ |
| 708 guardAsync(Function tryBody) { | 711 guardAsync(Function tryBody) { |
| 709 return _guardAsync(tryBody, null, currentTestCase); | 712 return _guardAsync(tryBody, null, currentTestCase); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 733 void _registerException(TestCase testCase, e, [trace]) { | 736 void _registerException(TestCase testCase, e, [trace]) { |
| 734 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 737 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 735 if (testCase.result == null) { | 738 if (testCase.result == null) { |
| 736 testCase.fail(message, trace); | 739 testCase.fail(message, trace); |
| 737 } else { | 740 } else { |
| 738 testCase.error(message, trace); | 741 testCase.error(message, trace); |
| 739 } | 742 } |
| 740 } | 743 } |
| 741 | 744 |
| 742 /** | 745 /** |
| 743 * Runs a batch of tests, yielding whenever an asynchronous test starts | 746 * Runs the next test. |
| 744 * running. Tests will resume executing when such asynchronous test calls | |
| 745 * [done] or if it fails with an exception. | |
| 746 */ | 747 */ |
| 747 void _nextBatch() { | 748 void _runTest() { |
| 748 while (true) { | 749 if (_currentTestCaseIndex >= testCases.length) { |
| 749 if (_currentTestCaseIndex >= testCases.length) { | 750 _completeTests(); |
| 750 _completeTests(); | 751 } else { |
| 751 break; | |
| 752 } | |
| 753 final testCase = testCases[_currentTestCaseIndex]; | 752 final testCase = testCases[_currentTestCaseIndex]; |
| 754 var f = _guardAsync(testCase._run, null, testCase); | 753 var f = _guardAsync(testCase._run, null, testCase); |
| 755 if (f != null) { | 754 f.whenComplete(() { |
| 756 f.whenComplete(() { | 755 var now = new DateTime.now().millisecondsSinceEpoch; |
| 757 _nextTestCase(); // Schedule the next test. | 756 if ((now - _lastBreath) >= BREATH_INTERVAL) { |
| 758 }); | 757 _lastBreath = now; |
| 759 break; | 758 Timer.run(_nextTestCase); |
| 760 } | 759 } else { |
| 761 _currentTestCaseIndex++; | 760 runAsync(_nextTestCase); // Schedule the next test. |
| 761 } | |
| 762 }); | |
| 762 } | 763 } |
| 763 } | 764 } |
| 764 | 765 |
| 765 /** Publish results on the page and notify controller. */ | 766 /** Publish results on the page and notify controller. */ |
| 766 void _completeTests() { | 767 void _completeTests() { |
| 767 if (!_initialized) return; | 768 if (!_initialized) return; |
| 768 int passed = 0; | 769 int passed = 0; |
| 769 int failed = 0; | 770 int failed = 0; |
| 770 int errors = 0; | 771 int errors = 0; |
| 771 | 772 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 864 | 865 |
| 865 if (!formatStacks) return trace; | 866 if (!formatStacks) return trace; |
| 866 | 867 |
| 867 // Format the stack trace by removing everything above TestCase._runTest, | 868 // Format the stack trace by removing everything above TestCase._runTest, |
| 868 // which is usually going to be irrelevant. Also fold together unittest and | 869 // which is usually going to be irrelevant. Also fold together unittest and |
| 869 // core library calls so only the function the user called is visible. | 870 // core library calls so only the function the user called is visible. |
| 870 return new Trace(trace.frames.takeWhile((frame) { | 871 return new Trace(trace.frames.takeWhile((frame) { |
| 871 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; | 872 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; |
| 872 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); | 873 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); |
| 873 } | 874 } |
| OLD | NEW |