mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 20:30:11 +08:00
add association check at beginning of mock method
This commit is contained in:
parent
6d6e2ecb7f
commit
4c6fc5b228
@ -12,6 +12,7 @@ public class ConstPool {
|
||||
|
||||
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||
public static final String FIELD_TARGET_CLASS = "targetClass";
|
||||
public static final String FIELD_SCOPE = "scope";
|
||||
|
||||
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||
public static final String MOCK_DIAGNOSE = "com.alibaba.testable.core.annotation.MockDiagnose";
|
||||
|
@ -7,9 +7,10 @@ import org.objectweb.asm.tree.*;
|
||||
*/
|
||||
abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
||||
|
||||
private static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
||||
private static final String CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||
private static final String CLASS_MOCK_CONTEXT_UTIL = "com/alibaba/testable/core/util/MockContextUtil";
|
||||
protected static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
||||
protected static final String CLASS_TESTABLE_UTIL = "com/alibaba/testable/core/util/TestableUtil";
|
||||
protected static final String CLASS_MOCK_CONTEXT_UTIL = "com/alibaba/testable/core/util/MockContextUtil";
|
||||
protected static final String CLASS_INVOKE_RECORD_UTIL = "com/alibaba/testable/core/util/InvokeRecordUtil";
|
||||
private static final String FIELD_SOURCE_METHOD = "SOURCE_METHOD";
|
||||
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
||||
private static final String METHOD_PARAMETERS = "parameters";
|
||||
|
@ -4,10 +4,11 @@ import com.alibaba.testable.agent.constant.ConstPool;
|
||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||
import com.alibaba.testable.agent.util.ClassUtil;
|
||||
import com.alibaba.testable.core.model.MockScope;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import javax.lang.model.type.NullType;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.CONSTRUCTOR;
|
||||
@ -18,9 +19,10 @@ import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassNa
|
||||
*/
|
||||
public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
|
||||
private static final String CLASS_INVOKE_RECORD_UTIL = "com/alibaba/testable/core/util/InvokeRecordUtil";
|
||||
private static final String METHOD_RECORD_MOCK_INVOKE = "recordMockInvoke";
|
||||
private static final String SIGNATURE_INVOKE_RECORDER_METHOD = "([Ljava/lang/Object;ZZ)V";
|
||||
private static final String METHOD_IS_ASSOCIATED = "isAssociated";
|
||||
private static final String SIGNATURE_IS_ASSOCIATED = "()Z";
|
||||
|
||||
public MockClassHandler(String className) {
|
||||
this.mockClassName = className;
|
||||
@ -28,7 +30,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
|
||||
@Override
|
||||
protected void transform(ClassNode cn) {
|
||||
addGetInstanceMethod(cn);
|
||||
injectGetInstanceMethod(cn);
|
||||
for (MethodNode mn : cn.methods) {
|
||||
if (isMockMethod(mn)) {
|
||||
mn.access &= ~ACC_PRIVATE;
|
||||
@ -36,11 +38,36 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
mn.access |= ACC_PUBLIC;
|
||||
unfoldTargetClass(mn);
|
||||
injectInvokeRecorder(mn);
|
||||
injectAssociationChecker(mn);
|
||||
handleTestableUtil(mn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add method to fetch singleton instance of this mock class
|
||||
*/
|
||||
private void injectGetInstanceMethod(ClassNode cn) {
|
||||
MethodNode getInstanceMethod = new MethodNode(ACC_PUBLIC | ACC_STATIC, GET_TESTABLE_REF,
|
||||
VOID_ARGS + ClassUtil.toByteCodeClassName(mockClassName), null, null);
|
||||
InsnList il = new InsnList();
|
||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
LabelNode label = new LabelNode();
|
||||
il.add(new JumpInsnNode(IFNONNULL, label));
|
||||
il.add(new TypeInsnNode(NEW, mockClassName));
|
||||
il.add(new InsnNode(DUP));
|
||||
il.add(new MethodInsnNode(INVOKESPECIAL, mockClassName, CONSTRUCTOR, VOID_ARGS + VOID_RES, false));
|
||||
il.add(new FieldInsnNode(PUTSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
il.add(label);
|
||||
il.add(new FrameNode(F_SAME, 0, null, 0, null));
|
||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
il.add(new InsnNode(ARETURN));
|
||||
getInstanceMethod.instructions = il;
|
||||
getInstanceMethod.maxStack = 2;
|
||||
getInstanceMethod.maxLocals = 0;
|
||||
cn.methods.add(getInstanceMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* put targetClass parameter in @MockMethod to first parameter of the mock method
|
||||
*/
|
||||
@ -74,25 +101,41 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void addGetInstanceMethod(ClassNode cn) {
|
||||
MethodNode getInstanceMethod = new MethodNode(ACC_PUBLIC | ACC_STATIC, GET_TESTABLE_REF,
|
||||
VOID_ARGS + ClassUtil.toByteCodeClassName(mockClassName), null, null);
|
||||
private void injectAssociationChecker(MethodNode mn) {
|
||||
if (isGlobalScope(mn)) {
|
||||
return;
|
||||
}
|
||||
LabelNode firstLine = new LabelNode(new Label());
|
||||
InsnList il = new InsnList();
|
||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
LabelNode label = new LabelNode();
|
||||
il.add(new JumpInsnNode(IFNONNULL, label));
|
||||
il.add(new TypeInsnNode(NEW, mockClassName));
|
||||
il.add(new InsnNode(DUP));
|
||||
il.add(new MethodInsnNode(INVOKESPECIAL, mockClassName, CONSTRUCTOR, VOID_ARGS + VOID_RES, false));
|
||||
il.add(new FieldInsnNode(PUTSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
il.add(label);
|
||||
il.add(new FrameNode(F_SAME, 0, null, 0, null));
|
||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
||||
il.add(new InsnNode(ARETURN));
|
||||
getInstanceMethod.instructions = il;
|
||||
getInstanceMethod.maxStack = 2;
|
||||
getInstanceMethod.maxLocals = 0;
|
||||
cn.methods.add(getInstanceMethod);
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_CONTEXT_UTIL, METHOD_IS_ASSOCIATED,
|
||||
SIGNATURE_IS_ASSOCIATED, false));
|
||||
il.add(new JumpInsnNode(IFNE, firstLine));
|
||||
il.add(firstLine);
|
||||
il.add( new FrameNode(F_SAME, 0, null, 0, null));
|
||||
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||
}
|
||||
|
||||
private boolean isGlobalScope(MethodNode mn) {
|
||||
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc) ||
|
||||
ClassUtil.toByteCodeClassName(ConstPool.MOCK_CONSTRUCTOR).equals(an.desc)) {
|
||||
MockScope scope = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_SCOPE,
|
||||
MockScope.ASSOCIATED, MockScope.class);
|
||||
if (scope.equals(MockScope.GLOBAL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private LabelNode getFirstLabel(MethodNode mn) {
|
||||
for (AbstractInsnNode n : mn.instructions) {
|
||||
if (n instanceof LabelNode) {
|
||||
return (LabelNode)n;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMockMethod(MethodNode mn) {
|
||||
@ -137,7 +180,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
||||
il.add(new InsnNode(ICONST_1));
|
||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
||||
SIGNATURE_INVOKE_RECORDER_METHOD, false));
|
||||
mn.instructions.insertBefore(mn.instructions.get(0), il);
|
||||
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||
}
|
||||
|
||||
private boolean isMockForConstructor(MethodNode mn) {
|
||||
|
@ -16,7 +16,6 @@ import java.util.Set;
|
||||
*/
|
||||
public class TestClassHandler extends BaseClassWithContextHandler {
|
||||
|
||||
private static final String CLASS_MOCK_CONTEXT_UTIL = "com/alibaba/testable/core/util/MockContextUtil";
|
||||
private static final String METHOD_INIT = "init";
|
||||
private static final String DESC_METHOD_INIT = "()V";
|
||||
private static final String METHOD_CLEAN = "clean";
|
||||
|
Loading…
Reference in New Issue
Block a user