diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/BaseClassWithContextHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/BaseClassWithContextHandler.java index f17ee54..52840d9 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/BaseClassWithContextHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/BaseClassWithContextHandler.java @@ -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"; diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java index b568f8c..5a00656 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java @@ -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); } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java index 9fe0935..d63eed7 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java @@ -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); } diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java index 0914513..f19d16c 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/TestableClassTransformer.java @@ -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 diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/MockAssociationUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/MockAssociationUtil.java new file mode 100644 index 0000000..33cce63 --- /dev/null +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/MockAssociationUtil.java @@ -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> mockToTests = UnnullableMap.of(new HashSet()); + + /** + * 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); + } + +} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/MockContextUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/MockContextUtil.java index 27c511b..a411e75 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/MockContextUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/MockContextUtil.java @@ -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 context = new TransmittableThreadLocal(); /** - * 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> mockToTests = UnnullableMap.of(new HashSet()); - - /** - * [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() : 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); - } - } diff --git a/testable-core/src/test/java/com/alibaba/testable/core/util/MockContextUtilTest.java b/testable-core/src/test/java/com/alibaba/testable/core/util/MockAssociationUtilTest.java similarity index 57% rename from testable-core/src/test/java/com/alibaba/testable/core/util/MockContextUtilTest.java rename to testable-core/src/test/java/com/alibaba/testable/core/util/MockAssociationUtilTest.java index c44e510..99d7367 100644 --- a/testable-core/src/test/java/com/alibaba/testable/core/util/MockContextUtilTest.java +++ b/testable-core/src/test/java/com/alibaba/testable/core/util/MockAssociationUtilTest.java @@ -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")); }