Index: pkg/unittest/lib/src/spread_args_helper.dart |
diff --git a/pkg/unittest/lib/src/spread_args_helper.dart b/pkg/unittest/lib/src/spread_args_helper.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08dd45868ee6b3a1da9fbabcaa76c83378ead782 |
--- /dev/null |
+++ b/pkg/unittest/lib/src/spread_args_helper.dart |
@@ -0,0 +1,123 @@ |
+part of unittest; |
+ |
+/** Simulates spread arguments using named arguments. */ |
+// TODO(sigmund): remove this class and simply use a closure with named |
+// arguments (if still applicable). |
+class _SpreadArgsHelper { |
+ final Function callback; |
+ final int minExpectedCalls; |
+ final int maxExpectedCalls; |
+ final Function isDone; |
+ final String id; |
+ int actualCalls = 0; |
+ final TestCase testCase; |
+ bool complete; |
+ static const sentinel = const _Sentinel(); |
+ |
+ _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, |
+ this.testCase = currentTestCase, |
+ 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 (isDone != null || minExpected > 0) { |
+ testCase._callbackFunctionsOutstanding++; |
+ complete = false; |
+ } else { |
+ complete = true; |
+ } |
+ } |
+ |
+ static String _makeCallbackId(String id, Function callback) { |
+ // Try to create a reasonable id. |
+ if (id != null) { |
+ 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 ''; |
+ } |
+ |
+ bool 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 ($actualCalls) after test case ' |
+ '${testCase.description} has already been marked as ' |
+ '${testCase.result}.'); |
+ } |
+ return false; |
+ } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { |
+ throw new TestFailure('Callback ${id}called more times than expected ' |
+ '($maxExpectedCalls).'); |
+ } |
+ return true; |
+ } |
+ |
+ void after() { |
+ 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; |
+ testCase._markCallbackComplete(); |
+ } |
+ } |
+ |
+ invoke0() { |
+ return _guardAsync( |
+ () { |
+ if (shouldCallBack()) { |
+ return callback(); |
+ } |
+ }, |
+ after, testCase); |
+ } |
+ |
+ invoke1(arg1) { |
+ return _guardAsync( |
+ () { |
+ if (shouldCallBack()) { |
+ return callback(arg1); |
+ } |
+ }, |
+ after, testCase); |
+ } |
+ |
+ invoke2(arg1, arg2) { |
+ return _guardAsync( |
+ () { |
+ if (shouldCallBack()) { |
+ return callback(arg1, arg2); |
+ } |
+ }, |
+ after, testCase); |
+ } |
+} |