• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 
6 package org.mockito.internal.invocation;
7 
8 import java.io.Serializable;
9 import java.lang.reflect.Method;
10 import java.util.Collections;
11 import java.util.LinkedList;
12 import java.util.List;
13 
14 import org.hamcrest.Matcher;
15 import org.mockito.internal.matchers.CapturesArguments;
16 import org.mockito.internal.matchers.MatcherDecorator;
17 import org.mockito.internal.matchers.VarargMatcher;
18 import org.mockito.internal.reporting.PrintSettings;
19 import org.mockito.invocation.DescribedInvocation;
20 import org.mockito.invocation.Invocation;
21 import org.mockito.invocation.Location;
22 
23 @SuppressWarnings("unchecked")
24 public class InvocationMatcher implements DescribedInvocation, CapturesArgumensFromInvocation, Serializable {
25 
26     private static final long serialVersionUID = -3047126096857467610L;
27     private final Invocation invocation;
28     private final List<Matcher> matchers;
29 
InvocationMatcher(Invocation invocation, List<Matcher> matchers)30     public InvocationMatcher(Invocation invocation, List<Matcher> matchers) {
31         this.invocation = invocation;
32         if (matchers.isEmpty()) {
33             this.matchers = ArgumentsProcessor.argumentsToMatchers(invocation.getArguments());
34         } else {
35             this.matchers = matchers;
36         }
37     }
38 
InvocationMatcher(Invocation invocation)39     public InvocationMatcher(Invocation invocation) {
40         this(invocation, Collections.<Matcher>emptyList());
41     }
42 
getMethod()43     public Method getMethod() {
44         return invocation.getMethod();
45     }
46 
getInvocation()47     public Invocation getInvocation() {
48         return this.invocation;
49     }
50 
getMatchers()51     public List<Matcher> getMatchers() {
52         return this.matchers;
53     }
54 
toString()55     public String toString() {
56         return new PrintSettings().print(matchers, invocation);
57     }
58 
matches(Invocation actual)59     public boolean matches(Invocation actual) {
60         return invocation.getMock().equals(actual.getMock())
61                 && hasSameMethod(actual)
62                 && new ArgumentsComparator().argumentsMatch(this, actual);
63     }
64 
safelyArgumentsMatch(Object[] actualArgs)65     private boolean safelyArgumentsMatch(Object[] actualArgs) {
66         try {
67             return new ArgumentsComparator().argumentsMatch(this, actualArgs);
68         } catch (Throwable t) {
69             return false;
70         }
71     }
72 
73     /**
74      * similar means the same method name, same mock, unverified
75      * and: if arguments are the same cannot be overloaded
76      */
hasSimilarMethod(Invocation candidate)77     public boolean hasSimilarMethod(Invocation candidate) {
78         String wantedMethodName = getMethod().getName();
79         String currentMethodName = candidate.getMethod().getName();
80 
81         final boolean methodNameEquals = wantedMethodName.equals(currentMethodName);
82         final boolean isUnverified = !candidate.isVerified();
83         final boolean mockIsTheSame = getInvocation().getMock() == candidate.getMock();
84         final boolean methodEquals = hasSameMethod(candidate);
85 
86         if (!methodNameEquals || !isUnverified || !mockIsTheSame) {
87             return false;
88         }
89 
90         final boolean overloadedButSameArgs = !methodEquals && safelyArgumentsMatch(candidate.getArguments());
91 
92         return !overloadedButSameArgs;
93     }
94 
hasSameMethod(Invocation candidate)95     public boolean hasSameMethod(Invocation candidate) {
96         //not using method.equals() for 1 good reason:
97         //sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest
98         Method m1 = invocation.getMethod();
99         Method m2 = candidate.getMethod();
100 
101         if (m1.getName() != null && m1.getName().equals(m2.getName())) {
102         	/* Avoid unnecessary cloning */
103         	Class[] params1 = m1.getParameterTypes();
104         	Class[] params2 = m2.getParameterTypes();
105         	if (params1.length == params2.length) {
106         	    for (int i = 0; i < params1.length; i++) {
107         		if (params1[i] != params2[i])
108         		    return false;
109         	    }
110         	    return true;
111         	}
112         }
113         return false;
114     }
115 
getLocation()116     public Location getLocation() {
117         return invocation.getLocation();
118     }
119 
captureArgumentsFrom(Invocation invocation)120     public void captureArgumentsFrom(Invocation invocation) {
121         for (int position = 0; position < matchers.size(); position++) {
122             Matcher m = matchers.get(position);
123             if (m instanceof CapturesArguments && invocation.getArguments().length > position) {
124                 if(isVariableArgument(invocation, position) && isVarargMatcher(m)) {
125                     ((CapturesArguments) m).captureFrom(invocation.getRawArguments()[position]);
126                 } else {
127                     ((CapturesArguments) m).captureFrom(invocation.getArguments()[position]);
128                 }
129             }
130         }
131     }
132 
isVarargMatcher(Matcher matcher)133     private boolean isVarargMatcher(Matcher matcher) {
134         Matcher actualMatcher = matcher;
135         if (actualMatcher instanceof MatcherDecorator) {
136             actualMatcher = ((MatcherDecorator) actualMatcher).getActualMatcher();
137         }
138         return actualMatcher instanceof VarargMatcher;
139     }
140 
isVariableArgument(Invocation invocation, int position)141     private boolean isVariableArgument(Invocation invocation, int position) {
142         return invocation.getRawArguments().length - 1 == position
143                 && invocation.getRawArguments()[position] != null
144                 && invocation.getRawArguments()[position].getClass().isArray()
145                 && invocation.getMethod().isVarArgs();
146     }
147 
createFrom(List<Invocation> invocations)148     public static List<InvocationMatcher> createFrom(List<Invocation> invocations) {
149         LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>();
150 
151         for (Invocation i : invocations) {
152             out.add(new InvocationMatcher(i));
153         }
154 
155         return out;
156     }
157 }