@@ -10,6 +10,8 @@ _WhenCall _whenCall = null;
1010final List <_VerifyCall > _verifyCalls = < _VerifyCall > [];
1111final _TimeStampProvider _timer = new _TimeStampProvider ();
1212final List _capturedArgs = [];
13+ final List <_ArgMatcher > _typedArgs = < _ArgMatcher > [];
14+ final Map <String , _ArgMatcher > _typedNamedArgs = < String , _ArgMatcher > {};
1315
1416// Hidden from the public API, used by spy.dart.
1517void setDefaultResponse (Mock mock, dynamic defaultResponse) {
@@ -31,6 +33,9 @@ class Mock {
3133 }
3234
3335 dynamic noSuchMethod (Invocation invocation) {
36+ if (_typedArgs.isNotEmpty || _typedNamedArgs.isNotEmpty) {
37+ invocation = _reconstituteInvocation (invocation);
38+ }
3439 if (_whenInProgress) {
3540 _whenCall = new _WhenCall (this , invocation);
3641 return null ;
@@ -55,6 +60,132 @@ class Mock {
5560 String toString () => _givenName != null ? _givenName : runtimeType.toString ();
5661}
5762
63+ // Return a new [Invocation], reconstituted from [invocation], [_typedArgs],
64+ // and [_typedNamedArgs].
65+ Invocation _reconstituteInvocation (Invocation invocation) {
66+ var newInvocation = new FakeInvocation (invocation);
67+ return newInvocation;
68+ }
69+
70+ /// An Invocation implementation that allows all attributes to be passed into
71+ /// the constructor.
72+ class FakeInvocation extends Invocation {
73+ final Symbol memberName;
74+ final Map <Symbol , dynamic > namedArguments;
75+ final List <dynamic > positionalArguments;
76+ final bool isGetter;
77+ final bool isMethod;
78+ final bool isSetter;
79+
80+ factory FakeInvocation (Invocation invocation) {
81+ if (_typedArgs.isEmpty && _typedNamedArgs.isEmpty) {
82+ throw new StateError ("FakeInvocation called when no typed calls have been saved." );
83+ }
84+
85+ // Handle named arguments first, so that we can provide useful errors for
86+ // the various bad states. If all is well with the named arguments, then we
87+ // can process the positional arguments, and resort to more general errors
88+ // if the state is still bad.
89+ var namedArguments = _reconstituteNamedArgs (invocation);
90+ var positionalArguments = _reconstitutePositionalArgs (invocation);
91+
92+ _typedArgs.clear ();
93+ _typedNamedArgs.clear ();
94+
95+ return new FakeInvocation ._(
96+ invocation.memberName,
97+ positionalArguments,
98+ namedArguments,
99+ invocation.isGetter,
100+ invocation.isMethod,
101+ invocation.isSetter);
102+ }
103+
104+ static Map <Symbol ,dynamic > _reconstituteNamedArgs (Invocation invocation) {
105+ var namedArguments = < Symbol , dynamic > {};
106+ var _typedNamedArgSymbols = _typedNamedArgs.keys.map ((name) => new Symbol (name));
107+ invocation.namedArguments.forEach ((name, arg) {
108+ if (arg == null ) {
109+ if (! _typedNamedArgSymbols.contains (name)) {
110+ // Incorrect usage of [typed], something like:
111+ // `when(obj.fn(a: typed(any)))`.
112+ throw new ArgumentError (
113+ 'A typed argument was passed in as a named argument named "$name ", '
114+ 'but did not a value for its name. Each typed argument that is '
115+ 'passed as a named argument needs to specify the `name` argument. '
116+ 'For example: `when(obj.fn(x: typed(any, name: "x")))`.' );
117+ }
118+ } else {
119+ // Add each real named argument that was _not_ passed with [typed].
120+ namedArguments[name] = arg;
121+ }
122+ });
123+
124+ _typedNamedArgs.forEach ((name, arg) {
125+ Symbol nameSymbol = new Symbol (name);
126+ if (! invocation.namedArguments.containsKey (nameSymbol)) {
127+ // Incorrect usage of [name], something like:
128+ // `when(obj.fn(typed(any, name: 'a')))`.
129+ throw new ArgumentError (
130+ 'A typed argument was declared with name $name , but was not passed '
131+ 'as an argument named $name .' );
132+ }
133+ if (invocation.namedArguments[nameSymbol] != null ) {
134+ // Incorrect usage of [name], something like:
135+ // `when(obj.fn(a: typed(any, name: 'b'), b: "string"))`.
136+ throw new ArgumentError (
137+ 'A typed argument was declared with name $name , but a different '
138+ 'value (${invocation .namedArguments [nameSymbol ]}) was passed as '
139+ '$name .' );
140+ }
141+ namedArguments[nameSymbol] = arg;
142+ });
143+
144+ return namedArguments;
145+ }
146+
147+ static List <dynamic > _reconstitutePositionalArgs (Invocation invocation) {
148+ var positionalArguments = < dynamic > [];
149+ var nullPositionalArguments =
150+ invocation.positionalArguments.where ((arg) => arg == null );
151+ if (_typedArgs.length != nullPositionalArguments.length) {
152+ throw new ArgumentError (
153+ 'null arguments are not allowed alongside typed(); use '
154+ '"typed(eq(null))"' );
155+ }
156+ int i = 0 ;
157+ int j = 0 ;
158+ while (i < _typedArgs.length && j < invocation.positionalArguments.length) {
159+ var arg = _typedArgs[i];
160+ if (invocation.positionalArguments[j] == null ) {
161+ // [typed] was used; add the [_ArgMatcher] given to [typed].
162+ positionalArguments.add (arg);
163+ i++ ;
164+ j++ ;
165+ } else {
166+ // [typed] was not used; add the [_ArgMatcher] from [invocation].
167+ positionalArguments.add (invocation.positionalArguments[j]);
168+ j++ ;
169+ }
170+ }
171+ while (j < invocation.positionalArguments.length) {
172+ // Some trailing non-[typed] arguments.
173+ positionalArguments.add (invocation.positionalArguments[j]);
174+ j++ ;
175+ }
176+
177+ return positionalArguments;
178+ }
179+
180+ FakeInvocation ._(
181+ this .memberName,
182+ this .positionalArguments,
183+ this .namedArguments,
184+ this .isGetter,
185+ this .isMethod,
186+ this .isSetter);
187+ }
188+
58189named (var mock, {String name, int hashCode}) => mock
59190 .._givenName = name
60191 .._givenHashCode = hashCode;
@@ -300,6 +431,15 @@ get captureAny => new _ArgMatcher(anything, true);
300431captureThat (Matcher matcher) => new _ArgMatcher (matcher, true );
301432argThat (Matcher matcher) => new _ArgMatcher (matcher, false );
302433
434+ /*=T*/ typed/*<T>*/ (_ArgMatcher matcher, {String name}) {
435+ if (name == null ) {
436+ _typedArgs.add (matcher);
437+ } else {
438+ _typedNamedArgs[name] = matcher;
439+ }
440+ return null ;
441+ }
442+
303443class VerificationResult {
304444 List captured = [];
305445 int callCount;
@@ -413,3 +553,14 @@ void logInvocations(List<Mock> mocks) {
413553 print (inv.toString ());
414554 });
415555}
556+
557+ /// Should only be used during Mockito testing.
558+ void resetMockitoState () {
559+ _whenInProgress = false ;
560+ _verificationInProgress = false ;
561+ _whenCall = null ;
562+ _verifyCalls.clear ();
563+ _capturedArgs.clear ();
564+ _typedArgs.clear ();
565+ _typedNamedArgs.clear ();
566+ }
0 commit comments