prepare mock association util

This commit is contained in:
金戟 2021-02-17 19:53:12 +08:00
parent 4c6fc5b228
commit 02c74299f4
7 changed files with 84 additions and 54 deletions

View File

@ -7,10 +7,9 @@ import org.objectweb.asm.tree.*;
*/ */
abstract public class BaseClassWithContextHandler extends BaseClassHandler { abstract public class BaseClassWithContextHandler extends BaseClassHandler {
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_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 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 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";

View File

@ -19,8 +19,13 @@ 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 CLASS_MOCK_ASSOCIATION_UTIL = "com/alibaba/testable/core/util/MockAssociationUtil";
private static final String METHOD_INVOKE_ORIGIN = "invokeOrigin";
private static final String SIGNATURE_INVOKE_ORIGIN =
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
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_RECORDER_METHOD_INVOKE = "([Ljava/lang/Object;ZZ)V";
private static final String METHOD_IS_ASSOCIATED = "isAssociated"; private static final String METHOD_IS_ASSOCIATED = "isAssociated";
private static final String SIGNATURE_IS_ASSOCIATED = "()Z"; private static final String SIGNATURE_IS_ASSOCIATED = "()Z";
@ -107,14 +112,20 @@ public class MockClassHandler extends BaseClassWithContextHandler {
} }
LabelNode firstLine = new LabelNode(new Label()); LabelNode firstLine = new LabelNode(new Label());
InsnList il = new InsnList(); InsnList il = new InsnList();
il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_CONTEXT_UTIL, METHOD_IS_ASSOCIATED, il.add(new MethodInsnNode(INVOKESTATIC, CLASS_MOCK_ASSOCIATION_UTIL, METHOD_IS_ASSOCIATED,
SIGNATURE_IS_ASSOCIATED, false)); SIGNATURE_IS_ASSOCIATED, false));
il.add(new JumpInsnNode(IFNE, firstLine)); il.add(new JumpInsnNode(IFNE, firstLine));
il.add(invokeOriginalMethod(mn));
il.add(firstLine); il.add(firstLine);
il.add( new FrameNode(F_SAME, 0, null, 0, null)); il.add( new FrameNode(F_SAME, 0, null, 0, null));
mn.instructions.insertBefore(mn.instructions.getFirst(), il); mn.instructions.insertBefore(mn.instructions.getFirst(), il);
} }
private InsnList invokeOriginalMethod(MethodNode mn) {
InsnList il = new InsnList();
return il;
}
private boolean isGlobalScope(MethodNode mn) { private boolean isGlobalScope(MethodNode mn) {
for (AnnotationNode an : mn.visibleAnnotations) { for (AnnotationNode an : mn.visibleAnnotations) {
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc) || if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_METHOD).equals(an.desc) ||
@ -179,7 +190,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_RECORDER_METHOD_INVOKE, false));
mn.instructions.insertBefore(mn.instructions.getFirst(), il); mn.instructions.insertBefore(mn.instructions.getFirst(), il);
} }

View File

@ -7,7 +7,7 @@ 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.agent.util.DiagnoseUtil; import com.alibaba.testable.agent.util.DiagnoseUtil;
import com.alibaba.testable.core.util.LogUtil; import com.alibaba.testable.core.util.LogUtil;
import com.alibaba.testable.core.util.MockContextUtil; import com.alibaba.testable.core.util.MockAssociationUtil;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -47,7 +47,7 @@ public class MockClassParser {
* @return found annotation or not * @return found annotation or not
*/ */
public boolean isMockClass(String className) { public boolean isMockClass(String className) {
return MockContextUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) || return MockAssociationUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) ||
hasMockMethod(className); hasMockMethod(className);
} }

View File

@ -8,7 +8,7 @@ import com.alibaba.testable.agent.model.MethodInfo;
import com.alibaba.testable.agent.util.*; import com.alibaba.testable.agent.util.*;
import com.alibaba.testable.core.model.ClassType; import com.alibaba.testable.core.model.ClassType;
import com.alibaba.testable.core.util.LogUtil; import com.alibaba.testable.core.util.LogUtil;
import com.alibaba.testable.core.util.MockContextUtil; import com.alibaba.testable.core.util.MockAssociationUtil;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -182,7 +182,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
// look for MockWith annotation // look for MockWith annotation
String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass); String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass);
if (mockClassName != null) { if (mockClassName != null) {
MockContextUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className)); MockAssociationUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
return ClassUtil.toSlashSeparatedName(mockClassName); return ClassUtil.toSlashSeparatedName(mockClassName);
} }
// look for Mock inner class // look for Mock inner class

View File

