forked from mockito/mockito
-
Notifications
You must be signed in to change notification settings - Fork 0
/
InvocationMatcher.java
181 lines (151 loc) · 6.21 KB
/
InvocationMatcher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.invocation;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.CapturesArguments;
import org.mockito.internal.reporting.PrintSettings;
import org.mockito.invocation.DescribedInvocation;
import org.mockito.invocation.Invocation;
import org.mockito.invocation.Location;
import static org.mockito.internal.invocation.ArgumentsComparator.argumentsMatch;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
@SuppressWarnings("unchecked")
/**
* In addition to all content of the invocation, the invocation matcher contains the argument matchers.
* Invocation matcher is used during verification and stubbing.
* In those cases, the user can provide argument matchers instead of 'raw' arguments.
* Raw arguments are converted to 'equals' matchers anyway.
*/
public class InvocationMatcher implements DescribedInvocation, CapturesArgumentsFromInvocation, Serializable {
private final Invocation invocation;
private final List<ArgumentMatcher> matchers;
public InvocationMatcher(Invocation invocation, List<ArgumentMatcher> matchers) {
this.invocation = invocation;
if (matchers.isEmpty()) {
this.matchers = ArgumentsProcessor.argumentsToMatchers(invocation.getArguments());
} else {
this.matchers = matchers;
}
}
public InvocationMatcher(Invocation invocation) {
this(invocation, Collections.<ArgumentMatcher>emptyList());
}
public Method getMethod() {
return invocation.getMethod();
}
public Invocation getInvocation() {
return this.invocation;
}
public List<ArgumentMatcher> getMatchers() {
return this.matchers;
}
public String toString() {
return new PrintSettings().print(matchers, invocation);
}
public boolean matches(Invocation actual) {
return invocation.getMock().equals(actual.getMock())
&& hasSameMethod(actual)
&& argumentsMatch(this, actual);
}
private boolean safelyArgumentsMatch(Object[] actualArgs) {
try {
return argumentsMatch(this, actualArgs);
} catch (Throwable t) {
return false;
}
}
/**
* similar means the same method name, same mock, unverified
* and: if arguments are the same cannot be overloaded
*/
public boolean hasSimilarMethod(Invocation candidate) {
String wantedMethodName = getMethod().getName();
String currentMethodName = candidate.getMethod().getName();
final boolean methodNameEquals = wantedMethodName.equals(currentMethodName);
final boolean isUnverified = !candidate.isVerified();
final boolean mockIsTheSame = getInvocation().getMock() == candidate.getMock();
final boolean methodEquals = hasSameMethod(candidate);
if (!methodNameEquals || !isUnverified || !mockIsTheSame) {
return false;
}
final boolean overloadedButSameArgs = !methodEquals && safelyArgumentsMatch(candidate.getArguments());
return !overloadedButSameArgs;
}
public boolean hasSameMethod(Invocation candidate) {
Method m1 = invocation.getMethod();
Method m2 = candidate.getMethod();
if (m1.getName() != null && m1.getName().equals(m2.getName())) {
/* Avoid unnecessary cloning */
Class<?>[] params1 = m1.getParameterTypes();
Class<?>[] params2 = m2.getParameterTypes();
if (params1.length == params2.length) {
for (int i = 0; i < params1.length; i++) {
if (params1[i] != params2[i])
return false;
}
return true;
}
}
return false;
}
public Location getLocation() {
return invocation.getLocation();
}
public void captureArgumentsFrom(Invocation invocation) {
captureRegularArguments(invocation);
captureVarargsPart(invocation);
}
private void captureRegularArguments(Invocation invocation) {
for (int position = 0; position < regularArgumentsSize(invocation); position++) {
ArgumentMatcher m = matchers.get(position);
if (m instanceof CapturesArguments) {
((CapturesArguments) m).captureFrom(invocation.getArgument(position));
}
}
}
private void captureVarargsPart(Invocation invocation) {
if (!invocation.getMethod().isVarArgs()) {
return;
}
int indexOfVararg = invocation.getRawArguments().length - 1;
for (ArgumentMatcher m : uniqueMatcherSet(indexOfVararg)) {
if (m instanceof CapturesArguments) {
Object rawArgument = invocation.getRawArguments()[indexOfVararg];
for (int i = 0; i < getLength(rawArgument); i++) {
((CapturesArguments) m).captureFrom(getArgument(rawArgument, i));
}
}
}
}
private int getLength(Object rawArgument) {
return rawArgument == null ? 1 : Array.getLength(rawArgument);
}
private Object getArgument(Object rawArgument, int i) {
return rawArgument == null ? null : Array.get(rawArgument, i);
}
private int regularArgumentsSize(Invocation invocation) {
return invocation.getMethod().isVarArgs() ?
invocation.getRawArguments().length - 1 // ignores vararg holder array
: matchers.size();
}
private Set<ArgumentMatcher> uniqueMatcherSet(int indexOfVararg) {
HashSet<ArgumentMatcher> set = new HashSet<ArgumentMatcher>();
for (int position = indexOfVararg; position < matchers.size(); position++) {
ArgumentMatcher matcher = matchers.get(position);
set.add(matcher);
}
return set;
}
public static List<InvocationMatcher> createFrom(List<Invocation> invocations) {
LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>();
for (Invocation i : invocations) {
out.add(new InvocationMatcher(i));
}
return out;
}
}