mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-07 19:00:45 +08:00
allow to dump single mock class
This commit is contained in:
parent
2780ccc6fa
commit
93ad1fa94f
@ -8,6 +8,7 @@ public class ConstPool {
|
||||
public static final String FIELD_TARGET_METHOD = "targetMethod";
|
||||
public static final String FIELD_TARGET_CLASS = "targetClass";
|
||||
public static final String FIELD_SCOPE = "scope";
|
||||
public static final String FIELD_VALUE = "value";
|
||||
|
||||
public static final String PROPERTY_USER_DIR = "user.dir";
|
||||
public static final String PROPERTY_TEMP_DIR = "java.io.tmpdir";
|
||||
|
@ -22,10 +22,10 @@ import java.lang.instrument.ClassFileTransformer;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.*;
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.CGLIB_CLASS_PATTERN;
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.KOTLIN_POSTFIX_COMPANION;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.DOLLAR;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
||||
import static com.alibaba.testable.core.util.PathUtil.createFolder;
|
||||
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
|
||||
/**
|
||||
@ -33,7 +33,6 @@ import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
*/
|
||||
public class TestableClassTransformer implements ClassFileTransformer {
|
||||
|
||||
private static final String FIELD_VALUE = "value";
|
||||
private static final String FIELD_TREAT_AS = "treatAs";
|
||||
private static final String CLASS_JUNIT_5_NESTED = "Lorg/junit/jupiter/api/Nested;";
|
||||
|
||||
@ -71,7 +70,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
if (mockClassParser.isMockClass(cn)) {
|
||||
// it's a mock class
|
||||
bytes = new MockClassHandler(className).getBytes(bytes);
|
||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||
return bytes;
|
||||
}
|
||||
String mockClass = foundMockForSourceClass(className);
|
||||
@ -79,14 +78,14 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
// it's a source class with testable enabled
|
||||
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
|
||||
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
|
||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||
return bytes;
|
||||
}
|
||||
Framework framework = testClassChecker.checkFramework(cn);
|
||||
if (framework != null) {
|
||||
// it's a test class
|
||||
bytes = new TestClassHandler(framework).getBytes(bytes);
|
||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
||||
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||
return bytes;
|
||||
} else if (cn.name.endsWith(TEST_POSTFIX)) {
|
||||
LogUtil.verbose("Failed to detect test framework for %s", cn.name);
|
||||
@ -101,7 +100,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
} finally {
|
||||
LogUtil.resetLogLevel();
|
||||
}
|
||||
BytecodeUtil.dumpByte(className, getDumpPathByAnnotation(cn), bytes);
|
||||
BytecodeUtil.dumpByte(cn, null, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -257,7 +256,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
ClassType type = AnnotationUtil.getAnnotationParameter(an, FIELD_TREAT_AS, ClassType.GuessByName,
|
||||
ClassType.class);
|
||||
if (isExpectedType(cn.name, type, expectedType)) {
|
||||
Type clazz = AnnotationUtil.getAnnotationParameter(an, FIELD_VALUE,
|
||||
Type clazz = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_VALUE,
|
||||
Type.getType(NullType.class), Type.class);
|
||||
DiagnoseUtil.setupByClass(ClassUtil.getClassNode(clazz.getClassName()));
|
||||
return clazz.getClassName();
|
||||
@ -268,21 +267,6 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getDumpPathByAnnotation(ClassNode cn) {
|
||||
if (cn.visibleAnnotations != null) {
|
||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||
if ((ClassUtil.toByteCodeClassName(ConstPool.DUMP_TO)).equals(an.desc)) {
|
||||
String path = AnnotationUtil.getAnnotationParameter(an, FIELD_VALUE, null, String.class);
|
||||
String fullPath = PathUtil.join(System.getProperty(PROPERTY_USER_DIR), path);
|
||||
if (createFolder(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
@ -1,19 +1,20 @@
|
||||
package com.alibaba.testable.agent.util;
|
||||
|
||||
import com.alibaba.testable.agent.constant.ByteCodeConst;
|
||||
import com.alibaba.testable.agent.constant.ConstPool;
|
||||
import com.alibaba.testable.agent.tool.ImmutablePair;
|
||||
import com.alibaba.testable.core.util.LogUtil;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.FIELD_VALUE;
|
||||
import static com.alibaba.testable.agent.constant.ConstPool.PROPERTY_USER_DIR;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.*;
|
||||
import static com.alibaba.testable.core.constant.ConstPool.UNDERLINE;
|
||||
import static com.alibaba.testable.core.util.PathUtil.createFolder;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -21,6 +22,8 @@ import static org.objectweb.asm.Opcodes.*;
|
||||
*/
|
||||
public class BytecodeUtil {
|
||||
|
||||
private static final String POSTFIX_CLASS = ".class";
|
||||
|
||||
/**
|
||||
* refer to https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
|
||||
* positive for push stack, negative for pop stack
|
||||
@ -208,17 +211,20 @@ public class BytecodeUtil {
|
||||
|
||||
/**
|
||||
* Dump byte code to specified class file
|
||||
* @param className original class name
|
||||
* @param cn original class node
|
||||
* @param dumpPath folder to store class file
|
||||
* @param bytes original class bytes
|
||||
*/
|
||||
public static void dumpByte(String className, String dumpPath, byte[] bytes) {
|
||||
public static void dumpByte(ClassNode cn, String dumpPath, byte[] bytes) {
|
||||
if (dumpPath == null) {
|
||||
return;
|
||||
dumpPath = getDumpPathByAnnotation(cn);
|
||||
if (dumpPath == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
String dumpFile = PathUtil.join(dumpPath,
|
||||
className.replace(SLASH, DOT).replace(DOLLAR, UNDERLINE) + ".class");
|
||||
cn.name.replace(SLASH, DOT).replace(DOLLAR, UNDERLINE) + POSTFIX_CLASS);
|
||||
LogUtil.verbose("Dump class: " + dumpFile);
|
||||
FileOutputStream stream = new FileOutputStream(dumpFile);
|
||||
stream.write(bytes);
|
||||
@ -275,4 +281,19 @@ public class BytecodeUtil {
|
||||
return new IntInsnNode(BIPUSH, num);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDumpPathByAnnotation(ClassNode cn) {
|
||||
if (cn.visibleAnnotations != null) {
|
||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||
if ((ClassUtil.toByteCodeClassName(ConstPool.DUMP_TO)).equals(an.desc)) {
|
||||
String path = AnnotationUtil.getAnnotationParameter(an, FIELD_VALUE, null, String.class);
|
||||
String fullPath = PathUtil.join(System.getProperty(PROPERTY_USER_DIR), path);
|
||||
if (createFolder(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -8,15 +8,13 @@ import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
public class DiagnoseUtil {
|
||||
|
||||
private static final String FIELD_VALUE = "value";
|
||||
|
||||
public static void setupByClass(ClassNode cn) {
|
||||
if (cn == null || cn.visibleAnnotations == null) {
|
||||
return;
|
||||
}
|
||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_DIAGNOSE).equals(an.desc)) {
|
||||
setupDiagnose(an, FIELD_VALUE);
|
||||
setupDiagnose(an, ConstPool.FIELD_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user