@ -0,0 +1,55 @@
package com.alibaba.testable.core.util;
import com.alibaba.testable.core.model.MockContext;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
public class MockAssociationUtil {
/**
* [0]Thread [1]MockAssociationUtil [2]MockClass
*/
public static final int INDEX_OF_MOCK_CLASS = 2;
/**
* Mock class referred by @MockWith annotation to list of its test classes
* MockClassName (dot-separated) Set of associated [TestClassNames (dot-separated)]
*/
public static Map<String, Set<String>> mockToTests = UnnullableMap.of(new HashSet<String>());
/**
* Check whether current mock method is invoked from its associated test class
* should be invoked in mock method
*/
public static boolean isAssociated() {
MockContext mockContext = MockContextUtil.context.get();
String testClassName = (mockContext == null) ? "" : mockContext.testClassName;
String mockClassName = Thread.currentThread().getStackTrace()[INDEX_OF_MOCK_CLASS].getClassName();
return isAssociatedByInnerMockClass(testClassName, mockClassName) ||
isAssociatedByOuterMockClass(testClassName, mockClassName) ||
isAssociatedByMockWithAnnotation(testClassName, mockClassName);
}
public static Object invokeOrigin(String originClass, String originMethod, Object originObj, Object... args) {
return null;
}
private static boolean isAssociatedByInnerMockClass(String testClassName, String mockClassName) {
return mockClassName.equals(String.format("%s$%s", testClassName, MOCK_POSTFIX));
}
private static boolean isAssociatedByOuterMockClass(String testClassName, String mockClassName) {
return testClassName.endsWith(TEST_POSTFIX) &&
mockClassName.equals(testClassName.substring(0, testClassName.length() - 4) + MOCK_POSTFIX);
}
private static boolean isAssociatedByMockWithAnnotation(String testClassName, String mockClassName) {
return mockToTests.get(mockClassName).contains(testClassName);
}
}

View File

@ -5,9 +5,6 @@ import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.*; import java.util.*;
import static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
public class MockContextUtil { public class MockContextUtil {
/** /**
@ -16,23 +13,17 @@ public class MockContextUtil {
public static InheritableThreadLocal<MockContext> context = new TransmittableThreadLocal<MockContext>(); public static InheritableThreadLocal<MockContext> context = new TransmittableThreadLocal<MockContext>();
/** /**
* Mock class referred by @MockWith annotation to list of its test classes * [0]Thread [1]MockContextUtil [2]TestClass
* MockClassName (dot-separated) Set of associated [TestClassNames (dot-separated)]
*/ */
public static Map<String, Set<String>> mockToTests = UnnullableMap.of(new HashSet<String>()); public static final int INDEX_OF_TEST_CLASS = 2;
/**
* [0]Thread [1]MockContextUtil [2]TestClass/MockClass
*/
public static final int INDEX_OF_INVOKER_CLASS = 2;
/** /**
* Initialize mock context * Initialize mock context
* should be invoked at the beginning of each test case method * should be invoked at the beginning of each test case method
*/ */
public static void init() { public static void init() {
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName(); String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getClassName();
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getMethodName(); String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getMethodName();
context.set(new MockContext(testClassName, testCaseName)); context.set(new MockContext(testClassName, testCaseName));
} }
@ -49,30 +40,4 @@ public class MockContextUtil {
return mockContext == null ? new HashMap<String, Object>() : mockContext.parameters; return mockContext == null ? new HashMap<String, Object>() : mockContext.parameters;
} }
/**
* Check whether current mock method is invoked from its associated test class
* should be invoked in mock method
*/
public static boolean isAssociated() {
MockContext mockContext = context.get();
String testClassName = (mockContext == null) ? "" : mockContext.testClassName;
String mockClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName();
return isAssociatedByInnerMockClass(testClassName, mockClassName) ||
isAssociatedByOuterMockClass(testClassName, mockClassName) ||
isAssociatedByMockWithAnnotation(testClassName, mockClassName);
}
private static boolean isAssociatedByInnerMockClass(String testClassName, String mockClassName) {
return mockClassName.equals(String.format("%s$%s", testClassName, MOCK_POSTFIX));
}
private static boolean isAssociatedByOuterMockClass(String testClassName, String mockClassName) {
return testClassName.endsWith(TEST_POSTFIX) &&
mockClassName.equals(testClassName.substring(0, testClassName.length() - 4) + MOCK_POSTFIX);
}
private static boolean isAssociatedByMockWithAnnotation(String testClassName, String mockClassName) {
return mockToTests.get(mockClassName).contains(testClassName);
}
} }

View File

@ -5,21 +5,21 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
class MockContextUtilTest { class MockAssociationUtilTest {
@Test @Test
void should_able_to_associate_by_inner_mock_class() { void should_able_to_associate_by_inner_mock_class() {
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass", assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTest$Mock")); "com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTest$Mock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass", assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTestMock")); "com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTestMock"));
} }
@Test @Test
void should_able_to_associate_by_outer_mock_class() { void should_able_to_associate_by_outer_mock_class() {
assertTrue((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass", assertTrue((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoMock")); "com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoMock"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass", assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
"com.alibaba.testable.DemoTester", "com.alibaba.testable.DemoMock")); "com.alibaba.testable.DemoTester", "com.alibaba.testable.DemoMock"));
} }