• 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 static org.mockito.internal.invocation.ArgumentsProcessor.argumentsToMatchers;
9 import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor;
10 import static org.mockito.internal.invocation.TypeSafeMatching.matchesTypeSafe;
11 
12 import java.io.Serializable;
13 import java.lang.reflect.Method;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.LinkedList;
17 import java.util.List;
18 import org.mockito.ArgumentMatcher;
19 import org.mockito.internal.matchers.CapturesArguments;
20 import org.mockito.internal.reporting.PrintSettings;
21 import org.mockito.invocation.DescribedInvocation;
22 import org.mockito.invocation.Invocation;
23 import org.mockito.invocation.Location;
24 import org.mockito.invocation.MatchableInvocation;
25 
26 /**
27  * 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.
28  */
29 @SuppressWarnings("serial")
30 public class InvocationMatcher implements MatchableInvocation, DescribedInvocation, Serializable {
31 
32     private final Invocation invocation;
33     private final List<ArgumentMatcher<?>> matchers;
34 
35     @SuppressWarnings({ "rawtypes", "unchecked" })
InvocationMatcher(Invocation invocation, List<ArgumentMatcher> matchers)36     public InvocationMatcher(Invocation invocation, List<ArgumentMatcher> matchers) {
37         this.invocation = invocation;
38         if (matchers.isEmpty()) {
39             this.matchers = (List) argumentsToMatchers(invocation.getArguments());
40         } else {
41             this.matchers = (List) matchers;
42         }
43     }
44 
45     @SuppressWarnings("rawtypes")
InvocationMatcher(Invocation invocation)46     public InvocationMatcher(Invocation invocation) {
47         this(invocation, Collections.<ArgumentMatcher> emptyList());
48     }
49 
createFrom(List<Invocation> invocations)50     public static List<InvocationMatcher> createFrom(List<Invocation> invocations) {
51         LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>();
52         for (Invocation i : invocations) {
53             out.add(new InvocationMatcher(i));
54         }
55         return out;
56     }
57 
getMethod()58     public Method getMethod() {
59         return invocation.getMethod();
60     }
61 
62     @Override
getInvocation()63     public Invocation getInvocation() {
64         return invocation;
65     }
66 
67     @Override
68     @SuppressWarnings({ "unchecked", "rawtypes" })
getMatchers()69     public List<ArgumentMatcher> getMatchers() {
70         return (List) matchers;
71     }
72 
73     @Override
74     @SuppressWarnings({ "unchecked", "rawtypes" })
toString()75     public String toString() {
76         return new PrintSettings().print((List) matchers, invocation);
77     }
78 
79     @Override
matches(Invocation candidate)80     public boolean matches(Invocation candidate) {
81         return invocation.getMock().equals(candidate.getMock()) && hasSameMethod(candidate) && argumentsMatch(candidate);
82     }
83 
84     /**
85      * similar means the same method name, same mock, unverified and: if arguments are the same cannot be overloaded
86      */
87     @Override
hasSimilarMethod(Invocation candidate)88     public boolean hasSimilarMethod(Invocation candidate) {
89         String wantedMethodName = getMethod().getName();
90         String candidateMethodName = candidate.getMethod().getName();
91 
92         if (!wantedMethodName.equals(candidateMethodName)) {
93             return false;
94         }
95         if (candidate.isVerified()) {
96             return false;
97         }
98         if (getInvocation().getMock() != candidate.getMock()) {
99             return false;
100         }
101         if (hasSameMethod(candidate)) {
102             return true;
103         }
104 
105         return !argumentsMatch(candidate);
106     }
107 
108     @Override
hasSameMethod(Invocation candidate)109     public boolean hasSameMethod(Invocation candidate) {
110         // not using method.equals() for 1 good reason:
111         // sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest
112         Method m1 = invocation.getMethod();
113         Method m2 = candidate.getMethod();
114 
115         if (m1.getName() != null && m1.getName().equals(m2.getName())) {
116             /* Avoid unnecessary cloning */
117             Class<?>[] params1 = m1.getParameterTypes();
118             Class<?>[] params2 = m2.getParameterTypes();
119             return Arrays.equals(params1, params2);
120         }
121         return false;
122     }
123 
124     @Override
getLocation()125     public Location getLocation() {
126         return invocation.getLocation();
127     }
128 
129     @Override
captureArgumentsFrom(Invocation invocation)130     public void captureArgumentsFrom(Invocation invocation) {
131         MatcherApplicationStrategy strategy = getMatcherApplicationStrategyFor(invocation, matchers);
132         strategy.forEachMatcherAndArgument(captureArgument());
133     }
134 
captureArgument()135     private ArgumentMatcherAction captureArgument() {
136         return new ArgumentMatcherAction() {
137 
138             @Override
139             public boolean apply(ArgumentMatcher<?> matcher, Object argument) {
140                 if (matcher instanceof CapturesArguments) {
141                     ((CapturesArguments) matcher).captureFrom(argument);
142                 }
143 
144                 return true;
145             }
146         };
147     }
148 
149     @SuppressWarnings({ "rawtypes", "unchecked" })
150     private boolean argumentsMatch(Invocation actual) {
151         List matchers = getMatchers();
152         return getMatcherApplicationStrategyFor(actual, matchers).forEachMatcherAndArgument( matchesTypeSafe());
153     }
154 }
155