Index: lib/src/expect_async.dart |
diff --git a/lib/src/expected_function.dart b/lib/src/expect_async.dart |
similarity index 66% |
rename from lib/src/expected_function.dart |
rename to lib/src/expect_async.dart |
index 7373c7550829fdb9b010a9a71de3ef77bf9f65a4..0179f851fa079b387e4a80865ffb70d9a8983d57 100644 |
--- a/lib/src/expected_function.dart |
+++ b/lib/src/expect_async.dart |
@@ -2,11 +2,11 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-library unittest.expected_function; |
+library unittest.expect_async; |
-import '../unittest.dart'; |
- |
-import 'internal_test_case.dart'; |
+import 'expect.dart'; |
+import 'invoker.dart'; |
+import 'state.dart'; |
/// An object used to detect unpassed arguments. |
const _PLACEHOLDER = const Object(); |
@@ -30,7 +30,7 @@ typedef bool _IsDoneCallback(); |
/// |
/// The wrapper function is accessible via [func]. It supports up to six |
/// optional and/or required positional arguments, but no named arguments. |
-class ExpectedFunction { |
+class _ExpectedFunction { |
/// The wrapped callback. |
final Function _callback; |
@@ -65,8 +65,8 @@ class ExpectedFunction { |
/// The number of times the function has been called. |
int _actualCalls = 0; |
- /// The test case in which this function was wrapped. |
- final InternalTestCase _testCase; |
+ /// The test invoker in which this function was wrapped. |
+ final Invoker _invoker; |
/// Whether this function has been called the requisite number of times. |
bool _complete; |
@@ -77,7 +77,7 @@ class ExpectedFunction { |
/// If passed, [id] is used as a descriptive name fo the function and [reason] |
/// as a reason it's expected to be called. If [isDone] is passed, the test |
/// won't be allowed to complete until it returns `true`. |
- ExpectedFunction(Function callback, int minExpected, int maxExpected, |
+ _ExpectedFunction(Function callback, int minExpected, int maxExpected, |
{String id, String reason, bool isDone()}) |
: this._callback = callback, |
_minExpectedCalls = minExpected, |
@@ -86,16 +86,17 @@ class ExpectedFunction { |
: maxExpected, |
this._isDone = isDone, |
this._reason = reason == null ? '' : '\n$reason', |
- this._testCase = currentTestCase as InternalTestCase, |
+ this._invoker = Invoker.current, |
this._id = _makeCallbackId(id, callback) { |
- ensureInitialized(); |
- if (_testCase == null) { |
- throw new StateError("No valid test. Did you forget to run your test " |
- "inside a call to test()?"); |
+ if (_invoker == null) { |
+ throw new StateError("[expectAsync] was called outside of a test."); |
+ } else if (maxExpected > 0 && minExpected > maxExpected) { |
+ throw new ArgumentError("max ($maxExpected) may not be less than count " |
+ "($minExpected)."); |
} |
if (isDone != null || minExpected > 0) { |
- _testCase.callbackFunctionsOutstanding++; |
+ _invoker.addOutstandingCallback(); |
_complete = false; |
} else { |
_complete = true; |
@@ -133,6 +134,7 @@ class ExpectedFunction { |
if (_callback is _Func1) return _max1; |
if (_callback is _Func0) return _max0; |
+ _invoker.removeOutstandingCallback(); |
throw new ArgumentError( |
'The wrapped function has more than 6 required arguments'); |
} |
@@ -159,22 +161,16 @@ class ExpectedFunction { |
_run([a0, a1, a2, a3, a4, a5].where((a) => a != _PLACEHOLDER)); |
/// Runs the wrapped function with [args] and returns its return value. |
- /// |
- /// This will pass any errors on to [_testCase] and return `null`. |
_run(Iterable args) { |
+ // Note that in the old unittest, this returned `null` if it encountered an |
+ // error, where now it just re-throws that error because Zone machinery will |
+ // pass it to the invoker anyway. |
try { |
_actualCalls++; |
- if (_testCase.isComplete) { |
- // Don't run the callback 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 ($_actualCalls) after test case ' |
- '${_testCase.description} had already been marked as ' |
- '${_testCase.result}.$_reason'); |
- } |
- return null; |
+ if (_invoker.liveTest.isComplete && |
+ _invoker.liveTest.state.result == Result.success) { |
+ throw 'Callback ${_id}called ($_actualCalls) after test case ' |
+ '${_invoker.liveTest.test.name} had already completed.$_reason'; |
} else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { |
throw new TestFailure('Callback ${_id}called more times than expected ' |
'($_maxExpectedCalls).$_reason'); |
@@ -182,7 +178,7 @@ class ExpectedFunction { |
return Function.apply(_callback, args.toList()); |
} catch (error, stackTrace) { |
- _testCase.registerException(error, stackTrace); |
+ _invoker.handleError(error, stackTrace); |
return null; |
} finally { |
_afterRun(); |
@@ -198,6 +194,41 @@ class ExpectedFunction { |
// Mark this callback as complete and remove it from the test case's |
// oustanding callback count; if that hits zero the test is done. |
_complete = true; |
- _testCase.markCallbackComplete(); |
+ _invoker.removeOutstandingCallback(); |
} |
} |
+ |
+/// Indicate that [callback] is expected to be called [count] number of times |
+/// (by default 1). |
+/// |
+/// The unittest framework will wait for the callback to run the [count] times |
+/// before it considers the current test to be complete. [callback] may take up |
+/// to six optional or required positional arguments; named arguments are not |
+/// supported. |
+/// |
+/// [max] can be used to specify an upper bound on the number of calls; if this |
+/// is exceeded the test will fail. If [max] is `0` (the default), the callback |
+/// is expected to be called exactly [count] times. If [max] is `-1`, the |
+/// callback is allowed to be called any number of times greater than [count]. |
+/// |
+/// Both [id] and [reason] are optional and provide extra information about the |
+/// callback when debugging. [id] should be the name of the callback, while |
+/// [reason] should be the reason the callback is expected to be called. |
+Function expectAsync(Function callback, |
+ {int count: 1, int max: 0, String id, String reason}) => |
+ new _ExpectedFunction(callback, count, max, id: id, reason: reason).func; |
+ |
+/// Indicate that [callback] is expected to be called until [isDone] returns |
+/// true. |
+/// |
+/// [isDone] is called after each time the function is run. Only when it returns |
+/// true will the callback be considered complete. [callback] may take up to six |
+/// optional or required positional arguments; named arguments are not |
+/// supported. |
+/// |
+/// Both [id] and [reason] are optional and provide extra information about the |
+/// callback when debugging. [id] should be the name of the callback, while |
+/// [reason] should be the reason the callback is expected to be called. |
+Function expectAsyncUntil(Function callback, bool isDone(), |
+ {String id, String reason}) => new _ExpectedFunction(callback, 0, -1, |
+ id: id, reason: reason, isDone: isDone).func; |