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

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

Issue 13251003: pkg/unittest: readonly view of testCase collection (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: nit 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/config.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 * 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 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 * async.complete(); 148 * async.complete();
149 * }); 149 * });
150 * }); 150 * });
151 * } 151 * }
152 * 152 *
153 */ 153 */
154 library unittest; 154 library unittest;
155 155
156 import 'dart:async'; 156 import 'dart:async';
157 import 'dart:isolate'; 157 import 'dart:isolate';
158 import 'dart:collection';
158 import 'matcher.dart'; 159 import 'matcher.dart';
159 export 'matcher.dart'; 160 export 'matcher.dart';
160 161
161 // TODO(amouravski): We should not need to import mock here, but it's necessary 162 // TODO(amouravski): We should not need to import mock here, but it's necessary
162 // to enable dartdoc on the mock library, as it's not picked up normally. 163 // to enable dartdoc on the mock library, as it's not picked up normally.
163 import 'mock.dart'; 164 import 'mock.dart';
164 165
165 part 'src/config.dart'; 166 part 'src/config.dart';
166 part 'src/test_case.dart'; 167 part 'src/test_case.dart';
167 168
(...skipping 21 matching lines...) Expand all
189 /** 190 /**
190 * Description text of the current test group. If multiple groups are nested, 191 * Description text of the current test group. If multiple groups are nested,
191 * this will contain all of their text concatenated. 192 * this will contain all of their text concatenated.
192 */ 193 */
193 String _currentGroup = ''; 194 String _currentGroup = '';
194 195
195 /** Separator used between group names and test names. */ 196 /** Separator used between group names and test names. */
196 String groupSep = ' '; 197 String groupSep = ' ';
197 198
198 /** Tests executed in this suite. */ 199 /** Tests executed in this suite. */
199 List<TestCase> _tests; 200 final List<TestCase> _testCases = new List<TestCase>();
200 201
201 /** Get the list of tests. */ 202 /** Get the list of tests. */
202 List<TestCase> get testCases => _tests; 203 final List<TestCase> testCases = new UnmodifiableListView(_testCases);
203 204
204 /** Setup function called before each test in a group */ 205 /** Setup function called before each test in a group */
205 Function _testSetup; 206 Function _testSetup;
206 207
207 /** Teardown function called after each test in a group */ 208 /** Teardown function called after each test in a group */
208 Function _testTeardown; 209 Function _testTeardown;
209 210
210 /** Current test being executed. */ 211 int _currentTestCaseIndex = 0;
211 int _currentTest = 0;
212 TestCase _currentTestCase;
213 212
213 /** [TestCase] currently being executed. */
214 TestCase get currentTestCase => 214 TestCase get currentTestCase =>
215 (_currentTest >= 0 && _currentTest < _tests.length) 215 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < _testCases.length)
216 ? _tests[_currentTest] 216 ? _testCases[_currentTestCaseIndex]
217 : null; 217 : null;
218 218
219 /** Whether the framework is in an initialized state. */ 219 /** Whether the framework is in an initialized state. */
220 bool _initialized = false; 220 bool _initialized = false;
221 221
222 String _uncaughtErrorMessage = null; 222 String _uncaughtErrorMessage = null;
223 223
224 /** Test case result strings. */ 224 /** Test case result strings. */
225 // TODO(gram) we should change these constants to use a different string 225 // TODO(gram) we should change these constants to use a different string
226 // (so that writing 'FAIL' in the middle of a test doesn't 226 // (so that writing 'FAIL' in the middle of a test doesn't
(...skipping 15 matching lines...) Expand all
242 */ 242 */
243 Map testState = {}; 243 Map testState = {};
244 244
245 /** 245 /**
246 * Creates a new test case with the given description and body. The 246 * Creates a new test case with the given description and body. The
247 * description will include the descriptions of any surrounding group() 247 * description will include the descriptions of any surrounding group()
248 * calls. 248 * calls.
249 */ 249 */
250 void test(String spec, TestFunction body) { 250 void test(String spec, TestFunction body) {
251 ensureInitialized(); 251 ensureInitialized();
252 _tests.add(new TestCase._internal(_tests.length + 1, _fullSpec(spec), body)); 252 _testCases.add(new TestCase._internal(_testCases.length + 1, _fullSpec(spec),
253 body));
253 } 254 }
254 255
255 /** 256 /**
256 * Creates a new test case with the given description and body. The 257 * Creates a new test case with the given description and body. The
257 * description will include the descriptions of any surrounding group() 258 * description will include the descriptions of any surrounding group()
258 * calls. 259 * calls.
259 * 260 *
260 * "solo_" means that this will be the only test that is run. All other tests 261 * "solo_" means that this will be the only test that is run. All other tests
261 * will be skipped. This is a convenience function to let you quickly isolate 262 * will be skipped. This is a convenience function to let you quickly isolate
262 * a single test by adding "solo_" before it to temporarily disable all other 263 * a single test by adding "solo_" before it to temporarily disable all other
263 * tests. 264 * tests.
264 */ 265 */
265 void solo_test(String spec, TestFunction body) { 266 void solo_test(String spec, TestFunction body) {
266 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, 267 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed,
267 // all of the solo-ed tests and none of the non-solo-ed ones should run. 268 // all of the solo-ed tests and none of the non-solo-ed ones should run.
268 if (_soloTest != null) { 269 if (_soloTest != null) {
269 throw new Exception('Only one test can be soloed right now.'); 270 throw new Exception('Only one test can be soloed right now.');
270 } 271 }
271 272
272 ensureInitialized(); 273 ensureInitialized();
273 274
274 _soloTest = new TestCase._internal(_tests.length + 1, _fullSpec(spec), body); 275 _soloTest = new TestCase._internal(_testCases.length + 1, _fullSpec(spec), bod y);
275 _tests.add(_soloTest); 276 _testCases.add(_soloTest);
276 } 277 }
277 278
278 /** Sentinel value for [_SpreadArgsHelper]. */ 279 /** Sentinel value for [_SpreadArgsHelper]. */
279 class _Sentinel { 280 class _Sentinel {
280 const _Sentinel(); 281 const _Sentinel();
281 } 282 }
282 283
283 /** Simulates spread arguments using named arguments. */ 284 /** Simulates spread arguments using named arguments. */
284 // TODO(sigmund): remove this class and simply use a closure with named 285 // TODO(sigmund): remove this class and simply use a closure with named
285 // arguments (if still applicable). 286 // arguments (if still applicable).
(...skipping 10 matching lines...) Expand all
296 static const sentinel = const _Sentinel(); 297 static const sentinel = const _Sentinel();
297 298
298 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, 299 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected,
299 Function isDone, String id) 300 Function isDone, String id)
300 : this.callback = callback, 301 : this.callback = callback,
301 minExpectedCalls = minExpected, 302 minExpectedCalls = minExpected,
302 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) 303 maxExpectedCalls = (maxExpected == 0 && minExpected > 0)
303 ? minExpected 304 ? minExpected
304 : maxExpected, 305 : maxExpected,
305 this.isDone = isDone, 306 this.isDone = isDone,
306 testNum = _currentTest, 307 testNum = _currentTestCaseIndex,
307 this.id = _makeCallbackId(id, callback) { 308 this.id = _makeCallbackId(id, callback) {
308 ensureInitialized(); 309 ensureInitialized();
309 if (!(_currentTest >= 0 && 310 if (!(_currentTestCaseIndex >= 0 &&
310 _currentTest < _tests.length && 311 _currentTestCaseIndex < _testCases.length &&
311 _tests[_currentTest] != null)) { 312 _testCases[_currentTestCaseIndex] != null)) {
312 print("No valid test, did you forget to run your test inside a call " 313 print("No valid test, did you forget to run your test inside a call "
313 "to test()?"); 314 "to test()?");
314 } 315 }
315 assert(_currentTest >= 0 && 316 assert(_currentTestCaseIndex >= 0 &&
316 _currentTest < _tests.length && 317 _currentTestCaseIndex < _testCases.length &&
317 _tests[_currentTest] != null); 318 _testCases[_currentTestCaseIndex] != null);
318 testCase = _tests[_currentTest]; 319 testCase = _testCases[_currentTestCaseIndex];
319 if (isDone != null || minExpected > 0) { 320 if (isDone != null || minExpected > 0) {
320 testCase._callbackFunctionsOutstanding++; 321 testCase._callbackFunctionsOutstanding++;
321 complete = false; 322 complete = false;
322 } else { 323 } else {
323 complete = true; 324 complete = true;
324 } 325 }
325 } 326 }
326 327
327 static _makeCallbackId(String id, Function callback) { 328 static _makeCallbackId(String id, Function callback) {
328 // Try to create a reasonable id. 329 // Try to create a reasonable id.
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after
627 * calls to [test]. The [teardownTest] function can be asynchronous; in this 628 * calls to [test]. The [teardownTest] function can be asynchronous; in this
628 * case it must return a [Future]. 629 * case it must return a [Future].
629 */ 630 */
630 void tearDown(Function teardownTest) { 631 void tearDown(Function teardownTest) {
631 _testTeardown = teardownTest; 632 _testTeardown = teardownTest;
632 } 633 }
633 634
634 /** Advance to the next test case. */ 635 /** Advance to the next test case. */
635 void _nextTestCase() { 636 void _nextTestCase() {
636 _defer(() { 637 _defer(() {
637 _currentTest++; 638 _currentTestCaseIndex++;
638 _nextBatch(); 639 _nextBatch();
639 }); 640 });
640 } 641 }
641 642
642 /** 643 /**
643 * Utility function that can be used to notify the test framework that an 644 * Utility function that can be used to notify the test framework that an
644 * error was caught outside of this library. 645 * error was caught outside of this library.
645 */ 646 */
646 void _reportTestError(String msg, String trace) { 647 void _reportTestError(String msg, String trace) {
647 if (_currentTest < _tests.length) { 648 if (_currentTestCaseIndex < _testCases.length) {
648 final testCase = _tests[_currentTest]; 649 final testCase = _testCases[_currentTestCaseIndex];
649 testCase.error(msg, trace); 650 testCase.error(msg, trace);
650 } else { 651 } else {
651 _uncaughtErrorMessage = "$msg: $trace"; 652 _uncaughtErrorMessage = "$msg: $trace";
652 } 653 }
653 } 654 }
654 655
655 /** 656 /**
656 * Runs [callback] at the end of the event loop. Note that we don't wrap 657 * Runs [callback] at the end of the event loop. Note that we don't wrap
657 * the callback in guardAsync; this is for test framework functions which 658 * the callback in guardAsync; this is for test framework functions which
658 * should not be throwing unexpected exceptions that end up failing test 659 * should not be throwing unexpected exceptions that end up failing test
(...skipping 20 matching lines...) Expand all
679 void filterTests(testFilter) { 680 void filterTests(testFilter) {
680 var filterFunction; 681 var filterFunction;
681 if (testFilter is String) { 682 if (testFilter is String) {
682 RegExp re = new RegExp(testFilter); 683 RegExp re = new RegExp(testFilter);
683 filterFunction = (t) => re.hasMatch(t.description); 684 filterFunction = (t) => re.hasMatch(t.description);
684 } else if (testFilter is RegExp) { 685 } else if (testFilter is RegExp) {
685 filterFunction = (t) => testFilter.hasMatch(t.description); 686 filterFunction = (t) => testFilter.hasMatch(t.description);
686 } else if (testFilter is Function) { 687 } else if (testFilter is Function) {
687 filterFunction = testFilter; 688 filterFunction = testFilter;
688 } 689 }
689 _tests.retainWhere(filterFunction); 690 _testCases.retainWhere(filterFunction);
690 } 691 }
691 692
692 /** Runs all queued tests, one at a time. */ 693 /** Runs all queued tests, one at a time. */
693 void runTests() { 694 void runTests() {
694 _currentTest = 0; 695 _currentTestCaseIndex = 0;
695 _currentGroup = ''; 696 _currentGroup = '';
696 697
697 // If we are soloing a test, remove all the others. 698 // If we are soloing a test, remove all the others.
698 if (_soloTest != null) { 699 if (_soloTest != null) {
699 filterTests((t) => t == _soloTest); 700 filterTests((t) => t == _soloTest);
700 } 701 }
701 702
702 _config.onStart(); 703 _config.onStart();
703 704
704 _defer(() { 705 _defer(() {
705 _nextBatch(); 706 _nextBatch();
706 }); 707 });
707 } 708 }
708 709
709 /** 710 /**
710 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is 711 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is
711 * passed to the corresponding test. 712 * passed to the corresponding test.
712 * 713 *
713 * The value returned by [tryBody] (if any) is returned by [guardAsync]. 714 * The value returned by [tryBody] (if any) is returned by [guardAsync].
714 */ 715 */
715 guardAsync(Function tryBody) { 716 guardAsync(Function tryBody) {
716 return _guardAsync(tryBody, null, _currentTest); 717 return _guardAsync(tryBody, null, _currentTestCaseIndex);
717 } 718 }
718 719
719 _guardAsync(Function tryBody, Function finallyBody, int testNum) { 720 _guardAsync(Function tryBody, Function finallyBody, int testNum) {
720 assert(testNum >= 0); 721 assert(testNum >= 0);
721 try { 722 try {
722 return tryBody(); 723 return tryBody();
723 } catch (e, trace) { 724 } catch (e, trace) {
724 _registerException(testNum, e, trace); 725 _registerException(testNum, e, trace);
725 } finally { 726 } finally {
726 if (finallyBody != null) finallyBody(); 727 if (finallyBody != null) finallyBody();
727 } 728 }
728 } 729 }
729 730
730 /** 731 /**
731 * Registers that an exception was caught for the current test. 732 * Registers that an exception was caught for the current test.
732 */ 733 */
733 void registerException(e, [trace]) { 734 void registerException(e, [trace]) {
734 _registerException(_currentTest, e, trace); 735 _registerException(_currentTestCaseIndex, e, trace);
735 } 736 }
736 737
737 /** 738 /**
738 * Registers that an exception was caught for the current test. 739 * Registers that an exception was caught for the current test.
739 */ 740 */
740 void _registerException(testNum, e, [trace]) { 741 void _registerException(testNum, e, [trace]) {
741 trace = trace == null ? '' : trace.toString(); 742 trace = trace == null ? '' : trace.toString();
742 String message = (e is TestFailure) ? e.message : 'Caught $e'; 743 String message = (e is TestFailure) ? e.message : 'Caught $e';
743 if (_tests[testNum].result == null) { 744 if (_testCases[testNum].result == null) {
744 _tests[testNum].fail(message, trace); 745 _testCases[testNum].fail(message, trace);
745 } else { 746 } else {
746 _tests[testNum].error(message, trace); 747 _testCases[testNum].error(message, trace);
747 } 748 }
748 } 749 }
749 750
750 /** 751 /**
751 * Runs a batch of tests, yielding whenever an asynchronous test starts 752 * Runs a batch of tests, yielding whenever an asynchronous test starts
752 * running. Tests will resume executing when such asynchronous test calls 753 * running. Tests will resume executing when such asynchronous test calls
753 * [done] or if it fails with an exception. 754 * [done] or if it fails with an exception.
754 */ 755 */
755 void _nextBatch() { 756 void _nextBatch() {
756 while (true) { 757 while (true) {
757 if (_currentTest >= _tests.length) { 758 if (_currentTestCaseIndex >= _testCases.length) {
758 _completeTests(); 759 _completeTests();
759 break; 760 break;
760 } 761 }
761 final testCase = _tests[_currentTest]; 762 final testCase = _testCases[_currentTestCaseIndex];
762 var f = _guardAsync(testCase._run, null, _currentTest); 763 var f = _guardAsync(testCase._run, null, _currentTestCaseIndex);
763 if (f != null) { 764 if (f != null) {
764 f.whenComplete(() { 765 f.whenComplete(() {
765 _nextTestCase(); // Schedule the next test. 766 _nextTestCase(); // Schedule the next test.
766 }); 767 });
767 break; 768 break;
768 } 769 }
769 _currentTest++; 770 _currentTestCaseIndex++;
770 } 771 }
771 } 772 }
772 773
773 /** Publish results on the page and notify controller. */ 774 /** Publish results on the page and notify controller. */
774 void _completeTests() { 775 void _completeTests() {
775 if (!_initialized) return; 776 if (!_initialized) return;
776 int passed = 0; 777 int passed = 0;
777 int failed = 0; 778 int failed = 0;
778 int errors = 0; 779 int errors = 0;
779 780
780 for (TestCase t in _tests) { 781 for (TestCase t in _testCases) {
781 switch (t.result) { 782 switch (t.result) {
782 case PASS: passed++; break; 783 case PASS: passed++; break;
783 case FAIL: failed++; break; 784 case FAIL: failed++; break;
784 case ERROR: errors++; break; 785 case ERROR: errors++; break;
785 } 786 }
786 } 787 }
787 _config.onSummary(passed, failed, errors, _tests, _uncaughtErrorMessage); 788 _config.onSummary(passed, failed, errors, _testCases, _uncaughtErrorMessage);
788 _config.onDone(passed > 0 && failed == 0 && errors == 0 && 789 _config.onDone(passed > 0 && failed == 0 && errors == 0 &&
789 _uncaughtErrorMessage == null); 790 _uncaughtErrorMessage == null);
790 _initialized = false; 791 _initialized = false;
791 } 792 }
792 793
793 String _fullSpec(String spec) { 794 String _fullSpec(String spec) {
794 if (spec == null) return '$_currentGroup'; 795 if (spec == null) return '$_currentGroup';
795 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; 796 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec;
796 } 797 }
797 798
798 /** 799 /**
799 * Lazily initializes the test library if not already initialized. 800 * Lazily initializes the test library if not already initialized.
800 */ 801 */
801 void ensureInitialized() { 802 void ensureInitialized() {
802 if (_initialized) { 803 if (_initialized) {
803 return; 804 return;
804 } 805 }
805 _initialized = true; 806 _initialized = true;
806 // Hook our async guard into the matcher library. 807 // Hook our async guard into the matcher library.
807 wrapAsync = (f, [id]) => expectAsync1(f, id: id); 808 wrapAsync = (f, [id]) => expectAsync1(f, id: id);
808 809
809 _tests = <TestCase>[];
810 _uncaughtErrorMessage = null; 810 _uncaughtErrorMessage = null;
811 811
812 if (_config == null) { 812 if (_config == null) {
813 unittestConfiguration = new Configuration(); 813 unittestConfiguration = new Configuration();
814 } 814 }
815 _config.onInit(); 815 _config.onInit();
816 816
817 if (_config.autoStart) { 817 if (_config.autoStart) {
818 // Immediately queue the suite up. It will run after a timeout (i.e. after 818 // Immediately queue the suite up. It will run after a timeout (i.e. after
819 // main() has returned). 819 // main() has returned).
820 _defer(runTests); 820 _defer(runTests);
821 } 821 }
822 } 822 }
823 823
824 /** Select a solo test by ID. */ 824 /** Select a solo test by ID. */
825 void setSoloTest(int id) { 825 void setSoloTest(int id) {
826 for (var i = 0; i < _tests.length; i++) { 826 for (var i = 0; i < _testCases.length; i++) {
827 if (_tests[i].id == id) { 827 if (_testCases[i].id == id) {
828 _soloTest = _tests[i]; 828 _soloTest = _testCases[i];
829 break; 829 break;
830 } 830 }
831 } 831 }
832 } 832 }
833 833
834 /** Enable/disable a test by ID. */ 834 /** Enable/disable a test by ID. */
835 void _setTestEnabledState(int testId, bool state) { 835 void _setTestEnabledState(int testId, bool state) {
836 // Try fast path first. 836 // Try fast path first.
837 if (_tests.length > testId && _tests[testId].id == testId) { 837 if (_testCases.length > testId && _testCases[testId].id == testId) {
838 _tests[testId].enabled = state; 838 _testCases[testId].enabled = state;
839 } else { 839 } else {
840 for (var i = 0; i < _tests.length; i++) { 840 for (var i = 0; i < _testCases.length; i++) {
841 if (_tests[i].id == testId) { 841 if (_testCases[i].id == testId) {
842 _tests[i].enabled = state; 842 _testCases[i].enabled = state;
843 break; 843 break;
844 } 844 }
845 } 845 }
846 } 846 }
847 } 847 }
848 848
849 /** Enable a test by ID. */ 849 /** Enable a test by ID. */
850 void enableTest(int testId) => _setTestEnabledState(testId, true); 850 void enableTest(int testId) => _setTestEnabledState(testId, true);
851 851
852 /** Disable a test by ID. */ 852 /** Disable a test by ID. */
853 void disableTest(int testId) => _setTestEnabledState(testId, false); 853 void disableTest(int testId) => _setTestEnabledState(testId, false);
854 854
855 /** Signature for a test function. */ 855 /** Signature for a test function. */
856 typedef dynamic TestFunction(); 856 typedef dynamic TestFunction();
OLDNEW
« no previous file with comments | « pkg/unittest/lib/src/config.dart ('k') | pkg/unittest/test/unittest_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698