From c5f39dfae3080f2b3a2fd390eac259cd554a5e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Sat, 15 May 2021 15:57:02 +0800 Subject: [PATCH] refactor logs, print mock method information in mock class handler --- .../agent/handler/MockClassHandler.java | 82 +++++--------- .../agent/handler/SourceClassHandler.java | 16 ++- .../agent/transformer/MockClassParser.java | 101 ++++++++++++------ .../testable/core/tool/OmniConstructor.java | 7 +- .../alibaba/testable/core/util/LogUtil.java | 2 +- 5 files changed, 108 insertions(+), 100 deletions(-) diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java index 9445686..7a05b5e 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/MockClassHandler.java @@ -1,11 +1,10 @@ package com.alibaba.testable.agent.handler; -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.util.*; -import com.alibaba.testable.core.exception.TargetNotExistException; import com.alibaba.testable.core.model.MockScope; +import com.alibaba.testable.core.util.LogUtil; import com.alibaba.testable.core.util.MockAssociationUtil; import org.objectweb.asm.Label; import org.objectweb.asm.Type; @@ -48,8 +47,10 @@ public class MockClassHandler extends BaseClassWithContextHandler { ClassUtil.toDotSeparatedName(cn.name)); } injectRefFieldAndGetInstanceMethod(cn); + int mockMethodCount = 0; for (MethodNode mn : cn.methods) { if (isMockMethod(mn)) { + mockMethodCount++; mn.access = BytecodeUtil.toPublicAccess(mn.access); // firstly, unfold target class from annotation to parameter unfoldTargetClass(mn); @@ -61,6 +62,7 @@ public class MockClassHandler extends BaseClassWithContextHandler { handleTestableUtil(mn); } } + LogUtil.diagnose(" Found %d mock methods", mockMethodCount); } /** @@ -240,74 +242,38 @@ public class MockClassHandler extends BaseClassWithContextHandler { } for (AnnotationNode an : mn.visibleAnnotations) { if (isMockMethodAnnotation(an)) { - checkTargetMethodExists(mn, an); + if (LogUtil.isVerboseEnabled()) { + LogUtil.verbose(" Mock method \"%s\" as \"%s\"", mn.name, MethodUtil.toJavaMethodDesc( + getTargetMethodOwner(mn, an), getTargetMethodName(mn, an), getTargetMethodDesc(mn, an))); + } return true; } else if (isMockConstructorAnnotation(an)) { - checkTargetConstructorExists(mn); + if (LogUtil.isVerboseEnabled()) { + LogUtil.verbose(" Mock constructor \"%s\" as \"%s\"", mn.name, MethodUtil.toJavaMethodDesc( + ClassUtil.toJavaStyleClassName(MethodUtil.getReturnType(mn.desc)), mn.desc)); + } return true; } } return false; } - private void checkTargetMethodExists(MethodNode mn, AnnotationNode an) { - String targetMethodName = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_METHOD, null, String.class); - if (targetMethodName == null) { - targetMethodName = mn.name; - } - String targetClassName; - String targetMethodDesc; - Type targetClass = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class); - if (targetClass != null) { - targetClassName = targetClass.getClassName(); - targetMethodDesc = mn.desc; - checkMethodExists(mn.name, targetClassName, targetMethodName, targetMethodDesc); - } else if (mn.desc.charAt(1) == TYPE_CLASS) { - ImmutablePair parameterPair = MethodUtil.splitFirstAndRestParameters(mn.desc); - targetClassName = ClassUtil.toDotSeparatedName(parameterPair.left); - targetMethodDesc = parameterPair.right; - checkMethodExists(mn.name, targetClassName, targetMethodName, - MethodUtil.removeFirstParameter(targetMethodDesc)); - } else { - throw new TargetNotExistException("target class not exist", mn.name); - } + private String getTargetMethodOwner(MethodNode mn, AnnotationNode mockMethodAnnotation) { + Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS, + null, Type.class); + return type == null ? MethodUtil.getFirstParameter(mn.desc) : type.getClassName(); } - private void checkMethodExists(String mockMethodName, String targetClassName, String targetMethodName, - String targetMethodDesc) { - ClassNode targetClassNode = ClassUtil.getClassNode(targetClassName); - if (targetClassNode == null) { - throw new TargetNotExistException("target class not found", mockMethodName); - } - boolean targetFound = false; - for (MethodNode targetMethodNode : targetClassNode.methods) { - if (targetMethodNode.name.equals(targetMethodName)) { - targetFound = true; - if (targetMethodNode.desc.equals(targetMethodDesc)) { - return; - } - } - } - throw new TargetNotExistException(targetFound ? - "mock method does not match original method" : "no such method in target class", mockMethodName); + private String getTargetMethodName(MethodNode mn, AnnotationNode mockMethodAnnotation) { + String name = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_METHOD, + null, String.class); + return name == null ? mn.name : name; } - private void checkTargetConstructorExists(MethodNode mn) { - String returnType = MethodUtil.getReturnType(mn.desc); - if (returnType.charAt(0) != TYPE_CLASS) { - throw new TargetNotExistException("return type is not a class", mn.name); - } - ClassNode targetClassNode = ClassUtil.getClassNode(ClassUtil.toJavaStyleClassName(returnType)); - if (targetClassNode == null) { - throw new TargetNotExistException("target class not found", mn.name); - } - for (MethodNode targetMethodNode : targetClassNode.methods) { - if (CONSTRUCTOR.equals(targetMethodNode.name) && - MethodUtil.getParameters(targetMethodNode.desc).equals(MethodUtil.getParameters(mn.desc))) { - return; - } - } - throw new TargetNotExistException("no such constructor in target class", mn.name); + private String getTargetMethodDesc(MethodNode mn, AnnotationNode mockMethodAnnotation) { + Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS, + null, Type.class); + return type == null ? MethodUtil.removeFirstParameter(mn.desc) : mn.desc; } private boolean isMockConstructorAnnotation(AnnotationNode an) { diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java index 6729ecf..2cf88d1 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/handler/SourceClassHandler.java @@ -57,7 +57,7 @@ public class SourceClassHandler extends BaseClassHandler { private void transformMethod(MethodNode mn, Set memberInjectMethods, Set newOperatorInjectMethods) { - LogUtil.diagnose(" Found method %s", mn.name); + LogUtil.verbose(" Found method %s", mn.name); if (mn.name.startsWith("$")) { // skip methods e.g. "$jacocoInit" return; @@ -237,7 +237,12 @@ public class SourceClassHandler extends BaseClassHandler { AbstractInsnNode[] instructions, int start, int end) { String mockMethodName = newOperatorInjectMethod.getMockName(); int invokeOpcode = newOperatorInjectMethod.isStatic() ? INVOKESTATIC : INVOKEVIRTUAL; - LogUtil.diagnose(" Line %d, mock method \"%s\" used", getLineNum(instructions, start), mockMethodName); + String log = String.format("Line %d, mock method \"%s\" used", getLineNum(instructions, start), mockMethodName); + if (LogUtil.isVerboseEnabled()) { + LogUtil.verbose(5, log); + } else { + LogUtil.diagnose(2, log); + } String classType = ((TypeInsnNode)instructions[start]).desc; String constructorDesc = ((MethodInsnNode)instructions[end]).desc; if (!newOperatorInjectMethod.isStatic()) { @@ -268,8 +273,13 @@ public class SourceClassHandler extends BaseClassHandler { private AbstractInsnNode[] replaceMemberCallOps(MethodNode mn, MethodInfo mockMethod, AbstractInsnNode[] instructions, String ownerClass, int opcode, int start, int end) { - LogUtil.diagnose(" Line %d, mock method \"%s\" used", getLineNum(instructions, start), + String log = String.format("Line %d, mock method \"%s\" used", getLineNum(instructions, start), mockMethod.getMockName()); + if (LogUtil.isVerboseEnabled()) { + LogUtil.verbose(5, log); + } else { + LogUtil.diagnose(2, log); + } if (!mockMethod.isStatic()) { mn.instructions.insertBefore(instructions[start], new MethodInsnNode(INVOKESTATIC, mockClassName, GET_TESTABLE_REF, VOID_ARGS + ClassUtil.toByteCodeClassName(mockClassName), false)); diff --git a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java index 9cd9622..a81f708 100644 --- a/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java +++ b/testable-agent/src/main/java/com/alibaba/testable/agent/transformer/MockClassParser.java @@ -7,7 +7,7 @@ import com.alibaba.testable.agent.util.AnnotationUtil; import com.alibaba.testable.agent.util.ClassUtil; import com.alibaba.testable.agent.util.DiagnoseUtil; import com.alibaba.testable.agent.util.MethodUtil; -import com.alibaba.testable.core.util.LogUtil; +import com.alibaba.testable.core.exception.TargetNotExistException; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; @@ -38,7 +38,6 @@ public class MockClassParser { for (MethodNode mn : getAllMethods(cn)) { checkMethodAnnotation(cn, methodInfos, mn); } - LogUtil.diagnose(" Found %d mock methods", methodInfos.size()); return methodInfos; } @@ -85,49 +84,21 @@ public class MockClassParser { for (AnnotationNode an : mn.visibleAnnotations) { String fullClassName = toJavaStyleClassName(an.desc); if (fullClassName.equals(ConstPool.MOCK_CONSTRUCTOR)) { - if (LogUtil.isVerboseEnabled()) { - LogUtil.verbose(" Mock constructor \"%s\" as \"%s\"", mn.name, MethodUtil.toJavaMethodDesc( - ClassUtil.toJavaStyleClassName(MethodUtil.getReturnType(mn.desc)), mn.desc)); - } + checkTargetConstructorExists(mn); addMockConstructor(methodInfos, cn, mn); } else if (fullClassName.equals(ConstPool.MOCK_METHOD) && isValidMockMethod(mn, an)) { - if (LogUtil.isVerboseEnabled()) { - LogUtil.verbose(" Mock method \"%s\" as \"%s\"", mn.name, MethodUtil.toJavaMethodDesc( - getTargetMethodOwner(mn, an), getTargetMethodName(mn, an), getTargetMethodDesc(mn, an))); - } + checkTargetMethodExists(mn, an); String targetMethod = AnnotationUtil.getAnnotationParameter( an, ConstPool.FIELD_TARGET_METHOD, mn.name, String.class); - if (CONSTRUCTOR.equals(targetMethod)) { - addMockConstructor(methodInfos, cn, mn); - } else { - MethodInfo mi = getMethodInfo(cn, mn, an, targetMethod); - if (mi != null) { - methodInfos.add(mi); - } + MethodInfo mi = getMethodInfo(cn, mn, an, targetMethod); + if (mi != null) { + methodInfos.add(mi); } break; } } } - private String getTargetMethodOwner(MethodNode mn, AnnotationNode mockMethodAnnotation) { - Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS, - null, Type.class); - return type == null ? MethodUtil.getFirstParameter(mn.desc) : type.getClassName(); - } - - private String getTargetMethodName(MethodNode mn, AnnotationNode mockMethodAnnotation) { - String name = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_METHOD, - null, String.class); - return name == null ? mn.name : name; - } - - private String getTargetMethodDesc(MethodNode mn, AnnotationNode mockMethodAnnotation) { - Type type = AnnotationUtil.getAnnotationParameter(mockMethodAnnotation, ConstPool.FIELD_TARGET_CLASS, - null, Type.class); - return type == null ? MethodUtil.removeFirstParameter(mn.desc) : mn.desc; - } - private MethodInfo getMethodInfo(ClassNode cn, MethodNode mn, AnnotationNode an, String targetMethod) { Type targetType = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class); boolean isStatic = isStatic(mn); @@ -152,6 +123,66 @@ public class MockClassParser { methodInfos.add(new MethodInfo(sourceClassName, CONSTRUCTOR, mn.desc, cn.name, mn.name, mn.desc, isStatic(mn))); } + private void checkTargetMethodExists(MethodNode mn, AnnotationNode an) { + String targetMethodName = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_METHOD, null, String.class); + if (targetMethodName == null) { + targetMethodName = mn.name; + } + String targetClassName; + String targetMethodDesc; + Type targetClass = AnnotationUtil.getAnnotationParameter(an, ConstPool.FIELD_TARGET_CLASS, null, Type.class); + if (targetClass != null) { + targetClassName = targetClass.getClassName(); + targetMethodDesc = mn.desc; + checkMethodExists(mn.name, targetClassName, targetMethodName, targetMethodDesc); + } else if (mn.desc.charAt(1) == TYPE_CLASS) { + ImmutablePair parameterPair = MethodUtil.splitFirstAndRestParameters(mn.desc); + targetClassName = ClassUtil.toDotSeparatedName(parameterPair.left); + targetMethodDesc = parameterPair.right; + checkMethodExists(mn.name, targetClassName, targetMethodName, + MethodUtil.removeFirstParameter(targetMethodDesc)); + } else { + throw new TargetNotExistException("target class not exist", mn.name); + } + } + + private void checkMethodExists(String mockMethodName, String targetClassName, String targetMethodName, + String targetMethodDesc) { + ClassNode targetClassNode = ClassUtil.getClassNode(targetClassName); + if (targetClassNode == null) { + throw new TargetNotExistException("target class not found", mockMethodName); + } + boolean targetFound = false; + for (MethodNode targetMethodNode : targetClassNode.methods) { + if (targetMethodNode.name.equals(targetMethodName)) { + targetFound = true; + if (targetMethodNode.desc.equals(targetMethodDesc)) { + return; + } + } + } + throw new TargetNotExistException(targetFound ? + "mock method does not match original method" : "no such method in target class", mockMethodName); + } + + private void checkTargetConstructorExists(MethodNode mn) { + String returnType = MethodUtil.getReturnType(mn.desc); + if (returnType.charAt(0) != TYPE_CLASS) { + throw new TargetNotExistException("return type is not a class", mn.name); + } + ClassNode targetClassNode = ClassUtil.getClassNode(ClassUtil.toJavaStyleClassName(returnType)); + if (targetClassNode == null) { + throw new TargetNotExistException("target class not found", mn.name); + } + for (MethodNode targetMethodNode : targetClassNode.methods) { + if (CONSTRUCTOR.equals(targetMethodNode.name) && + MethodUtil.getParameters(targetMethodNode.desc).equals(MethodUtil.getParameters(mn.desc))) { + return; + } + } + throw new TargetNotExistException("no such constructor in target class", mn.name); + } + /** * Check is MockMethod annotation is used on a valid mock method * @param mn mock method diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java index 2cfe37e..73fe582 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java @@ -54,7 +54,7 @@ public class OmniConstructor { } private static T newInstance(Class clazz, Set> classPool) { - LogUtil.verbose(classPool.size(), "Creating %s", clazz.getName()); + LogUtil.verbose(classPool.size() * 2, "Creating %s", clazz.getName()); if (classPool.contains(clazz)) { return null; } @@ -156,6 +156,7 @@ public class OmniConstructor { } private static T handleCircleReference(T instance) { + LogUtil.verbose("Verifying " + instance.getClass()); try { if (instance.getClass().isArray()) { for (int i = 0; i < Array.getLength(instance); i++) { @@ -189,14 +190,14 @@ public class OmniConstructor { if (fieldType.isArray()) { Class componentType = fieldType.getComponentType(); if (fieldIns != null && !TypeUtil.isBasicType(componentType)) { - LogUtil.verbose(classPool.size(), "Verifying Field(Array[%d]) %s", Array.getLength(fieldIns), f.getName()); + LogUtil.verbose(classPool.size() * 2, "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(), "Verifying Field %s", f.getName()); + LogUtil.verbose(classPool.size() * 2, "Verifying Field %s", f.getName()); handleCircleReference(fieldIns, fieldType, classPool); } } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java index 591fac5..8e905b1 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/util/LogUtil.java @@ -110,7 +110,7 @@ public class LogUtil { } private static String space(int indent) { - return StringUtil.repeat(" ", indent); + return StringUtil.repeat(" ", indent); } private static void write(String text) {