org.test4j.mock.faking.meta.FakeMethod Maven / Gradle / Ivy
package org.test4j.mock.faking.meta;
import g_asm.org.objectweb.asm.Type;
import lombok.Getter;
import org.test4j.annotations.Mock;
import org.test4j.mock.Invocation;
import org.test4j.mock.MockUp;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* MockUp @Mock 方法
*
* @author wudarui
*/
@SuppressWarnings("rawtypes")
public class FakeMethod {
/**
* {@link MockUp} 实例
*/
public final AbstractFake fake;
/**
* {@link Mock} 方法元数据
*/
public final MethodId meta;
/**
* {@link Mock} 方法
*/
private Method mockMethod;
@Getter
private final boolean hasMatchingRealMethod;
/**
* 被调用次数
*/
private final AtomicInteger invocationCount = new AtomicInteger(0);
/**
* 当前Invocation
*/
private final ThreadLocal proceedingInvocation = new ThreadLocal<>();
public FakeMethod(AbstractFake fake, MethodId meta) {
this.fake = fake;
this.meta = meta;
this.hasMatchingRealMethod = false;
}
public Method findMockMethod() {
if (this.mockMethod != null) {
return this.mockMethod;
}
String[] types = meta.getParaTypeNames();
Class aClass = fake.getClass();
while (this.mockMethod == null && MockUp.class.isAssignableFrom(aClass) && !aClass.equals(MockUp.class)) {
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
if (!Objects.equals(meta.name, method.getName())) {
continue;
}
if (matchParas(method, types)) {
this.mockMethod = method;
break;
}
}
aClass = aClass.getSuperclass();
}
if (this.mockMethod == null) {
String desc = meta.name + "(" + String.join(", ", types) + ")";
throw new IllegalArgumentException("No compatible method found: " + desc);
} else {
return this.mockMethod;
}
}
private boolean matchParas(Method method, String[] types) {
int count = method.getParameterCount();
if (count != types.length) {
return false;
}
Class[] pTypes = method.getParameterTypes();
for (int index = 0; index < count; index++) {
String metaTypeName = types[index];
String methodParaType = Type.getType(pTypes[index]).getClassName();
if (!Objects.equals(metaTypeName, methodParaType)) {
return false;
}
}
return true;
}
public boolean isProceeding() {
FakeInvocation invocation = this.getProceedingInvocation();
if (invocation != null && invocation.isProceeding()) {
invocation.setProceeding(false);
return true;
} else {
invocationCount.incrementAndGet();
return false;
}
}
public int getTimesInvoked() {
return invocationCount.get();
}
public void setProceedingInvocation(Invocation invocation) {
proceedingInvocation.set(invocation);
}
public FakeInvocation getProceedingInvocation() {
return (FakeInvocation) proceedingInvocation.get();
}
/**
* 设置当前正在执行的Invocation, 避免循环调用
*
* @param invocation Invocation
*/
public void setProceedingInvocation4NonRecursive(Invocation invocation) {
((FakeInvocation) invocation).setProceeding(true);
proceedingInvocation.set(invocation);
}
/**
* 清除当前调用器设置
*/
public void clearProceedIndicator() {
proceedingInvocation.set(null);
}
@Override
public String toString() {
return "(" + invocationCount.get() + ")" + meta.toString();
}
}