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 APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external
APIs; JUnit (4 & 5) and TestNG test runners are supported.
It also contains an advanced code coverage tool.
The newest version!
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.invocation;
import static mockit.internal.util.TypeDescriptor.getClassForType;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import mockit.asm.types.JavaType;
import mockit.internal.expectations.argumentMatching.ArgumentMatcher;
import mockit.internal.expectations.state.MockedTypeCascade;
import mockit.internal.reflection.GenericTypeReflection;
import mockit.internal.reflection.GenericTypeReflection.GenericSignature;
import mockit.internal.state.TestRun;
import mockit.internal.util.ClassLoad;
import mockit.internal.util.DefaultValues;
import mockit.internal.util.ObjectMethods;
import mockit.internal.util.StackTrace;
import org.checkerframework.checker.index.qual.NonNegative;
@SuppressWarnings("OverlyComplexClass")
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
private final ExpectationError invocationCause;
@Nullable
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();
}
@Nullable
public AssertionError getInvocationCause() {
return invocationCause;
}
@NonNull
private Object determineDefaultReturnValueFromMethodSignature() {
if (instance != null) {
Object rv = ObjectMethods.evaluateOverride(instance, getMethodNameAndDescription(), getArgumentValues());
if (rv != null) {
return rv;
}
}
return UNDEFINED_DEFAULT_RETURN;
}
// 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();
}
@NonNull
public String getSignatureWithResolvedReturnType() {
String signature = arguments.genericSignature;
if (signature != null) {
// TODO: cache it for use in return type conversion, cascading, etc.
String classDesc = getClassDesc();
Class> mockedClass = instance != null ? instance.getClass() : ClassLoad.loadByInternalName(classDesc);
GenericTypeReflection reflection = new GenericTypeReflection(mockedClass, null);
signature = reflection.resolveSignature(classDesc, signature);
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(@Nullable Object mock, @NonNull String invokedClassDesc, @NonNull String invokedMethod) {
return (invokedClassDesc.equals(getClassDesc()) || mock != null && TestRun.mockFixture().isCaptured(mock))
&& (isMatchingGenericMethod(mock, invokedMethod) || isMatchingMethod(invokedMethod));
}
private boolean isMatchingGenericMethod(@Nullable Object mock, @NonNull String invokedMethod) {
if (mock != null && instance != null) {
String genericSignature = arguments.genericSignature;
if (genericSignature != null) {
Class> mockedClass = mock.getClass();
if (mockedClass != instance.getClass()) {
GenericTypeReflection typeReflection = new GenericTypeReflection(mockedClass, null);
GenericSignature parsedSignature = typeReflection.parseSignature(genericSignature);
return parsedSignature.satisfiesSignature(invokedMethod) && isMatchingMethodName(invokedMethod);
}
}
}
return false;
}
private boolean isMatchingMethod(@NonNull String invokedMethod) {
int returnTypeStartPos = getReturnTypePosition(invokedMethod);
if (returnTypeStartPos < 0) {
return false;
}
if (haveSameReturnTypes(invokedMethod, returnTypeStartPos)) {
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:
return isReturnTypeOfRecordedMethodAssignableToReturnTypeOfInvokedMethod(invokedMethod, returnTypeStartPos);
}
private boolean isMatchingMethodName(@NonNull String invokedMethod) {
int methodNameEndPos = invokedMethod.indexOf('(');
String methodName = invokedMethod.substring(0, methodNameEndPos + 1);
return getMethodNameAndDescription().startsWith(methodName);
}
// Returns -1 if the method names or parameters are different.
private int getReturnTypePosition(@NonNull String invokedMethod) {
String recordedMethod = getMethodNameAndDescription();
int i = 0;
while (true) {
char c = recordedMethod.charAt(i);
if (c != invokedMethod.charAt(i)) {
return -1;
}
i++;
if (c == ')') {
return i;
}
}
}
private boolean haveSameReturnTypes(@NonNull String invokedMethod, @NonNegative int returnTypeStartPos) {
String recordedMethod = getMethodNameAndDescription();
int n = invokedMethod.length();
if (n != recordedMethod.length()) {
return false;
}
int j = returnTypeStartPos;
while (true) {
char c = recordedMethod.charAt(j);
if (c != invokedMethod.charAt(j)) {
return false;
}
j++;
if (j == n) {
return true;
}
}
}
private boolean isReturnTypeOfRecordedMethodAssignableToReturnTypeOfInvokedMethod(@NonNull String invokedMethod,
@NonNegative int returnTypeStartPos) {
String recordedMethod = getMethodNameAndDescription();
JavaType recordedRT = JavaType.getType(recordedMethod.substring(returnTypeStartPos));
JavaType invokedRT = JavaType.getType(invokedMethod.substring(returnTypeStartPos));
return getClassForType(invokedRT).isAssignableFrom(getClassForType(recordedRT));
}
public boolean isMatch(@NonNull ExpectedInvocation other) {
return isMatch(other.instance, other.getClassDesc(), other.getMethodNameAndDescription(), null);
}
public boolean isMatch(@Nullable Object replayInstance, @NonNull String invokedClassDesc,
@NonNull String invokedMethod, @Nullable Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy