@@ -11,6 +11,8 @@ _WhenCall _whenCall = null;
11
11
final List <_VerifyCall > _verifyCalls = < _VerifyCall > [];
12
12
final _TimeStampProvider _timer = new _TimeStampProvider ();
13
13
final List _capturedArgs = [];
14
+ final List <_ArgMatcher > _typedArgs = < _ArgMatcher > [];
15
+ final Map <String , _ArgMatcher > _typedNamedArgs = < String , _ArgMatcher > {};
14
16
15
17
class Mock {
16
18
final List <RealCall > _realCalls = < RealCall > [];
@@ -24,6 +26,9 @@ class Mock {
24
26
}
25
27
26
28
dynamic noSuchMethod (Invocation invocation) {
29
+ if (_typedArgs.isNotEmpty || _typedNamedArgs.isNotEmpty) {
30
+ invocation = _reconstituteInvocation (invocation);
31
+ }
27
32
if (_whenInProgress) {
28
33
_whenCall = new _WhenCall (this , invocation);
29
34
return null ;
@@ -48,6 +53,132 @@ class Mock {
48
53
String toString () => _givenName != null ? _givenName : runtimeType.toString ();
49
54
}
50
55
56
+ // Return a new [Invocation], reconstituted from [invocation], [_typedArgs],
57
+ // and [_typedNamedArgs].
58
+ Invocation _reconstituteInvocation (Invocation invocation) {
59
+ var newInvocation = new FakeInvocation (invocation);
60
+ return newInvocation;
61
+ }
62
+
63
+ /// An Invocation implementation that allows all attributes to be passed into
64
+ /// the constructor.
65
+ class FakeInvocation extends Invocation {
66
+ final Symbol memberName;
67
+ final Map <Symbol , dynamic > namedArguments;
68
+ final List <dynamic > positionalArguments;
69
+ final bool isGetter;
70
+ final bool isMethod;
71
+ final bool isSetter;
72
+
73
+ factory FakeInvocation (Invocation invocation) {
74
+ if (_typedArgs.isEmpty && _typedNamedArgs.isEmpty) {
75
+ throw new StateError ("FakeInvocation called when no typed calls have been saved." );
76
+ }
77
+
78
+ // Handle named arguments first, so that we can provide useful errors for
79
+ // the various bad states. If all is well with the named arguments, then we
80
+ // can process the positional arguments, and resort to more general errors
81
+ // if the state is still bad.
82
+ var namedArguments = _reconstituteNamedArgs (invocation);
83
+ var positionalArguments = _reconstitutePositionalArgs (invocation);
84
+
85
+ _typedArgs.clear ();
86
+ _typedNamedArgs.clear ();
87
+
88
+ return new FakeInvocation ._(
89
+ invocation.memberName,
90
+ positionalArguments,
91
+ namedArguments,
92
+ invocation.isGetter,
93
+ invocation.isMethod,
94
+ invocation.isSetter);
95
+ }
96
+
97
+ static Map <Symbol ,dynamic > _reconstituteNamedArgs (Invocation invocation) {
98
+ var namedArguments = < Symbol , dynamic > {};
99
+ var _typedNamedArgSymbols = _typedNamedArgs.keys.map ((name) => new Symbol (name));
100
+ invocation.namedArguments.forEach ((name, arg) {
101
+ if (arg == null ) {
102
+ if (! _typedNamedArgSymbols.contains (name)) {
103
+ // Incorrect usage of [typed], something like:
104
+ // `when(obj.fn(a: typed(any)))`.
105
+ throw new ArgumentError (
106
+ 'A typed argument was passed in as a named argument named "$name ", '
107
+ 'but did not a value for its name. Each typed argument that is '
108
+ 'passed as a named argument needs to specify the `name` argument. '
109
+ 'For example: `when(obj.fn(x: typed(any, name: "x")))`.' );
110
+ }
111
+ } else {
112
+ // Add each real named argument that was _not_ passed with [typed].
113
+ namedArguments[name] = arg;
114
+ }
115
+ });
116
+
117
+ _typedNamedArgs.forEach ((name, arg) {
118
+ Symbol nameSymbol = new Symbol (name);
119
+ if (! invocation.namedArguments.containsKey (nameSymbol)) {
120
+ // Incorrect usage of [name], something like:
121
+ // `when(obj.fn(typed(any, name: 'a')))`.
122
+ throw new ArgumentError (
123
+ 'A typed argument was declared with name $name , but was not passed '
124
+ 'as an argument named $name .' );
125
+ }
126
+ if (invocation.namedArguments[nameSymbol] != null ) {
127
+ // Incorrect usage of [name], something like:
128
+ // `when(obj.fn(a: typed(any, name: 'b'), b: "string"))`.
129
+ throw new ArgumentError (
130
+ 'A typed argument was declared with name $name , but a different '
131
+ 'value (${invocation .namedArguments [nameSymbol ]}) was passed as '
132
+ '$name .' );
133
+ }
134
+ namedArguments[nameSymbol] = arg;
135
+ });
136
+
137
+ return namedArguments;
138
+ }
139
+
140
+ static List <dynamic > _reconstitutePositionalArgs (Invocation invocation) {
141
+ var positionalArguments = < dynamic > [];
142
+ var nullPositionalArguments =
143
+ invocation.positionalArguments.where ((arg) => arg == null );
144
+ if (_typedArgs.length != nullPositionalArguments.length) {
145
+ throw new ArgumentError (
146
+ 'null arguments are not allowed alongside typed(); use '
147
+ '"typed(eq(null))"' );
148
+ }
149
+ int i = 0 ;
150
+ int j = 0 ;
151
+ while (i < _typedArgs.length && j < invocation.positionalArguments.length) {
152
+ var arg = _typedArgs[i];
153
+ if (invocation.positionalArguments[j] == null ) {
154
+ // [typed] was used; add the [_ArgMatcher] given to [typed].
155
+ positionalArguments.add (arg);
156
+ i++ ;
157
+ j++ ;
158
+ } else {
159
+ // [typed] was not used; add the [_ArgMatcher] from [invocation].
160
+ positionalArguments.add (invocation.positionalArguments[j]);
161
+ j++ ;
162
+ }
163
+ }
164
+ while (j < invocation.positionalArguments.length) {
165
+ // Some trailing non-[typed] arguments.
166
+ positionalArguments.add (invocation.positionalArguments[j]);
167
+ j++ ;
168
+ }
169
+
170
+ return positionalArguments;
171
+ }
172
+
173
+ FakeInvocation ._(
174
+ this .memberName,
175
+ this .positionalArguments,
176
+ this .namedArguments,
177
+ this .isGetter,
178
+ this .isMethod,
179
+ this .isSetter);
180
+ }
181
+
51
182
named (var mock, {String name, int hashCode}) => mock
52
183
.._givenName = name
53
184
.._givenHashCode = hashCode;
@@ -291,6 +422,15 @@ get captureAny => new _ArgMatcher(anything, true);
291
422
captureThat (Matcher matcher) => new _ArgMatcher (matcher, true );
292
423
argThat (Matcher matcher) => new _ArgMatcher (matcher, false );
293
424
425
+ /*=T*/ typed/*<T>*/ (_ArgMatcher matcher, {String name}) {
426
+ if (name == null ) {
427
+ _typedArgs.add (matcher);
428
+ } else {
429
+ _typedNamedArgs[name] = matcher;
430
+ }
431
+ return null ;
432
+ }
433
+
294
434
class VerificationResult {
295
435
List captured = [];
296
436
int callCount;
@@ -404,3 +544,14 @@ void logInvocations(List<Mock> mocks) {
404
544
print (inv.toString ());
405
545
});
406
546
}
547
+
548
+ /// Should only be used during Mockito testing.
549
+ void resetMockitoState () {
550
+ _whenInProgress = false ;
551
+ _verificationInProgress = false ;
552
+ _whenCall = null ;
553
+ _verifyCalls.clear ();
554
+ _capturedArgs.clear ();
555
+ _typedArgs.clear ();
556
+ _typedNamedArgs.clear ();
557
+ }
0 commit comments