mockit.internal.expectations.invocation.ExpectedInvocation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006-2015 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.invocation;
import java.util.*;
import javax.annotation.*;
import mockit.external.asm.*;
import mockit.internal.*;
import mockit.internal.expectations.*;
import mockit.internal.expectations.argumentMatching.*;
import mockit.internal.state.*;
import mockit.internal.util.*;
public final class ExpectedInvocation
{
@Nonnull private static final Object UNDEFINED_DEFAULT_RETURN = new Object();
@Nullable public final Object instance;
@Nullable public Object replacementInstance;
public boolean matchInstance;
@Nonnull public final InvocationArguments arguments;
@Nullable public CharSequence customErrorMessage;
@Nullable private final ExpectationError invocationCause;
@Nullable private Object defaultReturnValue;
public ExpectedInvocation(
@Nullable Object mock, @Nonnull String mockedClassDesc, @Nonnull String mockNameAndDesc,
@Nullable String genericSignature, @Nonnull Object[] args)
{
instance = mock;
arguments = new InvocationArguments(0, mockedClassDesc, mockNameAndDesc, genericSignature, args);
invocationCause = null;
defaultReturnValue = determineDefaultReturnValueFromMethodSignature();
}
public ExpectedInvocation(
@Nullable Object mock, int access, @Nonnull String mockedClassDesc, @Nonnull String mockNameAndDesc,
boolean matchInstance, @Nullable String genericSignature, @Nonnull Object[] args)
{
instance = mock;
this.matchInstance = matchInstance;
arguments = new InvocationArguments(access, mockedClassDesc, mockNameAndDesc, genericSignature, args);
invocationCause = new ExpectationError();
defaultReturnValue = determineDefaultReturnValueFromMethodSignature();
}
@Nonnull
private Object determineDefaultReturnValueFromMethodSignature()
{
if (instance != null) {
Object rv = ObjectMethods.evaluateOverride(instance, getMethodNameAndDescription(), getArgumentValues());
if (rv != null) {
return rv;
}
}
return UNDEFINED_DEFAULT_RETURN;
}
@Nonnull
public String getCallerClassName()
{
//noinspection ConstantConditions
StackTrace st = new StackTrace(invocationCause);
int steIndex = 3;
String firstCaller = st.getElement(steIndex).getClassName();
steIndex += "mockit.internal.expectations.mocking.MockedBridge".equals(firstCaller) ? 2 : 1;
String secondCaller = st.getElement(steIndex).getClassName();
if (secondCaller.startsWith("sun.reflect.")) { // called through Reflection
steIndex += 3;
while (true) {
String nextCaller = st.getElement(steIndex).getClassName();
steIndex++;
if ("mockit.Deencapsulation".equals(nextCaller)) {
continue;
}
if (!nextCaller.contains(".reflect.") && !nextCaller.startsWith("mockit.internal.")) {
return nextCaller;
}
}
}
if (!secondCaller.equals(firstCaller)) {
return secondCaller;
}
String thirdCaller = st.getElement(steIndex + 1).getClassName();
return thirdCaller;
}
// Simple getters //////////////////////////////////////////////////////////////////////////////////////////////////
@Nonnull public String getClassDesc() { return arguments.classDesc; }
@Nonnull public String getClassName() { return arguments.getClassName(); }
@Nonnull public String getMethodNameAndDescription() { return arguments.methodNameAndDesc; }
@Nonnull public Object[] getArgumentValues() { return arguments.getValues(); }
public boolean isConstructor() { return arguments.isForConstructor(); }
@Nullable
public Object getRecordedInstance()
{
return replacementInstance != null ? replacementInstance : instance;
}
@Nonnull
public String getSignatureWithResolvedReturnType()
{
String signature = arguments.genericSignature;
if (signature != null) {
char firstTypeChar = signature.charAt(signature.indexOf(')') + 1);
if (firstTypeChar != 'T' && firstTypeChar != '[') {
return signature;
}
}
return arguments.methodNameAndDesc;
}
// Matching based on instance or mocked type ///////////////////////////////////////////////////////////////////////
public boolean isMatch(@Nonnull String invokedClassDesc, @Nonnull String invokedMethod)
{
return invokedClassDesc.equals(getClassDesc()) && isMatchingMethod(invokedMethod);
}
private boolean isMatchingMethod(@Nonnull String invokedMethod)
{
String nameAndDesc = getMethodNameAndDescription();
int i = 0;
// Will return false if the method names or parameters are different:
while (true) {
char c = nameAndDesc.charAt(i);
if (c != invokedMethod.charAt(i)) {
return false;
}
i++;
if (c == ')') {
break;
}
}
int n = invokedMethod.length();
if (n == nameAndDesc.length()) {
int j = i;
// Given return types of same length, will return true if they are identical:
while (true) {
char c = nameAndDesc.charAt(j);
if (c != invokedMethod.charAt(j)) {
break;
}
j++;
if (j == n) {
return true;
}
}
}
// At this point the methods are known to differ only in return type, so check if the return
// type of the recorded one is assignable to the return type of the one invoked:
Type rt1 = Type.getType(nameAndDesc.substring(i));
Type rt2 = Type.getType(invokedMethod.substring(i));
return TypeDescriptor.getClassForType(rt2).isAssignableFrom(TypeDescriptor.getClassForType(rt1));
}
public boolean isMatch(
@Nullable Object replayInstance, @Nonnull String invokedClassDesc, @Nonnull String invokedMethod,
@Nullable Map