use mock class to setup diagnose

This commit is contained in:
金戟 2021-02-19 13:43:55 +08:00
parent 65cc631d39
commit 82d87f3447
7 changed files with 51 additions and 33 deletions

View File

@ -94,7 +94,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
// must get label before method description changed // must get label before method description changed
ImmutablePair<LabelNode, LabelNode> labels = getStartAndEndLabel(mn); ImmutablePair<LabelNode, LabelNode> labels = getStartAndEndLabel(mn);
mn.desc = MethodUtil.addParameterAtBegin(mn.desc, targetClassName); 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, mn.localVariables.add(parameterOffset, new LocalVariableNode("__self", targetClassName, null,
labels.left, labels.right, parameterOffset)); labels.left, labels.right, parameterOffset));
for (int i = parameterOffset + 1; i < mn.localVariables.size(); i++) { for (int i = parameterOffset + 1; i < mn.localVariables.size(); i++) {
@ -114,7 +114,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
} }
private ImmutablePair<LabelNode, LabelNode> getStartAndEndLabel(MethodNode mn) { private ImmutablePair<LabelNode, LabelNode> getStartAndEndLabel(MethodNode mn) {
if (MethodUtil.isStaticMethod(mn)) { if (MethodUtil.isStatic(mn)) {
LabelNode startLabel = null, endLabel = null; LabelNode startLabel = null, endLabel = null;
for (AbstractInsnNode n = mn.instructions.getFirst(); n != null; n = n.getNext()) { for (AbstractInsnNode n = mn.instructions.getFirst(); n != null; n = n.getNext()) {
if (n instanceof LabelNode) { if (n instanceof LabelNode) {
@ -251,7 +251,7 @@ public class MockClassHandler extends BaseClassWithContextHandler {
int size = types.size(); int size = types.size();
il.add(getIntInsn(size)); il.add(getIntInsn(size));
il.add(new TypeInsnNode(ANEWARRAY, ClassUtil.CLASS_OBJECT)); 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++) { for (int i = 0; i < size; i++) {
il.add(new InsnNode(DUP)); il.add(new InsnNode(DUP));
il.add(getIntInsn(i)); il.add(getIntInsn(i));

View File

@ -22,6 +22,8 @@ public class TestClassHandler extends BaseClassWithContextHandler {
private static final String DESC_METHOD_CLEAN = "()V"; private static final String DESC_METHOD_CLEAN = "()V";
private static final String THIS = "this"; private static final String THIS = "this";
private int testCaseCount = 0;
private final Framework[] frameworkClasses = new Framework[] { private final Framework[] frameworkClasses = new Framework[] {
new JUnit4Framework(), new JUnit4Framework(),
new JUnit5Framework(), new JUnit5Framework(),
@ -51,6 +53,7 @@ public class TestClassHandler extends BaseClassWithContextHandler {
handleTestableUtil(mn); handleTestableUtil(mn);
handleTestCaseMethod(mn, framework); handleTestCaseMethod(mn, framework);
} }
LogUtil.diagnose(String.format(" Found %d test cases", testCaseCount));
} }
private Framework checkFramework(ClassNode cn) { private Framework checkFramework(ClassNode cn) {
@ -96,7 +99,9 @@ public class TestClassHandler extends BaseClassWithContextHandler {
private void handleTestCaseMethod(MethodNode mn, Framework framework) { private void handleTestCaseMethod(MethodNode mn, Framework framework) {
TestCaseMethodType type = framework.checkMethodType(mn); TestCaseMethodType type = framework.checkMethodType(mn);
if (type.equals(TestCaseMethodType.TEST)) { if (type.equals(TestCaseMethodType.TEST)) {
LogUtil.verbose(String.format(" Test case \"%s\"", mn.name));
injectMockContextInit(mn); injectMockContextInit(mn);
testCaseCount++;
} else if (type.equals(TestCaseMethodType.AFTER_TEST)) { } else if (type.equals(TestCaseMethodType.AFTER_TEST)) {
injectMockContextClean(mn); injectMockContextClean(mn);
} }

View File

@ -8,7 +8,6 @@ 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.agent.util.MethodUtil; import com.alibaba.testable.agent.util.MethodUtil;
import com.alibaba.testable.core.util.LogUtil; import com.alibaba.testable.core.util.LogUtil;
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;
@ -18,7 +17,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.alibaba.testable.agent.util.ClassUtil.toDotSeparateFullClassName; 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; import static com.alibaba.testable.core.constant.ConstPool.CONSTRUCTOR;
public class MockClassParser { public class MockClassParser {
@ -50,11 +49,6 @@ public class MockClassParser {
* @return found annotation or not * @return found annotation or not
*/ */
public boolean isMockClass(String className) { public boolean isMockClass(String className) {
return MockAssociationUtil.mockToTests.containsKey(ClassUtil.toDotSeparatedName(className)) ||
hasMockMethod(className);
}
private boolean hasMockMethod(String className) {
ClassNode cn = ClassUtil.getClassNode(className); ClassNode cn = ClassUtil.getClassNode(className);
if (cn == null) { if (cn == null) {
return false; return false;
@ -120,7 +114,7 @@ public class MockClassParser {
private MethodInfo getMethodInfo(MethodNode mn, AnnotationNode an, String targetMethod) { private MethodInfo getMethodInfo(MethodNode mn, AnnotationNode an, String targetMethod) {
Type targetType = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class); Type targetType = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class);
boolean isStatic = isStaticMethod(mn); boolean isStatic = isStatic(mn);
if (targetType == null) { if (targetType == null) {
// "targetClass" unset, use first parameter as target class type // "targetClass" unset, use first parameter as target class type
ImmutablePair<String, String> methodDescPair = extractFirstParameter(mn.desc); ImmutablePair<String, String> methodDescPair = extractFirstParameter(mn.desc);
@ -138,7 +132,7 @@ public class MockClassParser {
private void addMockConstructor(List<MethodInfo> methodInfos, ClassNode cn, MethodNode mn) { private void addMockConstructor(List<MethodInfo> methodInfos, ClassNode cn, MethodNode mn) {
String sourceClassName = ClassUtil.getSourceClassName(cn.name); 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)));
} }
/** /**

View File

@ -106,7 +106,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
} }
private String foundMockForSourceClass(String className) { private String foundMockForSourceClass(String className) {
String mockClass = readMockWithAnnotationAsSourceClass(className); String mockClass = lookForMockWithAnnotationAsSourceClass(className);
if (mockClass != null) { if (mockClass != null) {
return mockClass; return mockClass;
} }
@ -114,10 +114,23 @@ public class TestableClassTransformer implements ClassFileTransformer {
} }
private String foundMockForTestClass(String className) { 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) { if (mockClass != null) {
return mockClass; 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)); mockClass = ClassUtil.getMockClassName(ClassUtil.getSourceClassName(className));
if (mockClassParser.isMockClass(mockClass)) { if (mockClassParser.isMockClass(mockClass)) {
return mockClass; return mockClass;
@ -160,7 +173,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
* @param className class that need to explore * @param className class that need to explore
* @return name of mock class, null for not found * @return name of mock class, null for not found
*/ */
private String readMockWithAnnotationAsSourceClass(String className) { private String lookForMockWithAnnotationAsSourceClass(String className) {
ClassNode cn = ClassUtil.getClassNode(className); ClassNode cn = ClassUtil.getClassNode(className);
if (cn == null) { if (cn == null) {
return 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 * @return name of mock class, null for not found
*/ */
private String readMockWithAnnotationAndInnerClassAsTestClass(String className) { private String lookForInnerMockClass(ClassNode cn) {
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
for (InnerClassNode ic : cn.innerClasses) { 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) { if ((ic.access & ACC_STATIC) != 0) {
return ic.name; return ic.name;
} else { } 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; 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 * Get mock class from @MockWith annotation
* *

View File

@ -15,7 +15,7 @@ public class MethodUtil {
* @param mn method to check * @param mn method to check
* @return is static or not * @return is static or not
*/ */
public static boolean isStaticMethod(MethodNode mn) { public static boolean isStatic(MethodNode mn) {
return (mn.access & ACC_STATIC) != 0; return (mn.access & ACC_STATIC) != 0;
} }

View File

@ -18,6 +18,7 @@ public @interface MockWith {
/** /**
* explicitly specify mock class * explicitly specify mock class
* @note this parameter will become mandatory in v0.6
* @return type of mock class * @return type of mock class
*/ */
Class<?> value() default NullType.class; Class<?> value() default NullType.class;

View File

@ -46,7 +46,7 @@ public class LogUtil {
public static void warn(String msg, Object... args) { public static void warn(String msg, Object... args) {
if (currentLogLevel.level >= LogLevel.LEVEL_WARN.level) { if (currentLogLevel.level >= LogLevel.LEVEL_WARN.level) {
System.out.println(String.format("[WARN] " + msg, args)); System.err.println(String.format("[WARN] " + msg, args));
} }
} }