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

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

Issue 149573006: pkg/unittest: more lock-down, cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: fighting cl Created 6 years, 10 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
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 * Support for writing Dart unit tests. 6 * Support for writing Dart unit tests.
7 * 7 *
8 * For information on installing and importing this library, see the 8 * For information on installing and importing this library, see the
9 * [unittest package on pub.dartlang.org] 9 * [unittest package on pub.dartlang.org]
10 * (http://pub.dartlang.org/packages/unittest). 10 * (http://pub.dartlang.org/packages/unittest).
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 import 'dart:collection'; 139 import 'dart:collection';
140 import 'dart:isolate'; 140 import 'dart:isolate';
141 import 'package:stack_trace/stack_trace.dart'; 141 import 'package:stack_trace/stack_trace.dart';
142 142
143 import 'matcher.dart'; 143 import 'matcher.dart';
144 export 'matcher.dart'; 144 export 'matcher.dart';
145 145
146 import 'src/utils.dart'; 146 import 'src/utils.dart';
147 147
148 part 'src/configuration.dart'; 148 part 'src/configuration.dart';
149 part 'src/group_context.dart';
149 part 'src/simple_configuration.dart'; 150 part 'src/simple_configuration.dart';
151 part 'src/spread_args_helper.dart';
150 part 'src/test_case.dart'; 152 part 'src/test_case.dart';
151 153
152 Configuration _config; 154 Configuration _config;
153 155
154 /** 156 /**
155 * [Configuration] used by the unittest library. Note that if a 157 * [Configuration] used by the unittest library. Note that if a
156 * configuration has not been set, calling this getter will create 158 * configuration has not been set, calling this getter will create
157 * a default configuration. 159 * a default configuration.
158 */ 160 */
159 Configuration get unittestConfiguration { 161 Configuration get unittestConfiguration {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 202
201 /** 203 /**
202 * The set of tests to run can be restricted by using [solo_test] and 204 * The set of tests to run can be restricted by using [solo_test] and
203 * [solo_group]. 205 * [solo_group].
204 * As groups can be nested we use a counter to keep track of the nest level 206 * As groups can be nested we use a counter to keep track of the nest level
205 * of soloing, and a flag to tell if we have seen any solo tests. 207 * of soloing, and a flag to tell if we have seen any solo tests.
206 */ 208 */
207 int _soloNestingLevel = 0; 209 int _soloNestingLevel = 0;
208 bool _soloTestSeen = false; 210 bool _soloTestSeen = false;
209 211
210 /**
211 * Setup and teardown functions for a group and its parents, the latter
212 * for chaining.
213 */
214 class _GroupContext {
215 final _GroupContext parent;
216
217 /** Description text of the current test group. */
218 final String _name;
219
220 /** Setup function called before each test in a group. */
221 Function _testSetup;
222
223 get testSetup => _testSetup;
224
225 get parentSetup => (parent == null) ? null : parent.testSetup;
226
227 set testSetup(Function setup) {
228 var preSetup = parentSetup;
229 if (preSetup == null) {
230 _testSetup = setup;
231 } else {
232 _testSetup = () {
233 var f = preSetup();
234 if (f is Future) {
235 return f.then((_) => setup());
236 } else {
237 return setup();
238 }
239 };
240 }
241 }
242
243 /** Teardown function called after each test in a group. */
244 Function _testTeardown;
245
246 get testTeardown => _testTeardown;
247
248 get parentTeardown => (parent == null) ? null : parent.testTeardown;
249
250 set testTeardown(Function teardown) {
251 var postTeardown = parentTeardown;
252 if (postTeardown == null) {
253 _testTeardown = teardown;
254 } else {
255 _testTeardown = () {
256 var f = teardown();
257 if (f is Future) {
258 return f.then((_) => postTeardown());
259 } else {
260 return postTeardown();
261 }
262 };
263 }
264 }
265
266 String get fullName => (parent == null || parent == _rootContext)
267 ? _name
268 : "${parent.fullName}$groupSep$_name";
269
270 _GroupContext([this.parent, this._name = '']) {
271 _testSetup = parentSetup;
272 _testTeardown = parentTeardown;
273 }
274 }
275
276 // We use a 'dummy' context for the top level to eliminate null 212 // We use a 'dummy' context for the top level to eliminate null
277 // checks when querying the context. This allows us to easily 213 // checks when querying the context. This allows us to easily
278 // support top-level setUp/tearDown functions as well. 214 // support top-level setUp/tearDown functions as well.
279 final _rootContext = new _GroupContext(); 215 final _rootContext = new _GroupContext();
280 _GroupContext _currentContext = _rootContext; 216 _GroupContext _currentContext = _rootContext;
281 217
282 /** 218 /**
283 * Represents the index of the currently running test case 219 * Represents the index of the currently running test case
284 * == -1 implies the test system is not running 220 * == -1 implies the test system is not running
285 * == [number of test cases] is a short-lived state flagging that the last test 221 * == [number of test cases] is a short-lived state flagging that the last test
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 } finally { 297 } finally {
362 --_soloNestingLevel; 298 --_soloNestingLevel;
363 } 299 }
364 } 300 }
365 301
366 /** Sentinel value for [_SpreadArgsHelper]. */ 302 /** Sentinel value for [_SpreadArgsHelper]. */
367 class _Sentinel { 303 class _Sentinel {
368 const _Sentinel(); 304 const _Sentinel();
369 } 305 }
370 306
371 /** Simulates spread arguments using named arguments. */
372 // TODO(sigmund): remove this class and simply use a closure with named
373 // arguments (if still applicable).
374 class _SpreadArgsHelper {
375 final Function callback;
376 final int minExpectedCalls;
377 final int maxExpectedCalls;
378 final Function isDone;
379 final String id;
380 int actualCalls = 0;
381 final TestCase testCase;
382 bool complete;
383 static const sentinel = const _Sentinel();
384
385 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected,
386 Function isDone, String id)
387 : this.callback = callback,
388 minExpectedCalls = minExpected,
389 maxExpectedCalls = (maxExpected == 0 && minExpected > 0)
390 ? minExpected
391 : maxExpected,
392 this.isDone = isDone,
393 this.testCase = currentTestCase,
394 this.id = _makeCallbackId(id, callback) {
395 ensureInitialized();
396 if (testCase == null) {
397 throw new StateError("No valid test. Did you forget to run your test "
398 "inside a call to test()?");
399 }
400
401 if (isDone != null || minExpected > 0) {
402 testCase._callbackFunctionsOutstanding++;
403 complete = false;
404 } else {
405 complete = true;
406 }
407 }
408
409 static String _makeCallbackId(String id, Function callback) {
410 // Try to create a reasonable id.
411 if (id != null) {
412 return "$id ";
413 } else {
414 // If the callback is not an anonymous closure, try to get the
415 // name.
416 var fname = callback.toString();
417 var prefix = "Function '";
418 var pos = fname.indexOf(prefix);
419 if (pos > 0) {
420 pos += prefix.length;
421 var epos = fname.indexOf("'", pos);
422 if (epos > 0) {
423 return "${fname.substring(pos, epos)} ";
424 }
425 }
426 }
427 return '';
428 }
429
430 bool shouldCallBack() {
431 ++actualCalls;
432 if (testCase.isComplete) {
433 // Don't run if the test is done. We don't throw here as this is not
434 // the current test, but we do mark the old test as having an error
435 // if it previously passed.
436 if (testCase.result == PASS) {
437 testCase.error(
438 'Callback ${id}called ($actualCalls) after test case '
439 '${testCase.description} has already been marked as '
440 '${testCase.result}.');
441 }
442 return false;
443 } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) {
444 throw new TestFailure('Callback ${id}called more times than expected '
445 '($maxExpectedCalls).');
446 }
447 return true;
448 }
449
450 void after() {
451 if (!complete) {
452 if (minExpectedCalls > 0 && actualCalls < minExpectedCalls) return;
453 if (isDone != null && !isDone()) return;
454
455 // Mark this callback as complete and remove it from the testcase
456 // oustanding callback count; if that hits zero the testcase is done.
457 complete = true;
458 testCase._markCallbackComplete();
459 }
460 }
461
462 invoke0() {
463 return _guardAsync(
464 () {
465 if (shouldCallBack()) {
466 return callback();
467 }
468 },
469 after, testCase);
470 }
471
472 invoke1(arg1) {
473 return _guardAsync(
474 () {
475 if (shouldCallBack()) {
476 return callback(arg1);
477 }
478 },
479 after, testCase);
480 }
481
482 invoke2(arg1, arg2) {
483 return _guardAsync(
484 () {
485 if (shouldCallBack()) {
486 return callback(arg1, arg2);
487 }
488 },
489 after, testCase);
490 }
491 }
492
493 /** 307 /**
494 * Indicate that [callback] is expected to be called a [count] number of times 308 * Indicate that [callback] is expected to be called a [count] number of times
495 * (by default 1). The unittest framework will wait for the callback to run the 309 * (by default 1). The unittest framework will wait for the callback to run the
496 * specified [count] times before it continues with the following test. Using 310 * specified [count] times before it continues with the following test. Using
497 * [expectAsync0] will also ensure that errors that occur within [callback] are 311 * [expectAsync0] will also ensure that errors that occur within [callback] are
498 * tracked and reported. [callback] should take 0 positional arguments (named 312 * tracked and reported. [callback] should take 0 positional arguments (named
499 * arguments are not supported). [id] can be used to provide more 313 * arguments are not supported). [id] can be used to provide more
500 * descriptive error messages if the callback is called more often than 314 * descriptive error messages if the callback is called more often than
501 * expected. [max] can be used to specify an upper bound on the number of 315 * expected. [max] can be used to specify an upper bound on the number of
502 * calls; if this is exceeded the test will fail (or be marked as in error if 316 * calls; if this is exceeded the test will fail (or be marked as in error if
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 _runTest(); 470 _runTest();
657 } 471 }
658 472
659 /** Handle errors that happen outside the tests. */ 473 /** Handle errors that happen outside the tests. */
660 // TODO(vsm): figure out how to expose the stack trace here 474 // TODO(vsm): figure out how to expose the stack trace here
661 // Currently e.message works in dartium, but not in dartc. 475 // Currently e.message works in dartium, but not in dartc.
662 void handleExternalError(e, String message, [stack]) { 476 void handleExternalError(e, String message, [stack]) {
663 var msg = '$message\nCaught $e'; 477 var msg = '$message\nCaught $e';
664 478
665 if (currentTestCase != null) { 479 if (currentTestCase != null) {
666 currentTestCase.error(msg, stack); 480 currentTestCase._error(msg, stack);
667 } else { 481 } else {
668 _uncaughtErrorMessage = "$msg: $stack"; 482 _uncaughtErrorMessage = "$msg: $stack";
669 } 483 }
670 } 484 }
671 485
672 void rerunTests() { 486 void rerunTests() {
673 _uncaughtErrorMessage = null; 487 _uncaughtErrorMessage = null;
674 _initialized = true; // We don't want to reset the test array. 488 _initialized = true; // We don't want to reset the test array.
675 runTests(); 489 runTests();
676 } 490 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 void registerException(e, [trace]) { 543 void registerException(e, [trace]) {
730 _registerException(currentTestCase, e, trace); 544 _registerException(currentTestCase, e, trace);
731 } 545 }
732 546
733 /** 547 /**
734 * Registers that an exception was caught for the current test. 548 * Registers that an exception was caught for the current test.
735 */ 549 */
736 void _registerException(TestCase testCase, e, [trace]) { 550 void _registerException(TestCase testCase, e, [trace]) {
737 String message = (e is TestFailure) ? e.message : 'Caught $e'; 551 String message = (e is TestFailure) ? e.message : 'Caught $e';
738 if (testCase.result == null) { 552 if (testCase.result == null) {
739 testCase.fail(message, trace); 553 testCase._fail(message, trace);
740 } else { 554 } else {
741 testCase.error(message, trace); 555 testCase._error(message, trace);
742 } 556 }
743 } 557 }
744 558
745 /** 559 /**
746 * Runs the next test. 560 * Runs the next test.
747 */ 561 */
748 void _runTest() { 562 void _runTest() {
749 if (_currentTestCaseIndex >= testCases.length) { 563 if (_currentTestCaseIndex >= testCases.length) {
750 assert(_currentTestCaseIndex == testCases.length); 564 assert(_currentTestCaseIndex == testCases.length);
751 _completeTests(); 565 _completeTests();
752 } else { 566 } else {
753 var testCase = testCases[_currentTestCaseIndex]; 567 var testCase = testCases[_currentTestCaseIndex];
754 Future f = _guardAsync(testCase._run, null, testCase); 568 Future f = _guardAsync(testCase._run, null, testCase);
755 var timeout = unittestConfiguration.timeout; 569 var timeout = unittestConfiguration.timeout;
756 570
757 Timer timer; 571 Timer timer;
758 if (timeout != null) { 572 if (timeout != null) {
759 try { 573 try {
760 timer = new Timer(timeout, () { 574 timer = new Timer(timeout, () {
761 testCase.error("Test timed out after ${timeout.inSeconds} seconds."); 575 testCase._error("Test timed out after ${timeout.inSeconds} seconds.");
762 _nextTestCase(); 576 _nextTestCase();
763 }); 577 });
764 } on UnsupportedError catch (e) { 578 } on UnsupportedError catch (e) {
765 if (e.message != "Timer greater than 0.") rethrow; 579 if (e.message != "Timer greater than 0.") rethrow;
766 // Support running on d8 and jsshell which don't support timers. 580 // Support running on d8 and jsshell which don't support timers.
767 } 581 }
768 } 582 }
769 f.whenComplete(() { 583 f.whenComplete(() {
770 if (timer != null) timer.cancel(); 584 if (timer != null) timer.cancel();
771 var now = new DateTime.now().millisecondsSinceEpoch; 585 var now = new DateTime.now().millisecondsSinceEpoch;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
833 } 647 }
834 648
835 /** Select a solo test by ID. */ 649 /** Select a solo test by ID. */
836 void setSoloTest(int id) => 650 void setSoloTest(int id) =>
837 _testCases.retainWhere((t) => t.id == id); 651 _testCases.retainWhere((t) => t.id == id);
838 652
839 /** Enable/disable a test by ID. */ 653 /** Enable/disable a test by ID. */
840 void _setTestEnabledState(int testId, bool state) { 654 void _setTestEnabledState(int testId, bool state) {
841 // Try fast path first. 655 // Try fast path first.
842 if (testCases.length > testId && testCases[testId].id == testId) { 656 if (testCases.length > testId && testCases[testId].id == testId) {
843 testCases[testId].enabled = state; 657 testCases[testId]._enabled = state;
844 } else { 658 } else {
845 for (var i = 0; i < testCases.length; i++) { 659 for (var i = 0; i < testCases.length; i++) {
846 if (testCases[i].id == testId) { 660 if (testCases[i].id == testId) {
847 testCases[i].enabled = state; 661 testCases[i]._enabled = state;
848 break; 662 break;
849 } 663 }
850 } 664 }
851 } 665 }
852 } 666 }
853 667
854 /** Enable a test by ID. */ 668 /** Enable a test by ID. */
855 void enableTest(int testId) => _setTestEnabledState(testId, true); 669 void enableTest(int testId) => _setTestEnabledState(testId, true);
856 670
857 /** Disable a test by ID. */ 671 /** Disable a test by ID. */
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
897 711
898 if (!filterStacks) return trace; 712 if (!filterStacks) return trace;
899 713
900 // Format the stack trace by removing everything above TestCase._runTest, 714 // Format the stack trace by removing everything above TestCase._runTest,
901 // which is usually going to be irrelevant. Also fold together unittest and 715 // which is usually going to be irrelevant. Also fold together unittest and
902 // core library calls so only the function the user called is visible. 716 // core library calls so only the function the user called is visible.
903 return new Trace(trace.frames.takeWhile((frame) { 717 return new Trace(trace.frames.takeWhile((frame) {
904 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; 718 return frame.package != 'unittest' || frame.member != 'TestCase._runTest';
905 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); 719 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore);
906 } 720 }
OLDNEW
« no previous file with comments | « pkg/unittest/lib/src/test_case.dart ('k') | pkg/unittest/test/unittest_async_exception2_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698