Index: pkg/unittest/lib/unittest.dart |
=================================================================== |
--- pkg/unittest/lib/unittest.dart (revision 19267) |
+++ pkg/unittest/lib/unittest.dart (working copy) |
@@ -218,7 +218,13 @@ |
/** Current test being executed. */ |
int _currentTest = 0; |
+TestCase _currentTestCase; |
+TestCase get currentTestCase => |
+ (_currentTest >= 0 && _currentTest < _tests.length) |
+ ? _tests[_currentTest] |
+ : null; |
+ |
/** Whether the framework is in an initialized state. */ |
bool _initialized = false; |
@@ -287,18 +293,27 @@ |
// TODO(sigmund): remove this class and simply use a closure with named |
// arguments (if still applicable). |
class _SpreadArgsHelper { |
- Function _callback; |
- int _expectedCalls; |
- int _actualCalls = 0; |
- int _testNum; |
- TestCase _testCase; |
- Function _shouldCallBack; |
- Function _isDone; |
- String _id; |
- static const _sentinel = const _Sentinel(); |
+ final Function callback; |
+ final int minExpectedCalls; |
+ final int maxExpectedCalls; |
+ final Function isDone; |
+ final int testNum; |
+ final String id; |
+ int actualCalls = 0; |
+ TestCase testCase; |
+ bool complete; |
+ static const sentinel = const _Sentinel(); |
- _init(Function callback, Function shouldCallBack, Function isDone, |
- [expectedCalls = 0]) { |
+ _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, |
+ Function isDone, String id) |
+ : this.callback = callback, |
+ minExpectedCalls = minExpected, |
+ maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
+ ? minExpected |
+ : maxExpected, |
+ this.isDone = isDone, |
+ testNum = _currentTest, |
+ this.id = _makeCallbackId(id, callback) { |
ensureInitialized(); |
if (!(_currentTest >= 0 && |
_currentTest < _tests.length && |
@@ -309,138 +324,124 @@ |
assert(_currentTest >= 0 && |
_currentTest < _tests.length && |
_tests[_currentTest] != null); |
- _callback = callback; |
- _shouldCallBack = shouldCallBack; |
- _isDone = isDone; |
- _expectedCalls = expectedCalls; |
- _testNum = _currentTest; |
- _testCase = _tests[_currentTest]; |
- if (expectedCalls > 0) { |
- _testCase.callbackFunctionsOutstanding++; |
+ testCase = _tests[_currentTest]; |
+ if (isDone != null || minExpected > 0) { |
+ testCase.callbackFunctionsOutstanding++; |
+ complete = false; |
+ } else { |
+ complete = true; |
} |
- _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)} "; |
- } |
- } |
} |
- _SpreadArgsHelper(callback, shouldCallBack, isDone) { |
- _init(callback, shouldCallBack, isDone); |
- } |
- |
- _SpreadArgsHelper.fixedCallCount(callback, expectedCalls, id) { |
- _init(callback, _checkCallCount, _allCallsDone, expectedCalls); |
+ static _makeCallbackId(String id, Function callback) { |
+ // Try to create a reasonable id. |
if (id != null) { |
- _id = "$id "; |
+ return "$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) { |
+ return "${fname.substring(pos, epos)} "; |
+ } |
+ } |
} |
+ return ''; |
} |
- _SpreadArgsHelper.variableCallCount(callback, isDone) { |
- _init(callback, _always, isDone, 1); |
- } |
- |
- _SpreadArgsHelper.optionalCalls(callback) { |
- _init(callback, _always, () => false, 0); |
- } |
- |
- _after() { |
- if (_isDone()) { |
- _handleCallbackFunctionComplete(_testNum, _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; |
} |
- _allCallsDone() => _actualCalls == _expectedCalls; |
+ after() { |
+ if (!complete) { |
+ if (minExpectedCalls > 0 && actualCalls < minExpectedCalls) return; |
+ if (isDone != null && !isDone()) return; |
- _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; |
+ // 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(); |
+ } |
} |
} |
- invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel, |
- arg3 = _sentinel, arg4 = _sentinel]) { |
+ invoke([arg0 = sentinel, arg1 = sentinel, arg2 = sentinel, |
+ arg3 = sentinel, arg4 = sentinel]) { |
return guardAsync(() { |
- ++_actualCalls; |
- if (!_shouldCallBack()) { |
+ if (!shouldCallBack()) { |
return; |
- } else if (arg0 == _sentinel) { |
- return _callback(); |
- } else if (arg1 == _sentinel) { |
- return _callback(arg0); |
- } else if (arg2 == _sentinel) { |
- return _callback(arg0, arg1); |
- } else if (arg3 == _sentinel) { |
- return _callback(arg0, arg1, arg2); |
- } else if (arg4 == _sentinel) { |
- return _callback(arg0, arg1, arg2, arg3); |
+ } else if (arg0 == sentinel) { |
+ return callback(); |
+ } else if (arg1 == sentinel) { |
+ return callback(arg0); |
+ } else if (arg2 == sentinel) { |
+ return callback(arg0, arg1); |
+ } else if (arg3 == sentinel) { |
+ return callback(arg0, arg1, arg2); |
+ } else if (arg4 == sentinel) { |
+ return callback(arg0, arg1, arg2, arg3); |
} else { |
- _testCase.error( |
+ testCase.error( |
'unittest lib does not support callbacks with more than' |
' 4 arguments.', |
''); |
} |
}, |
- _after, _testNum); |
+ after, testNum); |
} |
invoke0() { |
return guardAsync( |
() { |
- ++_actualCalls; |
- if (_shouldCallBack()) { |
- return _callback(); |
+ if (shouldCallBack()) { |
+ return callback(); |
} |
}, |
- _after, _testNum); |
+ after, testNum); |
} |
invoke1(arg1) { |
return guardAsync( |
() { |
- ++_actualCalls; |
- if (_shouldCallBack()) { |
- return _callback(arg1); |
+ if (shouldCallBack()) { |
+ return callback(arg1); |
} |
}, |
- _after, _testNum); |
+ after, testNum); |
} |
invoke2(arg1, arg2) { |
return guardAsync( |
() { |
- ++_actualCalls; |
- if (_shouldCallBack()) { |
- return _callback(arg1, arg2); |
+ if (shouldCallBack()) { |
+ return callback(arg1, arg2); |
} |
}, |
- _after, _testNum); |
+ 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 +454,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 +467,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 +500,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; |
} |
/** |
@@ -506,27 +514,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 +545,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 +558,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 +642,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 +651,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 +744,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); |
} |
} |