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

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

Issue 12213079: setUp/tearDown functions can now be asynchronous. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 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) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, 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, use the pub package manager. 8 * To import this library, use the pub package manager.
9 * Create a pubspec.yaml file in your project and add 9 * Create a pubspec.yaml file in your project and add
10 * a dependency on unittest with the following lines: 10 * a dependency on unittest with the following lines:
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 // TODO(sigmund): remove this class and simply use a closure with named 322 // TODO(sigmund): remove this class and simply use a closure with named
323 // arguments (if still applicable). 323 // arguments (if still applicable).
324 class _SpreadArgsHelper { 324 class _SpreadArgsHelper {
325 Function _callback; 325 Function _callback;
326 int _expectedCalls; 326 int _expectedCalls;
327 int _actualCalls = 0; 327 int _actualCalls = 0;
328 int _testNum; 328 int _testNum;
329 TestCase _testCase; 329 TestCase _testCase;
330 Function _shouldCallBack; 330 Function _shouldCallBack;
331 Function _isDone; 331 Function _isDone;
332 String _id;
332 static const _sentinel = const _Sentinel(); 333 static const _sentinel = const _Sentinel();
333 334
334 _init(Function callback, Function shouldCallBack, Function isDone, 335 _init(Function callback, Function shouldCallBack, Function isDone,
335 [expectedCalls = 0]) { 336 [expectedCalls = 0]) {
336 ensureInitialized(); 337 ensureInitialized();
337 if (!(_currentTest >= 0 && 338 if (!(_currentTest >= 0 &&
338 _currentTest < _tests.length && 339 _currentTest < _tests.length &&
339 _tests[_currentTest] != null)) { 340 _tests[_currentTest] != null)) {
340 print("No valid test, did you forget to run your test inside a call " 341 print("No valid test, did you forget to run your test inside a call "
341 "to test()?"); 342 "to test()?");
342 } 343 }
343 assert(_currentTest >= 0 && 344 assert(_currentTest >= 0 &&
344 _currentTest < _tests.length && 345 _currentTest < _tests.length &&
345 _tests[_currentTest] != null); 346 _tests[_currentTest] != null);
346 _callback = callback; 347 _callback = callback;
347 _shouldCallBack = shouldCallBack; 348 _shouldCallBack = shouldCallBack;
348 _isDone = isDone; 349 _isDone = isDone;
349 _expectedCalls = expectedCalls; 350 _expectedCalls = expectedCalls;
350 _testNum = _currentTest; 351 _testNum = _currentTest;
351 _testCase = _tests[_currentTest]; 352 _testCase = _tests[_currentTest];
352 if (expectedCalls > 0) { 353 if (expectedCalls > 0) {
353 _testCase.callbackFunctionsOutstanding++; 354 _testCase.callbackFunctionsOutstanding++;
354 } 355 }
356 _id = '';
355 } 357 }
356 358
357 _SpreadArgsHelper(callback, shouldCallBack, isDone) { 359 _SpreadArgsHelper(callback, shouldCallBack, isDone) {
358 _init(callback, shouldCallBack, isDone); 360 _init(callback, shouldCallBack, isDone);
359 } 361 }
360 362
361 _SpreadArgsHelper.fixedCallCount(callback, expectedCalls) { 363 _SpreadArgsHelper.fixedCallCount(callback, expectedCalls, id) {
362 _init(callback, _checkCallCount, _allCallsDone, expectedCalls); 364 _init(callback, _checkCallCount, _allCallsDone, expectedCalls);
365 if (id != null) {
366 _id = "$id ";
367 }
363 } 368 }
364 369
365 _SpreadArgsHelper.variableCallCount(callback, isDone) { 370 _SpreadArgsHelper.variableCallCount(callback, isDone) {
366 _init(callback, _always, isDone, 1); 371 _init(callback, _always, isDone, 1);
367 } 372 }
368 373
369 _SpreadArgsHelper.optionalCalls(callback) { 374 _SpreadArgsHelper.optionalCalls(callback) {
370 _init(callback, _always, () => false, 0); 375 _init(callback, _always, () => false, 0);
371 } 376 }
372 377
373 _after() { 378 _after() {
374 if (_isDone()) { 379 if (_isDone()) {
375 _handleCallbackFunctionComplete(_testNum); 380 _handleCallbackFunctionComplete(_testNum, _id);
376 } 381 }
377 } 382 }
378 383
379 _allCallsDone() => _actualCalls == _expectedCalls; 384 _allCallsDone() => _actualCalls == _expectedCalls;
380 385
381 _always() { 386 _always() {
382 // Always run except if the test is done. 387 // Always run except if the test is done.
383 if (_testCase.isComplete) { 388 if (_testCase.isComplete) {
384 _testCase.error( 389 _testCase.error(
385 'Callback called after already being marked as done ($_actualCalls).', 390 'Callback ${_id}called after already being marked '
391 'as done ($_actualCalls).',
386 ''); 392 '');
387 return false; 393 return false;
388 } else { 394 } else {
389 return true; 395 return true;
390 } 396 }
391 } 397 }
392 398
393 invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel, 399 invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel,
394 arg3 = _sentinel, arg4 = _sentinel]) { 400 arg3 = _sentinel, arg4 = _sentinel]) {
395 return guardAsync(() { 401 return guardAsync(() {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 if (_shouldCallBack()) { 451 if (_shouldCallBack()) {
446 return _callback(arg1, arg2); 452 return _callback(arg1, arg2);
447 } 453 }
448 }, 454 },
449 _after, _testNum); 455 _after, _testNum);
450 } 456 }
451 457
452 /** Returns false if we exceded the number of expected calls. */ 458 /** Returns false if we exceded the number of expected calls. */
453 bool _checkCallCount() { 459 bool _checkCallCount() {
454 if (_actualCalls > _expectedCalls) { 460 if (_actualCalls > _expectedCalls) {
455 _testCase.error('Callback called more times than expected ' 461 _testCase.error('Callback ${_id}called more times than expected '
456 '($_actualCalls > $_expectedCalls).', ''); 462 '($_actualCalls > $_expectedCalls).', '');
457 return false; 463 return false;
458 } 464 }
459 return true; 465 return true;
460 } 466 }
461 } 467 }
462 468
463 /** 469 /**
464 * Indicate that [callback] is expected to be called a [count] number of times 470 * Indicate that [callback] is expected to be called a [count] number of times
465 * (by default 1). The unittest framework will wait for the callback to run the 471 * (by default 1). The unittest framework will wait for the callback to run the
466 * specified [count] times before it continues with the following test. Using 472 * specified [count] times before it continues with the following test. Using
467 * [_expectAsync] will also ensure that errors that occur within [callback] are 473 * [_expectAsync] will also ensure that errors that occur within [callback] are
468 * tracked and reported. [callback] should take between 0 and 4 positional 474 * tracked and reported. [callback] should take between 0 and 4 positional
469 * arguments (named arguments are not supported here). 475 * arguments (named arguments are not supported here). [id] can be used
476 * to provide more descriptive error messages if the callback is called more
477 * often than expected.
470 */ 478 */
471 Function _expectAsync(Function callback, {int count: 1}) { 479 Function _expectAsync(Function callback, {int count: 1, String id}) {
472 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke; 480 return new _SpreadArgsHelper.
481 fixedCallCount(callback, count, id).invoke;
473 } 482 }
474 483
475 /** 484 /**
476 * Indicate that [callback] is expected to be called a [count] number of times 485 * Indicate that [callback] is expected to be called a [count] number of times
477 * (by default 1). The unittest framework will wait for the callback to run the 486 * (by default 1). The unittest framework will wait for the callback to run the
478 * specified [count] times before it continues with the following test. Using 487 * specified [count] times before it continues with the following test. Using
479 * [expectAsync0] will also ensure that errors that occur within [callback] are 488 * [expectAsync0] will also ensure that errors that occur within [callback] are
480 * tracked and reported. [callback] should take 0 positional arguments (named 489 * tracked and reported. [callback] should take 0 positional arguments (named
481 * arguments are not supported). 490 * arguments are not supported). [id] can be used to provide more
491 * descriptive error messages if the callback is called more often than
492 * expected.
482 */ 493 */
483 // TODO(sigmund): deprecate this API when issue 2706 is fixed. 494 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
484 Function expectAsync0(Function callback, {int count: 1}) { 495 Function expectAsync0(Function callback, {int count: 1, String id}) {
485 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke0; 496 return new _SpreadArgsHelper.
497 fixedCallCount(callback, count, id).invoke0;
486 } 498 }
487 499
488 /** Like [expectAsync0] but [callback] should take 1 positional argument. */ 500 /** Like [expectAsync0] but [callback] should take 1 positional argument. */
489 // TODO(sigmund): deprecate this API when issue 2706 is fixed. 501 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
490 Function expectAsync1(Function callback, {int count: 1}) { 502 Function expectAsync1(Function callback, {int count: 1, String id}) {
491 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke1; 503 return new _SpreadArgsHelper.
504 fixedCallCount(callback, count, id).invoke1;
492 } 505 }
493 506
494 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */ 507 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */
495 // TODO(sigmund): deprecate this API when issue 2706 is fixed. 508 // TODO(sigmund): deprecate this API when issue 2706 is fixed.
496 Function expectAsync2(Function callback, {int count: 1}) { 509 Function expectAsync2(Function callback, {int count: 1, String id}) {
497 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke2; 510 return new _SpreadArgsHelper.
511 fixedCallCount(callback, count, id).invoke2;
498 } 512 }
499 513
500 /** 514 /**
501 * Indicate that [callback] is expected to be called until [isDone] returns 515 * Indicate that [callback] is expected to be called until [isDone] returns
502 * true. The unittest framework checks [isDone] after each callback and only 516 * true. The unittest framework checks [isDone] after each callback and only
503 * when it returns true will it continue with the following test. Using 517 * when it returns true will it continue with the following test. Using
504 * [expectAsyncUntil] will also ensure that errors that occur within 518 * [expectAsyncUntil] will also ensure that errors that occur within
505 * [callback] are tracked and reported. [callback] should take between 0 and 519 * [callback] are tracked and reported. [callback] should take between 0 and
506 * 4 positional arguments (named arguments are not supported). 520 * 4 positional arguments (named arguments are not supported).
507 */ 521 */
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
611 // Now that the group is over, restore the previous one. 625 // Now that the group is over, restore the previous one.
612 _currentGroup = parentGroup; 626 _currentGroup = parentGroup;
613 _testSetup = parentSetup; 627 _testSetup = parentSetup;
614 _testTeardown = parentTeardown; 628 _testTeardown = parentTeardown;
615 } 629 }
616 } 630 }
617 631
618 /** 632 /**
619 * Register a [setUp] function for a test [group]. This function will 633 * Register a [setUp] function for a test [group]. This function will
620 * be called before each test in the group is run. Note that if groups 634 * be called before each test in the group is run. Note that if groups
621 * are nested only the most locally scoped [setUp] function will be run. 635 * are nested only the most locally scoped [setUpTest] function will be run.
622 * [setUp] and [tearDown] should be called within the [group] before any 636 * [setUp] and [tearDown] should be called within the [group] before any
623 * calls to [test]. 637 * calls to [test]. The [setupTest] function can be asynchronous; in this
638 * case it must return a [Future].
624 */ 639 */
625 void setUp(Function setupTest) { 640 void setUp(Function setupTest) {
626 _testSetup = setupTest; 641 _testSetup = setupTest;
627 } 642 }
628 643
629 /** 644 /**
630 * Register a [tearDown] function for a test [group]. This function will 645 * Register a [tearDown] function for a test [group]. This function will
631 * be called after each test in the group is run. Note that if groups 646 * be called after each test in the group is run. Note that if groups
632 * are nested only the most locally scoped [tearDown] function will be run. 647 * are nested only the most locally scoped [teardownTest] function will be run.
633 * [setUp] and [tearDown] should be called within the [group] before any 648 * [setUp] and [tearDown] should be called within the [group] before any
634 * calls to [test]. 649 * calls to [test]. The [teardownTest] function can be asynchronous; in this
650 * case it must return a [Future].
635 */ 651 */
636 void tearDown(Function teardownTest) { 652 void tearDown(Function teardownTest) {
637 _testTeardown = teardownTest; 653 _testTeardown = teardownTest;
638 } 654 }
639 655
640 /** 656 /**
641 * Called when one of the callback functions is done with all expected 657 * Called when one of the callback functions is done with all expected
642 * calls. 658 * calls.
643 */ 659 */
644 void _handleCallbackFunctionComplete(testNum) { 660 void _handleCallbackFunctionComplete(testNum, [id = '']) {
645 // TODO (gram): we defer this to give the nextBatch recursive 661 // TODO (gram): we defer this to give the nextBatch recursive
646 // stack a chance to unwind. This is a temporary hack but 662 // stack a chance to unwind. This is a temporary hack but
647 // really a bunch of code here needs to be fixed. We have a 663 // really a bunch of code here needs to be fixed. We have a
648 // single array that is being iterated through by nextBatch(), 664 // single array that is being iterated through by nextBatch(),
649 // which is recursively invoked in the case of async tests that 665 // which is recursively invoked in the case of async tests that
650 // run synchronously. Bad things can then happen. 666 // run synchronously. Bad things can then happen.
651 _defer(() { 667 _defer(() {
652 if (_currentTest != testNum) { 668 if (_currentTest != testNum) {
653 if (_tests[testNum].result == PASS) { 669 if (_tests[testNum].result == PASS &&
654 _tests[testNum].error("Unexpected extra callbacks", ''); 670 // This next test is a bit of a kludge, but saves us
671 // adding some other way of identifying that this callback
672 // is an asyn teardown (which would have already advanced
justinfagnani 2013/02/08 01:36:48 asyn -> async
gram 2013/02/08 17:52:24 Done.
673 // _currentTest).
674 id != '[Async tearDown completion handler] ') {
675 _tests[testNum].error("${id}Unexpected extra callbacks", '');
justinfagnani 2013/02/08 01:36:48 add space between ${id} and Unexpected?
gram 2013/02/08 17:52:24 id already has a space if it is non-empty; this al
655 } 676 }
656 return; // Extraneous callback. 677 return; // Extraneous callback.
657 } 678 }
658 if (_currentTest < _tests.length) { 679 if (_currentTest < _tests.length) {
659 final testCase = _tests[_currentTest]; 680 final testCase = _tests[_currentTest];
660 --testCase.callbackFunctionsOutstanding; 681 --testCase.callbackFunctionsOutstanding;
661 if (testCase.callbackFunctionsOutstanding < 0) { 682 if (testCase.callbackFunctionsOutstanding < 0) {
662 // TODO(gram): Check: Can this even happen? 683 // TODO(gram): Check: Can this even happen?
663 testCase.error( 684 testCase.error(
664 'More calls to _handleCallbackFunctionComplete() than expected.', 685 'More calls to _handleCallbackFunctionComplete() than expected.',
665 ''); 686 '');
666 } else if (testCase.callbackFunctionsOutstanding == 0) { 687 } else if (testCase.callbackFunctionsOutstanding == 0 &&
667 if (!testCase.isComplete) { 688 !testCase.isComplete) {
668 testCase.pass(); 689 testCase.pass();
669 }
670 _nextTestCase();
671 } 690 }
672 } 691 }
673 }); 692 });
674 } 693 }
675 694
676 /** Advance to the next test case. */ 695 /** Advance to the next test case. */
justinfagnani 2013/02/08 01:36:48 Maybe explain that the test is deferred? When I sa
gram 2013/02/08 17:52:24 I don't believe _nextTestCase can be called before
justinfagnani 2013/02/08 18:36:57 I missed a return statement in test_case. Curious:
gram 2013/02/08 18:43:15 Without _defer, we can build up a very deep stack.
677 void _nextTestCase() { 696 void _nextTestCase() {
678 _currentTest++; 697 _defer(() {
679 _testRunner(); 698 _currentTest++;
699 _testRunner();
700 });
680 } 701 }
681 702
682 /** 703 /**
683 * Temporary hack: expose old API. 704 * Temporary hack: expose old API.
684 * TODO(gram) remove this when WebKit tests are working with new framework 705 * TODO(gram) remove this when WebKit tests are working with new framework
685 */ 706 */
686 void callbackDone() { 707 void callbackDone() {
687 _handleCallbackFunctionComplete(_currentTest); 708 _handleCallbackFunctionComplete(_currentTest);
688 } 709 }
689 710
690 /** 711 /**
691 * Utility function that can be used to notify the test framework that an 712 * Utility function that can be used to notify the test framework that an
692 * error was caught outside of this library. 713 * error was caught outside of this library.
693 */ 714 */
694 void _reportTestError(String msg, String trace) { 715 void _reportTestError(String msg, String trace) {
695 if (_currentTest < _tests.length) { 716 if (_currentTest < _tests.length) {
696 final testCase = _tests[_currentTest]; 717 final testCase = _tests[_currentTest];
697 testCase.error(msg, trace); 718 testCase.error(msg, trace);
698 if (testCase.callbackFunctionsOutstanding > 0) {
699 _nextTestCase();
700 }
701 } else { 719 } else {
702 _uncaughtErrorMessage = "$msg: $trace"; 720 _uncaughtErrorMessage = "$msg: $trace";
703 } 721 }
704 } 722 }
705 723
706 /** Runs [callback] at the end of the event loop. */ 724 /** Runs [callback] at the end of the event loop. */
707 _defer(void callback()) { 725 _defer(void callback()) {
708 // Exploit isolate ports as a platform-independent mechanism to queue a 726 // Exploit isolate ports as a platform-independent mechanism to queue a
709 // message at the end of the event loop. 727 // message at the end of the event loop.
710 // TODO(sigmund): expose this functionality somewhere in our libraries. 728 // TODO(sigmund): expose this functionality somewhere in our libraries.
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
783 * Registers that an exception was caught for the current test. 801 * Registers that an exception was caught for the current test.
784 */ 802 */
785 _registerException(testNum, e, [trace]) { 803 _registerException(testNum, e, [trace]) {
786 trace = trace == null ? '' : trace.toString(); 804 trace = trace == null ? '' : trace.toString();
787 if (_tests[testNum].result == null) { 805 if (_tests[testNum].result == null) {
788 String message = (e is ExpectException) ? e.message : 'Caught $e'; 806 String message = (e is ExpectException) ? e.message : 'Caught $e';
789 _tests[testNum].fail(message, trace); 807 _tests[testNum].fail(message, trace);
790 } else { 808 } else {
791 _tests[testNum].error('Caught $e', trace); 809 _tests[testNum].error('Caught $e', trace);
792 } 810 }
793 if (testNum == _currentTest &&
794 _tests[testNum].callbackFunctionsOutstanding > 0) {
795 _nextTestCase();
796 }
797 } 811 }
798 812
799 /** 813 /**
800 * Runs a batch of tests, yielding whenever an asynchronous test starts 814 * Runs a batch of tests, yielding whenever an asynchronous test starts
801 * running. Tests will resume executing when such asynchronous test calls 815 * running. Tests will resume executing when such asynchronous test calls
802 * [done] or if it fails with an exception. 816 * [done] or if it fails with an exception.
803 */ 817 */
804 _nextBatch() { 818 _nextBatch() {
805 while (_currentTest < _tests.length) { 819 if (_currentTest >= _tests.length) {
820 _completeTests();
821 } else {
806 final testCase = _tests[_currentTest]; 822 final testCase = _tests[_currentTest];
807 guardAsync(() { 823 guardAsync(() {
808 testCase.run(); 824 testCase.run();
809 if (!testCase.isComplete && testCase.callbackFunctionsOutstanding == 0) { 825 if (!testCase.isComplete && testCase.callbackFunctionsOutstanding == 0) {
810 testCase.pass(); 826 testCase.pass();
811 } 827 }
812 }, null, _currentTest); 828 }, null, _currentTest);
813
814 if (!testCase.isComplete &&
815 testCase.callbackFunctionsOutstanding > 0) return;
816 _currentTest++;
817 } 829 }
818
819 _completeTests();
820 } 830 }
821 831
822 /** Publish results on the page and notify controller. */ 832 /** Publish results on the page and notify controller. */
823 _completeTests() { 833 _completeTests() {
824 if (!_initialized) return; 834 if (!_initialized) return;
825 int passed = 0; 835 int passed = 0;
826 int failed = 0; 836 int failed = 0;
827 int errors = 0; 837 int errors = 0;
828 838
829 for (TestCase t in _tests) { 839 for (TestCase t in _tests) {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
901 } 911 }
902 912
903 /** Enable a test by ID. */ 913 /** Enable a test by ID. */
904 void enableTest(int testId) => _setTestEnabledState(testId, true); 914 void enableTest(int testId) => _setTestEnabledState(testId, true);
905 915
906 /** Disable a test by ID. */ 916 /** Disable a test by ID. */
907 void disableTest(int testId) => _setTestEnabledState(testId, false); 917 void disableTest(int testId) => _setTestEnabledState(testId, false);
908 918
909 /** Signature for a test function. */ 919 /** Signature for a test function. */
910 typedef void TestFunction(); 920 typedef void TestFunction();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698