mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-08 19:30:47 +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_METHOD = "targetMethod";
|
||||||
public static final String FIELD_TARGET_CLASS = "targetClass";
|
public static final String FIELD_TARGET_CLASS = "targetClass";
|
||||||
public static final String FIELD_SCOPE = "scope";
|
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_USER_DIR = "user.dir";
|
||||||
public static final String PROPERTY_TEMP_DIR = "java.io.tmpdir";
|
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.security.ProtectionDomain;
|
||||||
import java.util.List;
|
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.DOLLAR;
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.TEST_POSTFIX;
|
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;
|
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 {
|
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_TREAT_AS = "treatAs";
|
||||||
private static final String CLASS_JUNIT_5_NESTED = "Lorg/junit/jupiter/api/Nested;";
|
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)) {
|
if (mockClassParser.isMockClass(cn)) {
|
||||||
// it's a mock class
|
// it's a mock class
|
||||||
bytes = new MockClassHandler(className).getBytes(bytes);
|
bytes = new MockClassHandler(className).getBytes(bytes);
|
||||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
String mockClass = foundMockForSourceClass(className);
|
String mockClass = foundMockForSourceClass(className);
|
||||||
@ -79,14 +78,14 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
// it's a source class with testable enabled
|
// it's a source class with testable enabled
|
||||||
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
|
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
|
||||||
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
|
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
|
||||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
Framework framework = testClassChecker.checkFramework(cn);
|
Framework framework = testClassChecker.checkFramework(cn);
|
||||||
if (framework != null) {
|
if (framework != null) {
|
||||||
// it's a test class
|
// it's a test class
|
||||||
bytes = new TestClassHandler(framework).getBytes(bytes);
|
bytes = new TestClassHandler(framework).getBytes(bytes);
|
||||||
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
|
BytecodeUtil.dumpByte(cn, GlobalConfig.getDumpPath(), bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
} else if (cn.name.endsWith(TEST_POSTFIX)) {
|
} else if (cn.name.endsWith(TEST_POSTFIX)) {
|
||||||
LogUtil.verbose("Failed to detect test framework for %s", cn.name);
|
LogUtil.verbose("Failed to detect test framework for %s", cn.name);
|
||||||
@ -101,7 +100,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
} finally {
|
} finally {
|
||||||
LogUtil.resetLogLevel();
|
LogUtil.resetLogLevel();
|
||||||
}
|
}
|
||||||
BytecodeUtil.dumpByte(className, getDumpPathByAnnotation(cn), bytes);
|
BytecodeUtil.dumpByte(cn, null, bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +256,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
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)) {
|
||||||
Type clazz = AnnotationUtil.getAnnotationParameter(an, FIELD_VALUE,
|
Type clazz = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_VALUE,
|
||||||
Type.getType(NullType.class), Type.class);
|
Type.getType(NullType.class), Type.class);
|
||||||
DiagnoseUtil.setupByClass(ClassUtil.getClassNode(clazz.getClassName()));
|
DiagnoseUtil.setupByClass(ClassUtil.getClassNode(clazz.getClassName()));
|
||||||
return clazz.getClassName();
|
return clazz.getClassName();
|
||||||
@ -268,21 +267,6 @@ public class TestableClassTransformer implements ClassFileTransformer {
|
|||||||
return null;
|
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) {
|
private boolean isExpectedType(String className, ClassType type, ClassType expectedType) {
|
||||||
if (type.equals(ClassType.GuessByName)) {
|
if (type.equals(ClassType.GuessByName)) {
|
||||||
return expectedType.equals(ClassType.TestClass) == className.endsWith(TEST_POSTFIX);
|
return expectedType.equals(ClassType.TestClass) == className.endsWith(TEST_POSTFIX);
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package com.alibaba.testable.agent.util;
|
package com.alibaba.testable.agent.util;
|
||||||
|
|
||||||
import com.alibaba.testable.agent.constant.ByteCodeConst;
|
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.agent.tool.ImmutablePair;
|
||||||
import com.alibaba.testable.core.util.LogUtil;
|
import com.alibaba.testable.core.util.LogUtil;
|
||||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
import org.objectweb.asm.tree.*;
|
||||||
import org.objectweb.asm.tree.InsnNode;
|
|
||||||
import org.objectweb.asm.tree.IntInsnNode;
|
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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.*;
|
||||||
import static com.alibaba.testable.core.constant.ConstPool.UNDERLINE;
|
import static com.alibaba.testable.core.util.PathUtil.createFolder;
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,6 +22,8 @@ import static org.objectweb.asm.Opcodes.*;
|
|||||||
*/
|
*/
|
||||||
public class BytecodeUtil {
|
public class BytecodeUtil {
|
||||||
|
|
||||||
|
private static final String POSTFIX_CLASS = ".class";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* refer to https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
|
* refer to https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
|
||||||
* positive for push stack, negative for pop stack
|
* positive for push stack, negative for pop stack
|
||||||
@ -208,17 +211,20 @@ public class BytecodeUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump byte code to specified class file
|
* 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 dumpPath folder to store class file
|
||||||
* @param bytes original class bytes
|
* @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) {
|
if (dumpPath == null) {
|
||||||
return;
|
dumpPath = getDumpPathByAnnotation(cn);
|
||||||
|
if (dumpPath == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String dumpFile = PathUtil.join(dumpPath,
|
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);
|
LogUtil.verbose("Dump class: " + dumpFile);
|
||||||
FileOutputStream stream = new FileOutputStream(dumpFile);
|
FileOutputStream stream = new FileOutputStream(dumpFile);
|
||||||
stream.write(bytes);
|
stream.write(bytes);
|
||||||
@ -275,4 +281,19 @@ public class BytecodeUtil {
|
|||||||
return new IntInsnNode(BIPUSH, num);
|
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 {
|
public class DiagnoseUtil {
|
||||||
|
|
||||||
private static final String FIELD_VALUE = "value";
|
|
||||||
|
|
||||||
public static void setupByClass(ClassNode cn) {
|
public static void setupByClass(ClassNode cn) {
|
||||||
if (cn == null || cn.visibleAnnotations == null) {
|
if (cn == null || cn.visibleAnnotations == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (AnnotationNode an : cn.visibleAnnotations) {
|
for (AnnotationNode an : cn.visibleAnnotations) {
|
||||||
if (ClassUtil.toByteCodeClassName(ConstPool.MOCK_DIAGNOSE).equals(an.desc)) {
|
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