Index: observatory_pub_packages/unittest/src/spread_args_helper.dart |
=================================================================== |
--- observatory_pub_packages/unittest/src/spread_args_helper.dart (revision 0) |
+++ observatory_pub_packages/unittest/src/spread_args_helper.dart (working copy) |
@@ -0,0 +1,166 @@ |
+part of unittest; |
+ |
+const _PLACE_HOLDER = const _ArgPlaceHolder(); |
+ |
+/// Used to track unused positional args. |
+class _ArgPlaceHolder { |
+ const _ArgPlaceHolder(); |
+} |
+ |
+/// Simulates spread arguments using named arguments. |
+class _SpreadArgsHelper { |
+ final Function callback; |
+ final int minExpectedCalls; |
+ final int maxExpectedCalls; |
+ final Function isDone; |
+ final String id; |
+ final String reason; |
+ int actualCalls = 0; |
+ final TestCase testCase; |
+ bool complete; |
+ |
+ _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, |
+ String id, String reason, {bool isDone()}) |
+ : this.callback = callback, |
+ minExpectedCalls = minExpected, |
+ maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
+ ? minExpected |
+ : maxExpected, |
+ this.isDone = isDone, |
+ this.reason = reason == null ? '' : '\n$reason', |
+ 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}.$reason'); |
+ } |
+ return false; |
+ } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { |
+ throw new TestFailure('Callback ${id}called more times than expected ' |
+ '($maxExpectedCalls).$reason'); |
+ } |
+ 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(); |
+ } |
+ } |
+ |
+ /// Returns a function that has as many required + positional arguments as |
+ /// [callback] (up to a total of 6). |
+ /// |
+ /// Optional positional arguments are supported by using const place-holders |
+ Function get func { |
+ if (callback is _Func6) return _max6; |
+ if (callback is _Func5) return _max5; |
+ if (callback is _Func4) return _max4; |
+ if (callback is _Func3) return _max3; |
+ if (callback is _Func2) return _max2; |
+ if (callback is _Func1) return _max1; |
+ if (callback is _Func0) return _max0; |
+ |
+ throw new ArgumentError( |
+ 'The callback argument has more than 6 required arguments'); |
+ } |
+ |
+ /// This indirection is critical. It ensures the returned function has an |
+ /// argument count of zero. |
+ _max0() => _max6(); |
+ |
+ _max1([a0 = _PLACE_HOLDER]) => _max6(a0); |
+ |
+ _max2([a0 = _PLACE_HOLDER, a1 = _PLACE_HOLDER]) => _max6(a0, a1); |
+ |
+ _max3([a0 = _PLACE_HOLDER, a1 = _PLACE_HOLDER, a2 = _PLACE_HOLDER]) => |
+ _max6(a0, a1, a2); |
+ |
+ _max4([a0 = _PLACE_HOLDER, a1 = _PLACE_HOLDER, a2 = _PLACE_HOLDER, |
+ a3 = _PLACE_HOLDER]) => _max6(a0, a1, a2, a3); |
+ |
+ _max5([a0 = _PLACE_HOLDER, a1 = _PLACE_HOLDER, a2 = _PLACE_HOLDER, |
+ a3 = _PLACE_HOLDER, a4 = _PLACE_HOLDER]) => _max6(a0, a1, a2, a3, a4); |
+ |
+ _max6([a0 = _PLACE_HOLDER, a1 = _PLACE_HOLDER, a2 = _PLACE_HOLDER, |
+ a3 = _PLACE_HOLDER, a4 = _PLACE_HOLDER, a5 = _PLACE_HOLDER]) { |
+ var args = [a0, a1, a2, a3, a4, a5]; |
+ args.removeWhere((a) => a == _PLACE_HOLDER); |
+ |
+ return _guardAsync( |
+ () { |
+ if (shouldCallBack()) { |
+ return Function.apply(callback, args); |
+ } |
+ }, |
+ after, testCase); |
+ } |
+ |
+ _guardAsync(Function tryBody, Function finallyBody, TestCase testCase) { |
+ assert(testCase != null); |
+ try { |
+ return tryBody(); |
+ } catch (e, trace) { |
+ _registerException(testCase, e, trace); |
+ } finally { |
+ if (finallyBody != null) finallyBody(); |
+ } |
+ } |
+} |
+ |
+typedef _Func0(); |
+typedef _Func1(a); |
+typedef _Func2(a, b); |
+typedef _Func3(a, b, c); |
+typedef _Func4(a, b, c, d); |
+typedef _Func5(a, b, c, d, e); |
+typedef _Func6(a, b, c, d, e, f); |