mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-02-13 21:31:08 +08:00
support set dump path for single class
This commit is contained in:
parent
55ccc385d5
commit
26d3178840
@ -47,7 +47,6 @@
|
||||
| --- | --- | --- | ---- | --- |
|
||||
| N/A | Class | 否 | NullType.class | 指定使用的Mock容器类 |
|
||||
| treatAs | ClassType | 否 | ClassType.GuessByName | 指定当前类是测试类或被测类 |
|
||||
| diagnose | LogLevel | 否 | N/A | (**deprecated**)指定Mock诊断日志级别 |
|
||||
|
||||
#### @MockDiagnose
|
||||
|
||||
|
@ -10,6 +10,7 @@ public class ConstPool {
|
||||
public static final String FIELD_SCOPE = "scope";
|
||||
|
||||
public static final String MOCK_WITH = "com.alibaba.testable.core.annotation.MockWith";
|
||||
public static final String DUMP_TO = "com.alibaba.testable.core.annotation.DumpTo";
|
||||
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_CONSTRUCTOR = "com.alibaba.testable.core.annotation.MockConstructor";
|
||||
|
@ -45,11 +45,10 @@ public class MockClassParser {
|
||||
/**
|
||||
* Check whether any method in specified class has mock-related annotation
|
||||
*
|
||||
* @param className class that need to explore
|
||||
* @param cn class that need to explore
|
||||
* @return found annotation or not
|
||||
*/
|
||||
public boolean isMockClass(String className) {
|
||||
ClassNode cn = ClassUtil.getClassNode(className);
|
||||
public boolean isMockClass(ClassNode cn) {
|
||||
if (cn == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InnerClassNode;
|
||||
|
||||
import javax.lang.model.type.NullType;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
@ -36,6 +35,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
|
||||
private static final String FIELD_VALUE = "value";
|
||||
private static final String FIELD_TREAT_AS = "treatAs";
|
||||
private static final String FIELD_PATH = "path";
|
||||
private static final String COMMA = ",";
|
||||
private static final String CLASS_NAME_MOCK = "Mock";
|
||||
|
||||
@ -55,19 +55,28 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
}
|
||||
LogUtil.verbose("Handle class: " + className);
|
||||
byte[] bytes = new OmniClassHandler().getBytes(classFileBuffer);
|
||||
ClassNode cn = ClassUtil.getClassNode(className);
|
||||
if (cn != null) {
|
||||
return transformMock(bytes, cn);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte[] transformMock(byte[] bytes, ClassNode cn) {
|
||||
String className = cn.name;
|
||||
try {
|
||||
if (mockClassParser.isMockClass(className)) {
|
||||
if (mockClassParser.isMockClass(cn)) {
|
||||
// it's a mock class
|
||||
LogUtil.diagnose("Handling mock class %s", className);
|
||||
bytes = new MockClassHandler(className).getBytes(bytes);
|
||||
dumpByte(className, bytes);
|
||||
dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
} else {
|
||||
String mockClass = foundMockForTestClass(className);
|
||||
if (mockClass != null) {
|
||||
// it's a test class with testable enabled
|
||||
LogUtil.diagnose("Handling test class %s", className);
|
||||
bytes = new TestClassHandler().getBytes(bytes);
|
||||
dumpByte(className, bytes);
|
||||
dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
} else {
|
||||
mockClass = foundMockForSourceClass(className);
|
||||
if (mockClass != null) {
|
||||
@ -75,7 +84,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
|
||||
LogUtil.diagnose("Handling source class %s", className);
|
||||
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
|
||||
dumpByte(className, bytes);
|
||||
dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,16 +95,16 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
} finally {
|
||||
LogUtil.resetLogLevel();
|
||||
}
|
||||
dumpByte(className, getDumpPathByAnnotation(cn), bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private void dumpByte(String className, byte[] bytes) {
|
||||
String dumpDir = GlobalConfig.getDumpPath();
|
||||
if (dumpDir == null || dumpDir.isEmpty() || !new File(dumpDir).isDirectory()) {
|
||||
private void dumpByte(String className, String dumpPath, byte[] bytes) {
|
||||
if (dumpPath == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String dumpFile = StringUtil.joinPath(dumpDir,
|
||||
String dumpFile = StringUtil.joinPath(dumpPath,
|
||||
className.replace(SLASH, DOT).replace(DOLLAR, UNDERLINE) + ".class");
|
||||
LogUtil.verbose("Dump class: " + dumpFile);
|
||||
FileOutputStream stream = new FileOutputStream(dumpFile);
|
||||
@ -139,10 +148,9 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
}
|
||||
|
||||
private String lookForOuterMockClass(String className) {
|
||||
String mockClass;
|
||||
mockClass = ClassUtil.getMockClassName(ClassUtil.getSourceClassName(className));
|
||||
if (mockClassParser.isMockClass(mockClass)) {
|
||||
return mockClass;
|
||||
String mockClassName = ClassUtil.getMockClassName(ClassUtil.getSourceClassName(className));
|
||||
if (mockClassParser.isMockClass(ClassUtil.getClassNode(mockClassName))) {
|
||||
return mockClassName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -193,7 +201,8 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
*/
|
||||
private String lookForInnerMockClass(ClassNode cn) {
|
||||
for (InnerClassNode ic : cn.innerClasses) {
|
||||
if (ic.name.equals(getInnerMockClassName(cn.name)) && mockClassParser.isMockClass(ic.name)) {
|
||||
ClassNode innerClassNode = ClassUtil.getClassNode(ic.name);
|
||||
if (ic.name.equals(getInnerMockClassName(cn.name)) && mockClassParser.isMockClass(innerClassNode)) {
|
||||
if ((ic.access & ACC_STATIC) == 0) {
|
||||
LogUtil.warn(String.format("Mock class in \"%s\" is not declared as static", cn.name));
|
||||
} else {
|
||||
@ -244,6 +253,17 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getDumpPathByAnnotation(ClassNode cn) {
|
||||
if (cn.visibleAnnotations != null) {
|
||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||
if (toJavaStyleClassName(an.desc).equals(ConstPool.DUMP_TO)) {
|
||||
return AnnotationUtil.getAnnotationParameter(an, FIELD_PATH, null, String.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isExpectedType(String className, ClassType type, ClassType expectedType) {
|
||||
if (type.equals(ClassType.GuessByName)) {
|
||||
return expectedType.equals(ClassType.TestClass) == className.endsWith(TEST_POSTFIX);
|
||||
|
@ -3,6 +3,8 @@ package com.alibaba.testable.agent.util;
|
||||
import com.alibaba.testable.core.model.MockScope;
|
||||
import com.alibaba.testable.core.util.LogUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author flin
|
||||
*/
|
||||
@ -31,7 +33,7 @@ public class GlobalConfig {
|
||||
}
|
||||
|
||||
public static String getDumpPath() {
|
||||
return dumpPath;
|
||||
return (dumpPath == null || dumpPath.isEmpty() || !new File(dumpPath).isDirectory()) ? null : dumpPath;
|
||||
}
|
||||
|
||||
public static void setDumpPath(String path) {
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.alibaba.testable.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Dump byte code for single class
|
||||
*
|
||||
* @author flin
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface DumpTo {
|
||||
|
||||
/**
|
||||
* dump class byte code to specified folder
|
||||
* @return an exist folder
|
||||
*/
|
||||
String path() default "";
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user