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 {
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 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_MOCK_CONTEXT = "MOCK_CONTEXT";
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 {
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 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 SIGNATURE_IS_ASSOCIATED = "()Z";
@ -107,14 +112,20 @@ public class MockClassHandler extends BaseClassWithContextHandler {
}
LabelNode firstLine = new LabelNode(new Label());
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));
il.add(new JumpInsnNode(IFNE, firstLine));
il.add(invokeOriginalMethod(mn));
il.add(firstLine);
il.add( new FrameNode(F_SAME, 0, null, 0, null));
mn.instructions.insertBefore(mn.instructions.getFirst(), il);
}
private InsnList invokeOriginalMethod(MethodNode mn) {
InsnList il = new InsnList();
return il;
}
private boolean isGlobalScope(MethodNode mn) {
for (AnnotationNode an : mn.visibleAnnotations) {
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 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);
}

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.DiagnoseUtil;
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.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
@ -47,7 +47,7 @@ public class MockClassParser {
* @return found annotation or not
*/
public boolean isMockClass(String className) {
return MockContextUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) ||
return MockAssociationUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(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.core.model.ClassType;
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.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
@ -182,7 +182,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
// look for MockWith annotation
String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass);
if (mockClassName != null) {
MockContextUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
MockAssociationUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className));
return ClassUtil.toSlashSeparatedName(mockClassName);
}
// 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 static com.alibaba.testable.core.constant.ConstPool.MOCK_POSTFIX;
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
public class MockContextUtil {
/**
@ -16,23 +13,17 @@ public class MockContextUtil {
public static InheritableThreadLocal<MockContext> context = new TransmittableThreadLocal<MockContext>();
/**
* Mock class referred by @MockWith annotation to list of its test classes
* MockClassName (dot-separated) Set of associated [TestClassNames (dot-separated)]
* [0]Thread [1]MockContextUtil [2]TestClass
*/
public static Map<String, Set<String>> mockToTests = UnnullableMap.of(new HashSet<String>());
/**
* [0]Thread [1]MockContextUtil [2]TestClass/MockClass
*/
public static final int INDEX_OF_INVOKER_CLASS = 2;
public static final int INDEX_OF_TEST_CLASS = 2;
/**
* Initialize mock context
* should be invoked at the beginning of each test case method
*/
public static void init() {
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getClassName();
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_INVOKER_CLASS].getMethodName();
String testClassName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getClassName();
String testCaseName = Thread.currentThread().getStackTrace()[INDEX_OF_TEST_CLASS].getMethodName();
context.set(new MockContext(testClassName, testCaseName));
}
@ -49,30 +40,4 @@ public class MockContextUtil {
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.*;
class MockContextUtilTest {
class MockAssociationUtilTest {
@Test
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"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByInnerMockClass",
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByInnerMockClass",
"com.alibaba.testable.DemoTest", "com.alibaba.testable.DemoTestMock"));
}
@Test
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"));
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockContextUtil.class, "isAssociatedByOuterMockClass",
assertFalse((Boolean)PrivateAccessor.invokeStatic(MockAssociationUtil.class, "isAssociatedByOuterMockClass",
"com.alibaba.testable.DemoTester", "com.alibaba.testable.DemoMock"));
}