Index: pkg/unittest/lib/unittest.dart |
=================================================================== |
--- pkg/unittest/lib/unittest.dart (revision 19168) |
+++ pkg/unittest/lib/unittest.dart (working copy) |
@@ -288,17 +288,18 @@ |
// arguments (if still applicable). |
class _SpreadArgsHelper { |
Function _callback; |
- int _expectedCalls; |
+ int _minExpectedCalls; |
Siggi Cherem (dart-lang)
2013/02/28 19:29:23
Now that we don't have _init, can we make some of
gram
2013/02/28 22:49:50
Done.
|
+ int _maxExpectedCalls; |
int _actualCalls = 0; |
int _testNum; |
TestCase _testCase; |
- Function _shouldCallBack; |
Function _isDone; |
String _id; |
+ bool _complete; |
Siggi Cherem (dart-lang)
2013/02/28 19:29:23
since this class is private and we don't return it
gram
2013/02/28 22:49:50
Done.
|
static const _sentinel = const _Sentinel(); |
- _init(Function callback, Function shouldCallBack, Function isDone, |
- [expectedCalls = 0]) { |
+ _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, |
Siggi Cherem (dart-lang)
2013/02/28 19:29:23
more precisely:
_SpreadArgsHelper(Function callbac
gram
2013/02/28 22:49:50
Done.
|
+ Function isDone, String id) { |
ensureInitialized(); |
if (!(_currentTest >= 0 && |
_currentTest < _tests.length && |
@@ -310,73 +311,83 @@ |
_currentTest < _tests.length && |
_tests[_currentTest] != null); |
_callback = callback; |
- _shouldCallBack = shouldCallBack; |
+ _minExpectedCalls = minExpected; |
+ if (maxExpected == 0 && minExpected > 0) { |
+ _maxExpectedCalls = minExpected; |
+ } else { |
+ _maxExpectedCalls = maxExpected; |
+ } |
_isDone = isDone; |
- _expectedCalls = expectedCalls; |
_testNum = _currentTest; |
_testCase = _tests[_currentTest]; |
- if (expectedCalls > 0) { |
+ if (isDone != null || minExpected > 0) { |
_testCase.callbackFunctionsOutstanding++; |
+ _complete = false; |
+ } else { |
+ _complete = true; |
} |
+ |
+ // Try to create a reasonable id. |
_id = ''; |
- // If the callback is not an anonymous closure, try to get the |
- // name. |
- var fname = callback.toString(); |
- var prefix = "Function '"; |
- var pos = fname.indexOf(prefix); |
- if (pos > 0) { |
- pos += prefix.length; |
- var epos = fname.indexOf("'", pos); |
- if (epos > 0) { |
- _id = "${fname.substring(pos, epos)} "; |
+ if (id != null) { |
+ _id = "$id "; |
+ } else { |
+ // If the callback is not an anonymous closure, try to get the |
+ // name. |
+ var fname = callback.toString(); |
+ var prefix = "Function '"; |
+ var pos = fname.indexOf(prefix); |
+ if (pos > 0) { |
+ pos += prefix.length; |
+ var epos = fname.indexOf("'", pos); |
+ if (epos > 0) { |
+ _id = "${fname.substring(pos, epos)} "; |
+ } |
} |
} |
} |
- _SpreadArgsHelper(callback, shouldCallBack, isDone) { |
- _init(callback, shouldCallBack, isDone); |
- } |
- |
- _SpreadArgsHelper.fixedCallCount(callback, expectedCalls, id) { |
- _init(callback, _checkCallCount, _allCallsDone, expectedCalls); |
- if (id != null) { |
- _id = "$id "; |
+ _shouldCallBack() { |
+ ++_actualCalls; |
+ if (_testCase.isComplete) { |
+ // Don't run if the test is done. We don't throw here as this is not |
+ // the current test, but we do mark the old test as having an error |
+ // if it previously passed. |
+ if (_testCase.result == PASS) { |
+ _testCase.error( |
+ 'Callback ${_id}called after test case ${_testCase.description} ' |
+ 'has already been marked as done.', |
+ ''); |
+ } |
+ return false; |
+ } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { |
+ throw new TestFailure('Callback ${_id}called more times than expected ' |
+ '($_maxExpectedCalls).'); |
} |
+ return true; |
} |
- _SpreadArgsHelper.variableCallCount(callback, isDone) { |
- _init(callback, _always, isDone, 1); |
- } |
- |
- _SpreadArgsHelper.optionalCalls(callback) { |
- _init(callback, _always, () => false, 0); |
- } |
- |
_after() { |
- if (_isDone()) { |
- _handleCallbackFunctionComplete(_testNum, _id); |
+ if (!_complete) { |
+ if (_minExpectedCalls > 0 && _actualCalls < _minExpectedCalls) { |
+ return; |
+ } |
+ if (_isDone != null && !_isDone()) { |
+ return; |
+ } |
+ // Mark this callback as complete and remove it from the testcase |
+ // oustanding callback count; if that hits zero the testcase is done. |
+ _complete = true; |
+ if (--_testCase.callbackFunctionsOutstanding == 0 && |
+ !_testCase.isComplete) { |
+ _testCase.pass(); |
+ } |
} |
} |
- _allCallsDone() => _actualCalls == _expectedCalls; |
- |
- _always() { |
- // Always run except if the test is done. |
- if (_testCase.isComplete) { |
- _testCase.error( |
- 'Callback ${_id}called after already being marked ' |
- 'as done ($_actualCalls).', |
- ''); |
- return false; |
- } else { |
- return true; |
- } |
- } |
- |
invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel, |
arg3 = _sentinel, arg4 = _sentinel]) { |
return guardAsync(() { |
- ++_actualCalls; |
if (!_shouldCallBack()) { |
return; |
} else if (arg0 == _sentinel) { |
@@ -402,7 +413,6 @@ |
invoke0() { |
return guardAsync( |
() { |
- ++_actualCalls; |
if (_shouldCallBack()) { |
return _callback(); |
} |
@@ -413,7 +423,6 @@ |
invoke1(arg1) { |
return guardAsync( |
() { |
- ++_actualCalls; |
if (_shouldCallBack()) { |
return _callback(arg1); |
} |
@@ -424,23 +433,12 @@ |
invoke2(arg1, arg2) { |
return guardAsync( |
() { |
- ++_actualCalls; |
if (_shouldCallBack()) { |
return _callback(arg1, arg2); |
} |
}, |
_after, _testNum); |
} |
- |
- /** Returns false if we exceded the number of expected calls. */ |
- bool _checkCallCount() { |
- if (_actualCalls > _expectedCalls) { |
- _testCase.error('Callback ${_id}called more times than expected ' |
- '($_actualCalls > $_expectedCalls).', ''); |
- return false; |
- } |
- return true; |
- } |
} |
/** |
@@ -453,9 +451,9 @@ |
* to provide more descriptive error messages if the callback is called more |
* often than expected. |
*/ |
-Function _expectAsync(Function callback, {int count: 1, String id}) { |
- return new _SpreadArgsHelper. |
- fixedCallCount(callback, count, id).invoke; |
+Function _expectAsync(Function callback, |
+ {int count: 1, int max: 0, String id}) { |
+ return new _SpreadArgsHelper(callback, count, max, null, id).invoke; |
} |
/** |
@@ -466,26 +464,31 @@ |
* tracked and reported. [callback] should take 0 positional arguments (named |
* arguments are not supported). [id] can be used to provide more |
* descriptive error messages if the callback is called more often than |
- * expected. |
+ * expected. [max] can be used to specify an upper bound on the number of |
+ * calls; if this is exceeded the test will fail (or be marked as in error if |
+ * it was already complete). A value of 0 for [max] (the default) will set |
+ * the upper bound to the same value as [count]; i.e. the callback should be |
+ * called exactly [count] times. A value of -1 for [max] will mean no upper |
+ * bound. |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsync0(Function callback, {int count: 1, String id}) { |
- return new _SpreadArgsHelper. |
- fixedCallCount(callback, count, id).invoke0; |
+Function expectAsync0(Function callback, |
+ {int count: 1, int max: 0, String id}) { |
+ return new _SpreadArgsHelper(callback, count, max, null, id).invoke0; |
} |
/** Like [expectAsync0] but [callback] should take 1 positional argument. */ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsync1(Function callback, {int count: 1, String id}) { |
- return new _SpreadArgsHelper. |
- fixedCallCount(callback, count, id).invoke1; |
+Function expectAsync1(Function callback, |
+ {int count: 1, int max: 0, String id}) { |
+ return new _SpreadArgsHelper(callback, count, max, null, id).invoke1; |
} |
/** Like [expectAsync0] but [callback] should take 2 positional arguments. */ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsync2(Function callback, {int count: 1, String id}) { |
- return new _SpreadArgsHelper. |
- fixedCallCount(callback, count, id).invoke2; |
+Function expectAsync2(Function callback, |
+ {int count: 1, int max: 0, String id}) { |
+ return new _SpreadArgsHelper(callback, count, max, null, id).invoke2; |
} |
/** |
@@ -494,10 +497,12 @@ |
* when it returns true will it continue with the following test. Using |
* [expectAsyncUntil] will also ensure that errors that occur within |
* [callback] are tracked and reported. [callback] should take between 0 and |
- * 4 positional arguments (named arguments are not supported). |
+ * 4 positional arguments (named arguments are not supported). [id] can be |
+ * used to identify the callback in error messages (for example if it is called |
+ * after the test case is complete). |
*/ |
-Function _expectAsyncUntil(Function callback, Function isDone) { |
- return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke; |
+Function _expectAsyncUntil(Function callback, Function isDone, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke; |
Siggi Cherem (dart-lang)
2013/02/28 19:29:23
strange - I was expecting that max was always >= 0
gram
2013/02/28 22:49:50
-1 means unlimited
|
} |
/** |
@@ -506,27 +511,29 @@ |
* when it returns true will it continue with the following test. Using |
* [expectAsyncUntil0] will also ensure that errors that occur within |
* [callback] are tracked and reported. [callback] should take 0 positional |
- * arguments (named arguments are not supported). |
+ * arguments (named arguments are not supported). [id] can be used to |
+ * identify the callback in error messages (for example if it is called |
+ * after the test case is complete). |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsyncUntil0(Function callback, Function isDone) { |
- return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke0; |
+Function expectAsyncUntil0(Function callback, Function isDone, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke0; |
} |
/** |
* Like [expectAsyncUntil0] but [callback] should take 1 positional argument. |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsyncUntil1(Function callback, Function isDone) { |
- return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke1; |
+Function expectAsyncUntil1(Function callback, Function isDone, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke1; |
} |
/** |
* Like [expectAsyncUntil0] but [callback] should take 2 positional arguments. |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function expectAsyncUntil2(Function callback, Function isDone) { |
- return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke2; |
+Function expectAsyncUntil2(Function callback, Function isDone, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke2; |
} |
/** |
@@ -535,10 +542,11 @@ |
* test. This is thus similar to expectAsync0. Use it to wrap any callbacks that |
* might optionally be called but may never be called during the test. |
* [callback] should take between 0 and 4 positional arguments (named arguments |
- * are not supported). |
+ * are not supported). [id] can be used to identify the callback in error |
+ * messages (for example if it is called after the test case is complete). |
*/ |
-Function _protectAsync(Function callback) { |
- return new _SpreadArgsHelper.optionalCalls(callback).invoke; |
+Function _protectAsync(Function callback, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke; |
} |
/** |
@@ -547,27 +555,28 @@ |
* test. This is thus similar to expectAsync0. Use it to wrap any callbacks that |
* might optionally be called but may never be called during the test. |
* [callback] should take 0 positional arguments (named arguments are not |
- * supported). |
+ * supported). [id] can be used to identify the callback in error |
+ * messages (for example if it is called after the test case is complete). |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function protectAsync0(Function callback) { |
- return new _SpreadArgsHelper.optionalCalls(callback).invoke0; |
+Function protectAsync0(Function callback, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke0; |
} |
/** |
* Like [protectAsync0] but [callback] should take 1 positional argument. |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function protectAsync1(Function callback) { |
- return new _SpreadArgsHelper.optionalCalls(callback).invoke1; |
+Function protectAsync1(Function callback, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke1; |
} |
/** |
* Like [protectAsync0] but [callback] should take 2 positional arguments. |
*/ |
// TODO(sigmund): deprecate this API when issue 2706 is fixed. |
-Function protectAsync2(Function callback) { |
- return new _SpreadArgsHelper.optionalCalls(callback).invoke2; |
+Function protectAsync2(Function callback, {String id}) { |
+ return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke2; |
} |
/** |
@@ -630,38 +639,6 @@ |
_testTeardown = teardownTest; |
} |
-/** |
- * Called when one of the callback functions is done with all expected |
- * calls. |
- */ |
-void _handleCallbackFunctionComplete(testNum, [id = '']) { |
- // TODO (gram): we defer this to give the nextBatch recursive |
- // stack a chance to unwind. This is a temporary hack but |
- // really a bunch of code here needs to be fixed. We have a |
- // single array that is being iterated through by nextBatch(), |
- // which is recursively invoked in the case of async tests that |
- // run synchronously. Bad things can then happen. |
- _defer(() { |
- if (_currentTest != testNum) { |
- if (_tests[testNum].result == PASS) { |
- _tests[testNum].error("${id}Unexpected extra callbacks", ''); |
- } |
- } else if (_currentTest < _tests.length) { |
- final testCase = _tests[_currentTest]; |
- --testCase.callbackFunctionsOutstanding; |
- if (testCase.callbackFunctionsOutstanding < 0) { |
- // TODO(gram): Check: Can this even happen? |
- testCase.error( |
- 'More calls to _handleCallbackFunctionComplete() than expected.', |
- ''); |
- } else if (testCase.callbackFunctionsOutstanding == 0 && |
- !testCase.isComplete) { |
- testCase.pass(); |
- } |
- } |
- }); |
-} |
- |
/** Advance to the next test case. */ |
void _nextTestCase() { |
_defer(() { |
@@ -671,14 +648,6 @@ |
} |
/** |
- * Temporary hack: expose old API. |
- * TODO(gram) remove this when WebKit tests are working with new framework |
- */ |
-void callbackDone() { |
- _handleCallbackFunctionComplete(_currentTest); |
-} |
- |
-/** |
* Utility function that can be used to notify the test framework that an |
* error was caught outside of this library. |
*/ |
@@ -772,11 +741,11 @@ |
*/ |
_registerException(testNum, e, [trace]) { |
trace = trace == null ? '' : trace.toString(); |
+ String message = (e is TestFailure) ? e.message : 'Caught $e'; |
if (_tests[testNum].result == null) { |
- String message = (e is TestFailure) ? e.message : 'Caught $e'; |
_tests[testNum].fail(message, trace); |
} else { |
- _tests[testNum].error('Caught $e', trace); |
+ _tests[testNum].error(message, trace); |
} |
} |