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_METHOD = "targetMethod";
|
||||||
public static final String FIELD_TARGET_CLASS = "targetClass";
|
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_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||||
public static final String MOCK_DIAGNOSE = "com.alibaba.testable.core.annotation.MockDiagnose";
|
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 {
|
abstract public class BaseClassWithContextHandler extends BaseClassHandler {
|
||||||
|
|
||||||
private static final String CLASS_TESTABLE_TOOL = "com/alibaba/testable/core/tool/TestableTool";
|
protected 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";
|
protected 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_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_SOURCE_METHOD = "SOURCE_METHOD";
|
||||||
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
private static final String FIELD_MOCK_CONTEXT = "MOCK_CONTEXT";
|
||||||
private static final String METHOD_PARAMETERS = "parameters";
|
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.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.agent.util.AnnotationUtil;
|
import com.alibaba.testable.agent.util.AnnotationUtil;
|
||||||
import com.alibaba.testable.agent.util.ClassUtil;
|
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.Type;
|
||||||
import org.objectweb.asm.tree.*;
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
import javax.lang.model.type.NullType;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.alibaba.testable.agent.constant.ConstPool.CONSTRUCTOR;
|
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 {
|
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 METHOD_RECORD_MOCK_INVOKE = "recordMockInvoke";
|
||||||
private static final String SIGNATURE_INVOKE_RECORDER_METHOD = "([Ljava/lang/Object;ZZ)V";
|
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) {
|
public MockClassHandler(String className) {
|
||||||
this.mockClassName = className;
|
this.mockClassName = className;
|
||||||
@ -28,7 +30,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void transform(ClassNode cn) {
|
protected void transform(ClassNode cn) {
|
||||||
addGetInstanceMethod(cn);
|
injectGetInstanceMethod(cn);
|
||||||
for (MethodNode mn : cn.methods) {
|
for (MethodNode mn : cn.methods) {
|
||||||
if (isMockMethod(mn)) {
|
if (isMockMethod(mn)) {
|
||||||
mn.access &= ~ACC_PRIVATE;
|
mn.access &= ~ACC_PRIVATE;
|
||||||
@ -36,11 +38,36 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
mn.access |= ACC_PUBLIC;
|
mn.access |= ACC_PUBLIC;
|
||||||
unfoldTargetClass(mn);
|
unfoldTargetClass(mn);
|
||||||
injectInvokeRecorder(mn);
|
injectInvokeRecorder(mn);
|
||||||
|
injectAssociationChecker(mn);
|
||||||
handleTestableUtil(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
|
* 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) {
|
private void injectAssociationChecker(MethodNode mn) {
|
||||||
MethodNode getInstanceMethod = new MethodNode(ACC_PUBLIC | ACC_STATIC, GET_TESTABLE_REF,
|
if (isGlobalScope(mn)) {
|
||||||
VOID_ARGS + ClassUtil.toByteCodeClassName(mockClassName), null, null);
|
return;
|
||||||
|
}
|
||||||
|
LabelNode firstLine = new LabelNode(new Label());
|
||||||
InsnList il = new InsnList();
|
InsnList il = new InsnList();
|
||||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_CONTEXT_UTIL, METHOD_IS_ASSOCIATED,
|
||||||
LabelNode label = new LabelNode();
|
SIGNATURE_IS_ASSOCIATED, false));
|
||||||
il.add(new JumpInsnNode(IFNONNULL, label));
|
il.add(new JumpInsnNode(IFNE, firstLine));
|
||||||
il.add(new TypeInsnNode(NEW, mockClassName));
|
il.add(firstLine);
|
||||||
il.add(new InsnNode(DUP));
|
il.add( new FrameNode(F_SAME, 0, null, 0, null));
|
||||||
il.add(new MethodInsnNode(INVOKESPECIAL, mockClassName, CONSTRUCTOR, VOID_ARGS + VOID_RES, false));
|
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
|
||||||
il.add(new FieldInsnNode(PUTSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
}
|
||||||
il.add(label);
|
|
||||||
il.add(new FrameNode(F_SAME, 0, null, 0, null));
|
private boolean isGlobalScope(MethodNode mn) {
|
||||||
il.add(new FieldInsnNode(GETSTATIC, mockClassName, TESTABLE_REF, ClassUtil.toByteCodeClassName(mockClassName)));
|
for (AnnotationNode an : mn.visibleAnnotations) {
|
||||||
il.add(new InsnNode(ARETURN));
|
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc) ||
|
||||||
getInstanceMethod.instructions = il;
|
ClassUtil.toByteCodeClassName(ConstPool.MOCK_CONSTRUCTOR).equals(an.desc)) {
|
||||||
getInstanceMethod.maxStack = 2;
|
MockScope scope = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_SCOPE,
|
||||||
getInstanceMethod.maxLocals = 0;
|
MockScope.ASSOCIATED, MockScope.class);
|
||||||
cn.methods.add(getInstanceMethod);
|
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) {
|
private boolean isMockMethod(MethodNode mn) {
|
||||||
@ -137,7 +180,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
|
|||||||
il.add(new InsnNode(ICONST_1));
|
il.add(new InsnNode(ICONST_1));
|
||||||
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_INVOKE_RECORD_UTIL, METHOD_RECORD_MOCK_INVOKE,
|
||||||
SIGNATURE_INVOKE_RECORDER_METHOD, false));
|
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) {
|
private boolean isMockForConstructor(MethodNode mn) {
|
||||||
|
@ -16,7 +16,6 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class TestClassHandler extends BaseClassWithContextHandler {
|
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 METHOD_INIT = "init";
|
||||||
private static final String DESC_METHOD_INIT = "()V";
|
private static final String DESC_METHOD_INIT = "()V";
|
||||||
private static final String METHOD_CLEAN = "clean";
|
private static final String METHOD_CLEAN = "clean";
|
||||||
|
Loading…
Reference in New Issue
Block a user