org.test4j.mock.faking.FakeInvoker Maven / Gradle / Ivy
package org.test4j.mock.faking;
import g_asm.org.objectweb.asm.Type;
import org.test4j.mock.Invocation;
import org.test4j.mock.MockUp;
import org.test4j.mock.faking.fluent.FluentMockUp;
import org.test4j.mock.faking.meta.FakeInvocation;
import org.test4j.mock.faking.meta.FakeMethod;
import org.test4j.mock.faking.modifier.BridgeFakeInvocation;
import org.test4j.mock.faking.modifier.BridgeTransformer;
import org.test4j.mock.faking.util.ClassLoad;
import org.test4j.mock.faking.util.ReflectUtility;
import org.test4j.mock.faking.util.TypeUtility;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.Objects;
import static org.test4j.mock.faking.modifier.BridgeFakeInvocation.Enter_Non_Mock_Block;
import static org.test4j.mock.faking.util.ReflectUtility.invoke;
/**
* 执行调用{@link MockUp} 的mock操作
*
* @author darui.wu
*/
@SuppressWarnings({"unused", "rawtypes"})
public class FakeInvoker {
/**
* 真实对象实例, not MockUp instance
*/
private final Object target;
/**
* new MockUp()实例
*/
private final FakeMethod fakeMethod;
/**
* 真实对象对应类名称描述 (L....;)
*/
private final String realClassDesc;
/**
* 真实方法的方法名称
*/
private final String methodName;
/**
* 真实方法的参数描述
*/
private final String paramsDesc;
/**
* args of method, 第一个参数可能是Invocation
*/
private final Object[] args;
public FakeInvoker(Object target, FakeMethod fakeMethod, Class realClass, Method method, Object[] args) {
this.target = target;
this.fakeMethod = fakeMethod;
this.realClassDesc = TypeUtility.classPath(realClass);
this.methodName = method.getName();
this.paramsDesc = Type.getMethodDescriptor(method);
this.args = args;
}
public FakeInvoker(Object target, FakeMethod fakeMethod, String realClassDesc, String methodName,
String paramsDesc, Object[] args) {
this.target = target;
this.fakeMethod = fakeMethod;
this.realClassDesc = realClassDesc;
this.methodName = methodName;
this.paramsDesc = paramsDesc;
this.args = args;
}
/**
* 执行具体的fake逻辑
*
* called by {@link BridgeFakeInvocation#invoke(Object, Method, Object[])}
*
* @return ignore
*/
public Object callFakeMethod() {
assert fakeMethod != null;
if (this.fakeMethod.fake instanceof FluentMockUp) {
return this.callFluentMockUpInvocation();
} else if (this.fakeMethod.meta.hasInvocation) {
return this.callMockWithInvocation();
} else {
return callMockWithoutInvocation(this.fakeMethod, args);
}
}
private Object callFluentMockUpInvocation() {
return ((FluentMockUp) this.fakeMethod.fake).invoke(this.newInvocation(), fakeMethod.meta.methodDesc, args);
}
/**
* 调用没有 Invocation 参数的mock方法
*
* @return ignore
*/
public static Object callMockWithoutInvocation(FakeMethod fakeMethod, Object[] args) {
Method method = fakeMethod.findMockMethod();
return invoke(method, () -> method.invoke(fakeMethod.fake, args));
}
/**
* 调用带 Invocation参数的mock方法
*
* @return ignore
*/
private Object callMockWithInvocation() {
if (this.isCallSuperOverrideMethod()) {
return Enter_Non_Mock_Block;
} else {
Invocation invocation = this.newInvocation();
Object result = callMockWithInvocation(this.fakeMethod, invocation, args);
if (invocation instanceof FakeInvocation && ((FakeInvocation) invocation).isProceedIntoConstructor()) {
return Enter_Non_Mock_Block;
} else {
return result;
}
}
}
/**
* 是否进入了重载方法调用
* super.sameName(same para type)
*
* @return ignore
*/
private boolean isCallSuperOverrideMethod() {
/* mock 静态方法 **/
FakeInvocation invocation = this.fakeMethod.getProceedingInvocation();
if (target == null || invocation == null) {
return false;
}
Executable executable = invocation.getInvokedMember();
if (target == invocation.getTarget() && executable instanceof Method) {
String invokedClassDesc = TypeUtility.classPath(executable.getDeclaringClass());
return !Objects.equals(invokedClassDesc, realClassDesc);
} else {
return false;
}
}
public static Object callMockWithInvocation(FakeMethod fakeMethod, Invocation inv, Object[] args) {
Object[] args2 = new Object[1 + args.length];
args2[0] = inv;
if (args.length > 0) {
System.arraycopy(args, 0, args2, 1, args.length);
}
Method method = fakeMethod.findMockMethod();
return invoke(method, () -> method.invoke(fakeMethod.fake, args2));
}
protected Invocation newInvocation() {
Class fakedClass = ClassLoad.loadClass(realClassDesc);
Executable invokedMember = ReflectUtility.findMethodByDesc(fakedClass, methodName, paramsDesc);
return new FakeInvocation(target, args, fakeMethod, invokedMember);
}
public static String getHostClassName() {
return BridgeTransformer.initBridgeField();
}
}