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 78c6847..160cd1e 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 @@ -94,7 +94,7 @@ public class MockClassHandler extends BaseClassWithContextHandler { // must get label before method description changed ImmutablePair labels = getStartAndEndLabel(mn); mn.desc = MethodUtil.addParameterAtBegin(mn.desc, targetClassName); - int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1; + int parameterOffset = MethodUtil.isStatic(mn) ? 0 : 1; mn.localVariables.add(parameterOffset, new LocalVariableNode("__self", targetClassName, null, labels.left, labels.right, parameterOffset)); for (int i = parameterOffset + 1; i < mn.localVariables.size(); i++) { @@ -114,7 +114,7 @@ public class MockClassHandler extends BaseClassWithContextHandler { } private ImmutablePair getStartAndEndLabel(MethodNode mn) { - if (MethodUtil.isStaticMethod(mn)) { + if (MethodUtil.isStatic(mn)) { LabelNode startLabel = null, endLabel = null; for (AbstractInsnNode n = mn.instructions.getFirst(); n != null; n = n.getNext()) { if (n instanceof LabelNode) { @@ -251,7 +251,7 @@ public class MockClassHandler extends BaseClassWithContextHandler { int size = types.size(); il.add(getIntInsn(size)); il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT)); - int parameterOffset = MethodUtil.isStaticMethod(mn) ? 0 : 1; + int parameterOffset = MethodUtil.isStatic(mn) ? 0 : 1; for (int i = 0; i < size; i++) { il.add(new InsnNode(DUP)); il.add(getIntInsn(i)); diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java index 83be580..79c4c69 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/TestClassHandler.java @@ -22,6 +22,8 @@ public class TestClassHandler extends BaseClassWithContextHandler { private static final String DESC_METHOD_CLEAN = "()V"; private static final String THIS = "this"; + private int testCaseCount = 0; + private final Framework[] frameworkClasses = new Framework[] { new JUnit4Framework(), new JUnit5Framework(), @@ -51,6 +53,7 @@ public class TestClassHandler extends BaseClassWithContextHandler { handleTestableUtil(mn); handleTestCaseMethod(mn, framework); } + LogUtil.diagnose(String.format(" Found %d test cases", testCaseCount)); } private Framework checkFramework(ClassNode cn) { @@ -96,7 +99,9 @@ public class TestClassHandler extends BaseClassWithContextHandler { private void handleTestCaseMethod(MethodNode mn, Framework framework) { TestCaseMethodType type = framework.checkMethodType(mn); if (type.equals(TestCaseMethodType.TEST)) { + LogUtil.verbose(String.format(" Test case \"%s\"", mn.name)); injectMockContextInit(mn); + testCaseCount++; } else if (type.equals(TestCaseMethodType.AFTER_TEST)) { injectMockContextClean(mn); } 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 80c06fc..f3385c9 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 @@ -8,7 +8,6 @@ import com.alibaba.testable.agent.util.ClassUtil; import com.alibaba.testable.agent.util.DiagnoseUtil; import com.alibaba.testable.agent.util.MethodUtil; import com.alibaba.testable.core.util.LogUtil; -import com.alibaba.testable.core.util.MockAssociationUtil; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; @@ -18,7 +17,7 @@ import java.util.ArrayList; import java.util.List; import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName; -import static com.alibaba.testable.agent.util.MethodUtil.isStaticMethod; +import static com.alibaba.testable.agent.util.MethodUtil.isStatic; import static com.alibaba.testable.core.constant.ConstPool.CONSTRUCTOR; public class MockClassParser { @@ -50,11 +49,6 @@ public class MockClassParser { * @return found annotation or not */ public boolean isMockClass(String className) { - return MockAssociationUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) || - hasMockMethod(className); - } - - private boolean hasMockMethod(String className) { ClassNode cn = ClassUtil.getClassNode(className); if (cn == null) { return false; @@ -120,7 +114,7 @@ public class MockClassParser { private MethodInfo getMethodInfo(MethodNode mn, AnnotationNode an, String targetMethod) { Type targetType = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class); - boolean isStatic = isStaticMethod(mn); + boolean isStatic = isStatic(mn); if (targetType == null) { // "targetClass" unset, use first parameter as target class type ImmutablePair methodDescPair = extractFirstParameter(mn.desc); @@ -138,7 +132,7 @@ public class MockClassParser { private void addMockConstructor(List methodInfos, ClassNode cn, MethodNode mn) { String sourceClassName = ClassUtil.getSourceClassName(cn.name); - methodInfos.add(new MethodInfo(sourceClassName, CONSTRUCTOR, mn.desc, mn.name, mn.desc, isStaticMethod(mn))); + methodInfos.add(new MethodInfo(sourceClassName, CONSTRUCTOR, mn.desc, mn.name, mn.desc, isStatic(mn))); } /** 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 49a0ce0..81fa0d0 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 @@ -106,7 +106,7 @@ public class TestableClassTransformer implements ClassFileTransformer { } private String foundMockForSourceClass(String className) { - String mockClass = readMockWithAnnotationAsSourceClass(className); + String mockClass = lookForMockWithAnnotationAsSourceClass(className); if (mockClass != null) { return mockClass; } @@ -114,10 +114,23 @@ public class TestableClassTransformer implements ClassFileTransformer { } private String foundMockForTestClass(String className) { - String mockClass = readMockWithAnnotationAndInnerClassAsTestClass(className); + ClassNode cn = ClassUtil.getClassNode(className); + if (cn == null) { + return null; + } + String mockClass = lookForMockWithAnnotationAsTestClass(cn); if (mockClass != null) { return mockClass; } + mockClass = lookForInnerMockClass(cn); + if (mockClass != null) { + return mockClass; + } + return lookForOuterMockClass(className); + } + + private String lookForOuterMockClass(String className) { + String mockClass; mockClass = ClassUtil.getMockClassName(ClassUtil.getSourceClassName(className)); if (mockClassParser.isMockClass(mockClass)) { return mockClass; @@ -160,7 +173,7 @@ public class TestableClassTransformer implements ClassFileTransformer { * @param className class that need to explore * @return name of mock class, null for not found */ - private String readMockWithAnnotationAsSourceClass(String className) { + private String lookForMockWithAnnotationAsSourceClass(String className) { ClassNode cn = ClassUtil.getClassNode(className); if (cn == null) { return null; @@ -169,35 +182,40 @@ public class TestableClassTransformer implements ClassFileTransformer { } /** - * Read @MockWith annotation upon class and inner class "Mock" to fetch mock class + * Read inner class "Mock" to fetch mock class * - * @param className class that need to explore + * @param cn class that need to explore * @return name of mock class, null for not found */ - private String readMockWithAnnotationAndInnerClassAsTestClass(String className) { - ClassNode cn = ClassUtil.getClassNode(className); - if (cn == null) { - return null; - } - // look for MockWith annotation - String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass); - if (mockClassName != null) { - MockAssociationUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(className)); - return ClassUtil.toSlashSeparatedName(mockClassName); - } - // look for Mock inner class + private String lookForInnerMockClass(ClassNode cn) { for (InnerClassNode ic : cn.innerClasses) { - if ((ic.access & ACC_PUBLIC) != 0 && ic.name.equals(getInnerMockClassName(className))) { + if ((ic.access & ACC_PUBLIC) != 0 && ic.name.equals(getInnerMockClassName(cn.name)) && + mockClassParser.isMockClass(ic.name)) { if ((ic.access & ACC_STATIC) != 0) { return ic.name; } else { - LogUtil.warn(String.format("Mock class in \"%s\" is not static", className)); + LogUtil.warn(String.format("Mock class in \"%s\" is not declared as static", cn.name)); } } } return null; } + /** + * Read @MockWith annotation upon class to fetch mock class + * + * @param cn class that need to explore + * @return name of mock class, null for not found + */ + private String lookForMockWithAnnotationAsTestClass(ClassNode cn) { + String mockClassName = parseMockWithAnnotation(cn, ClassType.TestClass); + if (mockClassName != null) { + MockAssociationUtil.mockToTests.get(mockClassName).add(ClassUtil.toDotSeparateFullClassName(cn.name)); + return ClassUtil.toSlashSeparatedName(mockClassName); + } + return null; + } + /** * Get mock class from @MockWith annotation * diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/util/MethodUtil.java b/testable-agent/src/main/java/com/alibaba/testable/agent/util/MethodUtil.java index 0dfd46d..d470bed 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/util/MethodUtil.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/util/MethodUtil.java @@ -15,7 +15,7 @@ public class MethodUtil { * @param mn method to check * @return is static or not */ - public static boolean isStaticMethod(MethodNode mn) { + public static boolean isStatic(MethodNode mn) { return (mn.access & ACC_STATIC) != 0; } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/annotation/MockWith.java b/testable-core/src/main/java/com/alibaba/testable/core/annotation/MockWith.java index 4bd719b..29e53c8 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/annotation/MockWith.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/annotation/MockWith.java @@ -18,6 +18,7 @@ public @interface MockWith { /** * explicitly specify mock class + * @note this parameter will become mandatory in v0.6 * @return type of mock class */ Class value() default NullType.class; diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java index f671d86..09fcea3 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java @@ -46,7 +46,7 @@ public class LogUtil { public static void warn(String msg, Object... args) { if (currentLogLevel.level >= LogLevel.LEVEL_WARN.level) { - System.out.println(String.format("[WARN] " + msg, args)); + System.err.println(String.format("[WARN] " + msg, args)); } }