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