support mock package mapping

This commit is contained in:
金戟 2021-05-03 14:56:33 +08:00
parent a33908db41
commit c577301493
4 changed files with 45 additions and 23 deletions

View File

@ -22,6 +22,7 @@ public class PropertiesParser {
private static final String LOG_FILE = "log.file";
private static final String LOG_LEVEL = "log.level";
private static final String INNER_MOCK_CLASS_NAME = "mock.innerClass.name";
private static final String MOCK_PKG_MAPPING_PREFIX = "mock.package.mapping.";
private static final String DEFAULT_MOCK_SCOPE = "mock.scope.default";
private static final String ENABLE_OMNI_INJECT = "omni.constructor.enhance.enable";
private static final String OMNI_INJECT_EXCLUDES = "omni.constructor.enhance.pkgPrefix.excludes";
@ -62,6 +63,8 @@ public class PropertiesParser {
GlobalConfig.setLogLevel(v);
} else if (k.equals(INNER_MOCK_CLASS_NAME)) {
GlobalConfig.setInnerMockClassName(v);
} else if (k.startsWith(MOCK_PKG_MAPPING_PREFIX)) {
GlobalConfig.addMockPackageMapping(k.substring(MOCK_PKG_MAPPING_PREFIX.length()), v);
} else if (k.equals(DEFAULT_MOCK_SCOPE)) {
GlobalConfig.setDefaultMockScope(MockScope.of(v));
} else if (k.equals(ENABLE_OMNI_INJECT)) {

View File

@ -21,7 +21,6 @@ import java.security.ProtectionDomain;
import java.util.List;
import static com.alibaba.testable.agent.constant.ConstPool.*;
import static com.alibaba.testable.agent.util.ClassUtil.toJavaStyleClassName;
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;
@ -67,29 +66,26 @@ public class TestableClassTransformer implements ClassFileTransformer {
}
private byte[] transformMock(byte[] bytes, ClassNode cn) {
String className = cn.name;
String className = (GlobalConfig.getMockPackageMapping() == null) ? cn.name : mapPackage(cn.name);
try {
if (mockClassParser.isMockClass(cn)) {
// it's a mock class
LogUtil.diagnose("Found mock class %s", className);
bytes = new MockClassHandler(className).getBytes(bytes);
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
} else if (foundMockForTestClass(className) != null) {
// it's a test class with testable enabled
LogUtil.diagnose("Found test class %s", className);
bytes = new TestClassHandler().getBytes(bytes);
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
} else {
String mockClass = foundMockForTestClass(className);
String mockClass = foundMockForSourceClass(className);
if (mockClass != null) {
// it's a test class with testable enabled
LogUtil.diagnose("Found test class %s", className);
bytes = new TestClassHandler().getBytes(bytes);
// it's a source class with testable enabled
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
LogUtil.diagnose("Found source class %s", className);
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
} else {
mockClass = foundMockForSourceClass(className);
if (mockClass != null) {
// it's a source class with testable enabled
List<MethodInfo> injectMethods = mockClassParser.getTestableMockMethods(mockClass);
LogUtil.diagnose("Found source class %s", className);
bytes = new SourceClassHandler(injectMethods, mockClass).getBytes(bytes);
BytecodeUtil.dumpByte(className, GlobalConfig.getDumpPath(), bytes);
}
}
}
} catch (Throwable t) {
@ -103,6 +99,17 @@ public class TestableClassTransformer implements ClassFileTransformer {
return bytes;
}
private String mapPackage(String name) {
String dotSeparatedName = ClassUtil.toDotSeparatedName(name);
for (String prefix : GlobalConfig.getMockPackageMapping().keySet()) {
if (dotSeparatedName.startsWith(prefix)) {
return ClassUtil.toSlashSeparatedName(GlobalConfig.getMockPackageMapping().get(prefix))
+ name.substring(prefix.length());
}
}
return name;
}
private String foundMockForSourceClass(String className) {
String mockClass = lookForMockWithAnnotationAsSourceClass(className);
if (mockClass != null) {
@ -236,7 +243,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
private String parseMockWithAnnotation(ClassNode cn, ClassType expectedType) {
if (cn.visibleAnnotations != null) {
for (AnnotationNode an : cn.visibleAnnotations) {
if (toJavaStyleClassName(an.desc).equals(ConstPool.MOCK_WITH)) {
if (ClassUtil.toJavaStyleClassName(an.desc).equals(ConstPool.MOCK_WITH)) {
ClassType type = AnnotationUtil.getAnnotationParameter(an, FIELD_TREAT_AS, ClassType.GuessByName,
ClassType.class);
if (isExpectedType(cn.name, type, expectedType)) {
@ -254,7 +261,7 @@ public class TestableClassTransformer implements ClassFileTransformer {
private String getDumpPathByAnnotation(ClassNode cn) {
if (cn.visibleAnnotations != null) {
for (AnnotationNode an : cn.visibleAnnotations) {
if (toJavaStyleClassName(an.desc).equals(ConstPool.DUMP_TO)) {
if (ClassUtil.toJavaStyleClassName(an.desc).equals(ConstPool.DUMP_TO)) {
String path = AnnotationUtil.getAnnotationParameter(an, FIELD_VALUE, null, String.class);
String fullPath = PathUtil.join(System.getProperty(PROPERTY_USER_DIR), path);
if (createFolder(fullPath)) {

View File

@ -7,12 +7,13 @@ import com.alibaba.testable.core.util.LogUtil;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.alibaba.testable.agent.constant.ConstPool.PROPERTY_TEMP_DIR;
import static com.alibaba.testable.agent.constant.ConstPool.PROPERTY_USER_DIR;
import static com.alibaba.testable.core.constant.ConstPool.COMMA;
import static com.alibaba.testable.core.constant.ConstPool.DOT;
import static com.alibaba.testable.core.constant.ConstPool.*;
import static com.alibaba.testable.core.util.PathUtil.createFolder;
/**
@ -36,6 +37,7 @@ public class GlobalConfig {
private static boolean enhanceThreadLocal = false;
private static boolean enhanceOmniConstructor = false;
private static String innerMockClassName = "Mock";
private static Map<String, String> mockPkgMapping = null;
public static void setLogLevel(String level) {
if (level.equals(MUTE)) {
@ -103,7 +105,7 @@ public class GlobalConfig {
private static String getBuildOutputFolder() {
String contextFolder = System.getProperty(PROPERTY_USER_DIR);
URL rootResourceFolder = Object.class.getResource("/");
URL rootResourceFolder = Object.class.getResource(SLASH);
if (rootResourceFolder != null) {
return PathUtil.getFirstLevelFolder(contextFolder, rootResourceFolder.getPath());
} else if (PathUtil.folderExists(PathUtil.join(contextFolder, DEFAULT_MAVEN_OUTPUT_FOLDER))) {
@ -139,6 +141,17 @@ public class GlobalConfig {
return innerMockClassName;
}
public static void addMockPackageMapping(String originPkg, String mockClassPkg) {
if (mockPkgMapping == null) {
mockPkgMapping = new HashMap<String, String>(5);
}
mockPkgMapping.put(originPkg + DOT, mockClassPkg + DOT);
}
public static Map<String, String> getMockPackageMapping() {
return mockPkgMapping;
}
private static List<String> parsePkgPrefixList(String prefixes) {
List<String> whiteList = new ArrayList<String>();
for (String p : prefixes.split(COMMA)) {

View File

@ -177,7 +177,6 @@ public class OmniConstructor {
// don't travel null object
return;
}
LogUtil.verbose(classPool.size(), "Verifying %s", type.getName());
classPool.put(type, instance);
for (Field f : TypeUtil.getAllFields(type)) {
if (f.getName().startsWith("$") || isStaticFinalField(f)) {
@ -190,14 +189,14 @@ public class OmniConstructor {
if (fieldType.isArray()) {
Class<?> componentType = fieldType.getComponentType();
if (fieldIns != null && !TypeUtil.isBasicType(componentType)) {
LogUtil.verbose(classPool.size(), "Field(Array[%d]) %s", Array.getLength(fieldIns), f.getName());
LogUtil.verbose(classPool.size(), "Verifying Field(Array[%d]) %s", Array.getLength(fieldIns), f.getName());
handleCircleReferenceOfArrayField(fieldIns, componentType, classPool);
}
} else if (!TypeUtil.isBasicType(fieldType)) {
if (fieldIns == null && classPool.containsKey(fieldType)) {
f.set(instance, classPool.get(fieldType));
} else if (!classPool.containsKey(fieldType)) {
LogUtil.verbose(classPool.size(), "Field %s", f.getName());
LogUtil.verbose(classPool.size(), "Verifying Field %s", f.getName());
handleCircleReference(fieldIns, fieldType, classPool);
}
}