extract testable context

This commit is contained in:
金戟
2020-05-19 13:17:28 +08:00
parent 7c1cb7d0ad
commit 24a0cb32b2
7 changed files with 135 additions and 104 deletions

View File

@@ -2,16 +2,14 @@ package com.alibaba.testable.generator;
import com.alibaba.testable.generator.model.Statement;
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
import com.alibaba.testable.model.TestableContext;
import com.alibaba.testable.translator.TestableClassDevRoleTranslator;
import com.alibaba.testable.util.ConstPool;
import com.alibaba.testable.util.StringUtil;
import com.squareup.javapoet.*;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Names;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
@@ -28,19 +26,15 @@ import java.util.Set;
*/
public class TestableClassDevRoleGenerator {
private final JavacTrees trees;
private final TreeMaker treeMaker;
private final Names names;
private final TestableContext cx;
public TestableClassDevRoleGenerator(JavacTrees trees, TreeMaker treeMaker, Names names) {
this.trees = trees;
this.treeMaker = treeMaker;
this.names = names;
public TestableClassDevRoleGenerator(TestableContext cx) {
this.cx = cx;
}
public String fetch(Symbol.ClassSymbol clazz, String packageName, String className) {
JCTree tree = trees.getTree(clazz);
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(treeMaker, names);
JCTree tree = cx.trees.getTree(clazz);
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(cx);
tree.accept(translator);
List<MethodSpec> methodSpecs = new ArrayList<>();

View File

@@ -0,0 +1,63 @@
package com.alibaba.testable.model;
import com.alibaba.testable.util.TestableLogger;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.Filer;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* @author flin
*/
public class TestableContext {
/**
* TestableLogger used for printing log during compilation
*/
public final TestableLogger logger;
/**
* Filer used for generate source file
*/
public final Filer filter;
/**
* Elements used for operator element
*/
public final Elements elementUtils;
/**
* Types used for operator type
*/
public final Types typeUtils;
/**
* JavacTrees provide the source AST
*/
public final JavacTrees trees;
/**
* TreeMaker used for creating AST node
*/
public final TreeMaker treeMaker;
/**
* Names used for creating resource name
*/
public final Names names;
public TestableContext(TestableLogger logger, Filer filter, Elements elementUtils,
Types typeUtils, JavacTrees trees, TreeMaker treeMaker, Names names) {
this.logger = logger;
this.filter = filter;
this.elementUtils = elementUtils;
this.typeUtils = typeUtils;
this.trees = trees;
this.treeMaker = treeMaker;
this.names = names;
}
}

View File

@@ -1,5 +1,7 @@
package com.alibaba.testable.processor;
import com.alibaba.testable.model.TestableContext;
import com.alibaba.testable.util.TestableLogger;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.TreeMaker;
@@ -7,75 +9,22 @@ import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
/**
* @author flin
*/
public abstract class BaseProcessor extends AbstractProcessor {
/**
* Messager used for printing log during compilation
*/
private Messager messager;
/**
* Filer used for generate source file
*/
protected Filer filter;
/**
* Elements used for operator element
*/
protected Elements elementUtils;
/**
* Types used for operator type
*/
protected Types typeUtils;
/**
* JavacTrees provide the source AST
*/
protected JavacTrees trees;
/**
* TreeMaker used for creating AST node
*/
protected TreeMaker treeMaker;
/**
* Names used for creating resource name
*/
protected Names names;
protected TestableContext cx;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
Context context = ((JavacProcessingEnvironment)processingEnv).getContext();
messager = processingEnv.getMessager();
filter = processingEnv.getFiler();
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
trees = JavacTrees.instance(processingEnv);
treeMaker = TreeMaker.instance(context);
names = Names.instance(context);
cx = new TestableContext(new TestableLogger(processingEnv.getMessager()), processingEnv.getFiler(),
processingEnv.getElementUtils(), processingEnv.getTypeUtils(), JavacTrees.instance(processingEnv),
TreeMaker.instance(context), Names.instance(context));
}
protected void info(String msg) {
System.out.println("[INFO] " + msg);
}
protected void warn(String msg) {
messager.printMessage(Diagnostic.Kind.MANDATORY_WARNING, msg);
}
protected void error(String msg) {
messager.printMessage(Diagnostic.Kind.ERROR, msg);
}
}

View File

@@ -53,7 +53,7 @@ public class TestableProcessor extends BaseProcessor {
private void createStaticNewClass() {
if (!isStaticNewClassExist()) {
try {
writeSourceFile(ConstPool.SN_PKG_CLS, new StaticNewClassGenerator().fetch());
writeSourceFile(ConstPool.SN_PKG_CLS, new StaticNewClassGenerator(cx).fetch());
} catch (IOException e) {
e.printStackTrace();
}
@@ -62,7 +62,7 @@ public class TestableProcessor extends BaseProcessor {
private boolean isStaticNewClassExist() {
try {
FileObject staticNewClassFile = filter.getResource(SOURCE_OUTPUT, ConstPool.SN_PKG,
FileObject staticNewClassFile = cx.filter.getResource(SOURCE_OUTPUT, ConstPool.SN_PKG,
ConstPool.SN_CLS + JAVA_POSTFIX);
return isCompilingTestClass(staticNewClassFile) || staticNewClassFile.getLastModified() > 0;
} catch (FilerException e) {
@@ -81,20 +81,20 @@ public class TestableProcessor extends BaseProcessor {
}
private void processDevRoleClassElement(Symbol.ClassSymbol clazz) {
String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString();
String packageName = cx.elementUtils.getPackageOf(clazz).getQualifiedName().toString();
String testableTypeName = getTestableClassName(clazz.getSimpleName());
String fullQualityTypeName = packageName + "." + testableTypeName;
try {
writeSourceFile(fullQualityTypeName,
new TestableClassDevRoleGenerator(trees, treeMaker, names).fetch(clazz, packageName, testableTypeName));
new TestableClassDevRoleGenerator(cx).fetch(clazz, packageName, testableTypeName));
} catch (IOException e) {
e.printStackTrace();
}
}
private void processTestRoleClassElement(Symbol.ClassSymbol clazz) {
JCTree tree = trees.getTree(clazz);
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), treeMaker, names));
JCTree tree = cx.trees.getTree(clazz);
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), cx));
}
private String getPkgName(Symbol.ClassSymbol clazz) {
@@ -107,7 +107,7 @@ public class TestableProcessor extends BaseProcessor {
}
private void writeSourceFile(String fullQualityTypeName, String content) throws IOException {
JavaFileObject jfo = filter.createSourceFile(fullQualityTypeName);
JavaFileObject jfo = cx.filter.createSourceFile(fullQualityTypeName);
Writer writer = jfo.openWriter();
writer.write(content);
writer.close();

View File

@@ -1,16 +1,15 @@
package com.alibaba.testable.translator;
import com.alibaba.testable.model.TestableContext;
import com.alibaba.testable.translator.tree.TestableFieldAccess;
import com.alibaba.testable.translator.tree.TestableMethodInvocation;
import com.alibaba.testable.util.ConstPool;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.lang.reflect.Modifier;
@@ -21,8 +20,7 @@ import java.lang.reflect.Modifier;
*/
public class TestableClassDevRoleTranslator extends TreeTranslator {
private final TreeMaker treeMaker;
private final Names names;
private final TestableContext cx;
/**
* Methods to inject
@@ -42,9 +40,8 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
return fields;
}
public TestableClassDevRoleTranslator(TreeMaker treeMaker, Names names) {
this.treeMaker = treeMaker;
this.names = names;
public TestableClassDevRoleTranslator(TestableContext cx) {
this.cx = cx;
}
@Override
@@ -152,12 +149,12 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
}
private TestableMethodInvocation getStaticNewCall(JCTree.JCNewClass newClassExpr, Name className) {
TestableFieldAccess snClass = new TestableFieldAccess(treeMaker.Ident(names.fromString(ConstPool.SN_PKG)),
names.fromString(ConstPool.SN_CLS), null);
TestableFieldAccess snClass = new TestableFieldAccess(cx.treeMaker.Ident(cx.names.fromString(ConstPool.SN_PKG)),
cx.names.fromString(ConstPool.SN_CLS), null);
TestableFieldAccess snMethod = new TestableFieldAccess(snClass,
names.fromString(ConstPool.SN_METHOD), null);
JCTree.JCExpression classType = new TestableFieldAccess(treeMaker.Ident(className),
names.fromString("class"), null);
cx.names.fromString(ConstPool.SN_METHOD), null);
JCTree.JCExpression classType = new TestableFieldAccess(cx.treeMaker.Ident(className),
cx.names.fromString("class"), null);
ListBuffer<JCTree.JCExpression> args = ListBuffer.of(classType);
args.addAll(newClassExpr.args);
return new TestableMethodInvocation(null, snMethod, args.toList());

View File

@@ -1,12 +1,12 @@
package com.alibaba.testable.translator;
import com.alibaba.testable.model.TestLibType;
import com.alibaba.testable.model.TestableContext;
import com.alibaba.testable.translator.tree.TestableFieldAccess;
import com.alibaba.testable.translator.tree.TestableMethodInvocation;
import com.alibaba.testable.util.ConstPool;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;
@@ -22,8 +22,7 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
private static final String ANNOTATION_TESTABLE_INJECT = "com.alibaba.testable.annotation.TestableInject";
private static final String ANNOTATION_JUNIT5_SETUP = "org.junit.jupiter.api.BeforeEach";
private static final String ANNOTATION_JUNIT5_TEST = "org.junit.jupiter.api.Test";
private TreeMaker treeMaker;
private Names names;
private final TestableContext cx;
private String sourceClassName;
private ListBuffer<Name> sourceClassIns = new ListBuffer();
private List<String> stubbornFields = List.nil();
@@ -31,10 +30,9 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
private String testSetupMethodName;
private TestLibType testLibType = TestLibType.JUnit4;
public TestableClassTestRoleTranslator(String pkgName, String className, TreeMaker treeMaker, Names names) {
public TestableClassTestRoleTranslator(String pkgName, String className, TestableContext cx) {
this.sourceClassName = className;
this.treeMaker = treeMaker;
this.names = names;
this.cx = cx;
try {
stubbornFields = List.from(
(String[])Class.forName(pkgName + "." + className + ConstPool.TESTABLE)
@@ -105,24 +103,24 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
super.visitClassDef(jcClassDecl);
ListBuffer<JCTree> ndefs = new ListBuffer<>();
ndefs.addAll(jcClassDecl.defs);
JCTree.JCModifiers mods = treeMaker.Modifiers(Modifier.PUBLIC, makeAnnotations());
ndefs.add(treeMaker.MethodDef(mods, names.fromString("testableSetup"), treeMaker.Type(new Type.JCVoidType()),
List.<JCTree.JCTypeParameter>nil(), List.<JCTree.JCVariableDecl>nil(), List.<JCTree.JCExpression>nil(),
testableSetupBlock(), null));
JCTree.JCModifiers mods = cx.treeMaker.Modifiers(Modifier.PUBLIC, makeAnnotations());
ndefs.add(cx.treeMaker.MethodDef(mods, cx.names.fromString("testableSetup"),
cx.treeMaker.Type(new Type.JCVoidType()), List.<JCTree.JCTypeParameter>nil(),
List.<JCTree.JCVariableDecl>nil(), List.<JCTree.JCExpression>nil(), testableSetupBlock(), null));
jcClassDecl.defs = ndefs.toList();
}
private List<JCTree.JCAnnotation> makeAnnotations() {
String[] elems = ANNOTATION_JUNIT5_TEST.split("\\.");
JCTree.JCExpression e = treeMaker.Ident(names.fromString(elems[0]));
JCTree.JCExpression e = cx.treeMaker.Ident(cx.names.fromString(elems[0]));
for (int i = 1 ; i < elems.length ; i++) {
e = treeMaker.Select(e, names.fromString(elems[i]));
e = cx.treeMaker.Select(e, cx.names.fromString(elems[i]));
}
return List.of(treeMaker.Annotation(e, List.<JCTree.JCExpression>nil()));
return List.of(cx.treeMaker.Annotation(e, List.<JCTree.JCExpression>nil()));
}
private JCTree.JCBlock testableSetupBlock() {
return treeMaker.Block(0, List.<JCTree.JCStatement>nil());
return cx.treeMaker.Block(0, List.<JCTree.JCStatement>nil());
}
/**
@@ -154,7 +152,7 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
private Name getStubbornSetterMethodName(JCTree.JCAssign assign) {
String name = ((JCTree.JCFieldAccess)assign.lhs).name.toString() + ConstPool.TESTABLE_SET_METHOD_PREFIX;
return names.fromString(name);
return cx.names.fromString(name);
}
private boolean isAssignStubbornField(JCTree.JCAssign expr) {
@@ -165,7 +163,7 @@ public class TestableClassTestRoleTranslator extends TreeTranslator {
private JCTree.JCIdent getTestableClassIdent(JCTree.JCExpression clazz) {
Name className = ((JCTree.JCIdent)clazz).name;
return treeMaker.Ident(names.fromString(className + ConstPool.TESTABLE));
return cx.treeMaker.Ident(cx.names.fromString(className + ConstPool.TESTABLE));
}
}

View File

@@ -0,0 +1,30 @@
package com.alibaba.testable.util;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
/**
* practical logger
*
* @author flin
*/
public class TestableLogger {
private Messager messager;
public TestableLogger(Messager messager) {
this.messager = messager;
}
public void info(String msg) {
System.out.println("[INFO] " + msg);
}
public void warn(String msg) {
messager.printMessage(Diagnostic.Kind.MANDATORY_WARNING, msg);
}
public void error(String msg) {
messager.printMessage(Diagnostic.Kind.ERROR, msg);
}
}