suggest use MockDiagnose to set log level

This commit is contained in:
金戟 2021-02-16 10:47:18 +08:00
parent 9eb8682ec0
commit be53ea2d9c
5 changed files with 63 additions and 15 deletions

View File

@ -17,6 +17,7 @@ public class ConstPool {
public static final String FIELD_TARGET_CLASS = "targetClass"; public static final String FIELD_TARGET_CLASS = "targetClass";
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith"; public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
public static final String MOCK_DIAGNOSE = "com.alibaba.testable.core.annotation.MockDiagnose";
public static final String MOCK_METHOD = "com.alibaba.testable.core.annotation.MockMethod"; public static final String MOCK_METHOD = "com.alibaba.testable.core.annotation.MockMethod";
public static final String MOCK_CONSTRUCTOR = "com.alibaba.testable.core.annotation.MockConstructor"; public static final String MOCK_CONSTRUCTOR = "com.alibaba.testable.core.annotation.MockConstructor";

View File

@ -11,7 +11,7 @@ import com.alibaba.testable.agent.util.ClassUtil;
import com.alibaba.testable.agent.util.GlobalConfig; import com.alibaba.testable.agent.util.GlobalConfig;
import com.alibaba.testable.agent.util.StringUtil; import com.alibaba.testable.agent.util.StringUtil;
import com.alibaba.testable.core.model.ClassType; import com.alibaba.testable.core.model.ClassType;
import com.alibaba.testable.core.model.MockDiagnose; import com.alibaba.testable.core.model.LogLevel;
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.MockContextUtil;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
@ -48,12 +48,11 @@ public class TestableClassTransformer implements ClassFileTransformer {
private static final String CLASS_OBJECT = "java/lang/Object"; private static final String CLASS_OBJECT = "java/lang/Object";
/** /**
* Just avoid spend time to scan those surely non-user classes * Just avoid spend time to scan those surely non-user classes Should keep these lists as tiny as possible
* Should keep these lists as tiny as possible
*/ */
private final String[] WHITELIST_PREFIXES = new String[] { "com/alibaba/testable/demo/" }; private final String[] WHITELIST_PREFIXES = new String[] {"com/alibaba/testable/demo/"};
private final String[] BLACKLIST_PREFIXES = new String[] { "jdk/", "java/", "javax/", "com/sun/", private final String[] BLACKLIST_PREFIXES = new String[] {"jdk/", "java/", "javax/", "com/sun/",
"org/apache/maven/", "com/alibaba/testable/", "junit/", "org/junit/", "org/testng/" }; "org/apache/maven/", "com/alibaba/testable/", "junit/", "org/junit/", "org/testng/"};
@Override @Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
@ -103,7 +102,8 @@ public class TestableClassTransformer implements ClassFileTransformer {
return; return;
} }
try { try {
String dumpFile = StringUtil.joinPath(dumpDir, className.replace(SLASH, DOT).replace(DOLLAR, UNDERLINE) + ".class"); String dumpFile = StringUtil.joinPath(dumpDir,
className.replace(SLASH, DOT).replace(DOLLAR, UNDERLINE) + ".class");
LogUtil.verbose("Dump class: " + dumpFile); LogUtil.verbose("Dump class: " + dumpFile);
FileOutputStream stream = new FileOutputStream(dumpFile); FileOutputStream stream = new FileOutputStream(dumpFile);
stream.write(bytes); stream.write(bytes);
@ -241,6 +241,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
/** /**
* Read @MockWith annotation upon class to fetch mock class * Read @MockWith annotation upon class to fetch mock class
*
* @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
*/ */
@ -254,6 +255,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
/** /**
* Read @MockWith annotation upon class and inner class "Mock" to fetch mock class * Read @MockWith annotation upon class and inner class "Mock" to fetch mock class
*
* @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
*/ */
@ -283,14 +285,15 @@ public class TestableClassTransformer implements ClassFileTransformer {
/** /**
* Get mock class from @MockWith annotation * Get mock class from @MockWith annotation
*
* @param cn class that may have @MockWith annotation * @param cn class that may have @MockWith annotation
* @return mock class name * @return mock class name
*/ */
private String parseMockWithAnnotation(ClassNode cn, ClassType expectedType) { private String parseMockWithAnnotation(ClassNode cn, ClassType expectedType) {
if (cn.visibleAnnotations != null) { if (cn.visibleAnnotations != null) {
for (AnnotationNode an : cn.visibleAnnotations) { for (AnnotationNode an : cn.visibleAnnotations) {
setupDiagnose(an);
if (toDotSeparateFullClassName(an.desc).equals(ConstPool.MOCK_WITH)) { if (toDotSeparateFullClassName(an.desc).equals(ConstPool.MOCK_WITH)) {
setupDiagnose(an);
ClassType type = AnnotationUtil.getAnnotationParameter(an, FIELD_TREAT_AS, ClassType.GuessByName, ClassType type = AnnotationUtil.getAnnotationParameter(an, FIELD_TREAT_AS, ClassType.GuessByName,
ClassType.class); ClassType.class);
if (isExpectedType(cn.name, type, expectedType)) { if (isExpectedType(cn.name, type, expectedType)) {
@ -316,6 +319,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
/** /**
* Check whether any method in specified class has mock-related annotation * Check whether any method in specified class has mock-related annotation
*
* @param className class that need to explore * @param className class that need to explore
* @return found annotation or not * @return found annotation or not
*/ */
@ -324,6 +328,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
if (cn == null) { if (cn == null) {
return false; return false;
} }
setupDiagnose(cn);
for (MethodNode mn : cn.methods) { for (MethodNode mn : cn.methods) {
if (mn.visibleAnnotations != null) { if (mn.visibleAnnotations != null) {
for (AnnotationNode an : mn.visibleAnnotations) { for (AnnotationNode an : mn.visibleAnnotations) {
@ -352,11 +357,29 @@ public class TestableClassTransformer implements ClassFileTransformer {
return cn; return cn;
} }
private void setupDiagnose(ClassNode cn) {
if (cn.visibleAnnotations == null) {
return;
}
for (AnnotationNode an : cn.visibleAnnotations) {
setupDiagnose(an);
}
}
private void setupDiagnose(AnnotationNode an) { private void setupDiagnose(AnnotationNode an) {
MockDiagnose diagnose = AnnotationUtil.getAnnotationParameter(an, FIELD_DIAGNOSE, null, MockDiagnose.class); if (toDotSeparateFullClassName(an.desc).equals(MOCK_WITH)) {
if (diagnose != null) { setupDianose(an, FIELD_DIAGNOSE);
LogUtil.setLevel(diagnose == MockDiagnose.ENABLE ? LogUtil.LogLevel.LEVEL_DIAGNOSE : }
(diagnose == MockDiagnose.VERBOSE ? LogUtil.LogLevel.LEVEL_VERBOSE : LogUtil.LogLevel.LEVEL_MUTE)); if (toDotSeparateFullClassName(an.desc).equals(ConstPool.MOCK_DIAGNOSE)) {
setupDianose(an, FIELD_VALUE);
}
}
private void setupDianose(AnnotationNode an, String fieldDiagnose) {
LogLevel level = AnnotationUtil.getAnnotationParameter(an, fieldDiagnose, null, LogLevel.class);
if (level != null) {
LogUtil.setLevel(level == LogLevel.ENABLE ? LogUtil.LogLevel.LEVEL_DIAGNOSE :
(level == LogLevel.VERBOSE ? LogUtil.LogLevel.LEVEL_VERBOSE : LogUtil.LogLevel.LEVEL_MUTE));
} }
} }

View File

@ -0,0 +1,23 @@
package com.alibaba.testable.core.annotation;
import com.alibaba.testable.core.model.LogLevel;
import java.lang.annotation.*;
/**
* Set extra mock parameter to test class
*
* @author flin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MockDiagnose {
/**
* switch of mock diagnose information of current test class
* @return enable or disable
*/
LogLevel value() default LogLevel.DISABLE;
}

View File

@ -1,7 +1,7 @@
package com.alibaba.testable.core.annotation; package com.alibaba.testable.core.annotation;
import com.alibaba.testable.core.model.ClassType; import com.alibaba.testable.core.model.ClassType;
import com.alibaba.testable.core.model.MockDiagnose; import com.alibaba.testable.core.model.LogLevel;
import javax.lang.model.type.NullType; import javax.lang.model.type.NullType;
import java.lang.annotation.*; import java.lang.annotation.*;
@ -30,8 +30,9 @@ public @interface MockWith {
/** /**
* switch of mock diagnose information of current test class * switch of mock diagnose information of current test class
* @deprecated to be removed in v0.6, use @MockDiagnose annotation instead
* @return enable or disable * @return enable or disable
*/ */
MockDiagnose diagnose() default MockDiagnose.DISABLE; LogLevel diagnose() default LogLevel.DISABLE;
} }

View File

@ -4,7 +4,7 @@ package com.alibaba.testable.core.model;
* @author flin * @author flin
*/ */
public enum MockDiagnose { public enum LogLevel {
/** /**
* Be quiet * Be quiet