move agent jar to class folder and bump version to v0.1.0-snapshot

This commit is contained in:
金戟 2020-10-14 20:18:45 +08:00
parent c869caa0c9
commit 3bf9e1a623
7 changed files with 87 additions and 72 deletions

View File

@ -5,7 +5,7 @@
<groupId>com.alibaba.testable</groupId>
<artifactId>agent</artifactId>
<version>0.0.5-SNAPSHOT</version>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

View File

@ -8,7 +8,7 @@
<description>Unit test enhancement toolkit</description>
<groupId>com.alibaba.testable</groupId>
<artifactId>core</artifactId>
<version>0.0.5-SNAPSHOT</version>
<version>0.1.0-SNAPSHOT</version>
<properties>
<plugin.compiler.version>3.8.1</plugin.compiler.version>

View File

@ -60,4 +60,14 @@ public class TestableContext {
this.names = names;
}
public TestableContext(TestableLogger logger, Filer filter) {
this.logger = logger;
this.filter = filter;
this.elementUtils = null;
this.typeUtils = null;
this.trees = null;
this.treeMaker = null;
this.names = null;
}
}

View File

@ -1,30 +0,0 @@
package com.alibaba.testable.core.processor;
import com.alibaba.testable.core.model.TestableContext;
import com.alibaba.testable.core.util.TestableLogger;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
/**
* @author flin
*/
public abstract class BaseProcessor extends AbstractProcessor {
protected TestableContext cx;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
Context context = ((JavacProcessingEnvironment)processingEnv).getContext();
cx = new TestableContext(new TestableLogger(processingEnv.getMessager()), processingEnv.getFiler(),
processingEnv.getElementUtils(), processingEnv.getTypeUtils(), JavacTrees.instance(processingEnv),
TreeMaker.instance(context), Names.instance(context));
}
}

View File

@ -1,15 +1,20 @@
package com.alibaba.testable.core.processor;
import com.alibaba.testable.core.annotation.EnableTestable;
import com.alibaba.testable.core.translator.EnableTestableTranslator;
import com.alibaba.testable.core.constant.ConstPool;
import com.alibaba.testable.core.model.TestableContext;
import com.alibaba.testable.core.translator.EnableTestableTranslator;
import com.alibaba.testable.core.util.ResourceUtil;
import com.alibaba.testable.core.util.TestableLogger;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
@ -24,15 +29,41 @@ import java.util.Set;
* @author flin
*/
@SupportedAnnotationTypes("com.alibaba.testable.core.annotation.EnableTestable")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class EnableTestableProcessor extends BaseProcessor {
public class EnableTestableProcessor extends AbstractProcessor {
private static final String TESTABLE_AGENT_JAR = "testable-agent.jar";
private static boolean hasFirstClassCompiled = false;
private static final String TEST_OUTPUT_FOLDER_MARK = "/test-classes/";
private TestableContext cx;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
Context context = getJavacProcessingContext(processingEnv);
if (context == null) {
cx = new TestableContext(new TestableLogger(processingEnv.getMessager()), processingEnv.getFiler());
cx.logger.info("Skip testable compile time processing");
} else {
cx = new TestableContext(new TestableLogger(processingEnv.getMessager()), processingEnv.getFiler(),
processingEnv.getElementUtils(), processingEnv.getTypeUtils(), JavacTrees.instance(processingEnv),
TreeMaker.instance(context), Names.instance(context));
}
createTestableAgentJar();
cx.logger.info("Testable processor initialized");
}
private Context getJavacProcessingContext(ProcessingEnvironment processingEnv) {
try {
return ((JavacProcessingEnvironment)processingEnv).getContext();
} catch (Exception e) {
return null;
}
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
createTestableAgentJar();
if (cx.names == null) {
return true;
}
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(EnableTestable.class);
for (Element element : elements) {
if (element.getKind().isClass() && isTestClass(element.getSimpleName())) {
@ -42,40 +73,41 @@ public class EnableTestableProcessor extends BaseProcessor {
return true;
}
@Override
public SourceVersion getSupportedSourceVersion() {
// always return the latest version
return SourceVersion.values()[SourceVersion.values().length - 1];
}
private boolean isTestClass(Name name) {
return name.toString().endsWith(ConstPool.TEST_POSTFIX);
}
private void processClassElement(Symbol.ClassSymbol clazz) {
JCTree tree = cx.trees.getTree(clazz);
tree.accept(new EnableTestableTranslator(getPkgName(clazz), clazz.getSimpleName().toString(), cx));
}
private String getPkgName(Symbol.ClassSymbol clazz) {
return ((Symbol.PackageSymbol)clazz.owner).fullname.toString();
String pkgName = ((Symbol.PackageSymbol)clazz.owner).fullname.toString();
tree.accept(new EnableTestableTranslator(pkgName, clazz.getSimpleName().toString(), cx));
}
private void createTestableAgentJar() {
if (!hasFirstClassCompiled) {
hasFirstClassCompiled = true;
byte[] bytes = ResourceUtil.fetchBinary(TESTABLE_AGENT_JAR);
if (bytes.length == 0) {
cx.logger.error("Failed to generate testable agent jar");
}
writeBinaryFile("", TESTABLE_AGENT_JAR, bytes);
byte[] bytes = ResourceUtil.fetchBinary(TESTABLE_AGENT_JAR);
if (bytes.length == 0) {
cx.logger.info("Failed to fetch testable agent jar");
}
}
private void writeBinaryFile(String path, String fileName, byte[] content) {
try {
FileObject resource = cx.filter.createResource(StandardLocation.SOURCE_OUTPUT, path, fileName);
FileObject resource = cx.filter.createResource(StandardLocation.CLASS_OUTPUT, "", TESTABLE_AGENT_JAR);
if (!resource.getName().contains(TEST_OUTPUT_FOLDER_MARK)) {
cx.logger.info("Skip generate testable agent jar");
return;
}
cx.logger.info("Generating " + resource.getName());
try (OutputStream out = resource.openOutputStream()) {
out.write(content);
out.write(bytes);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
cx.logger.error("Failed to write " + fileName);
cx.logger.error("Failed to generate testable agent jar");
}
}
}

View File

@ -13,8 +13,7 @@ public class ResourceUtil {
StringBuilder buffer = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null)
{
while ((line = reader.readLine()) != null) {
buffer.append(line).append('\n');
}
reader.close();

View File

@ -1,22 +1,26 @@
# Release Note
## v0.0.1
- PoC version
- use compile time code modification to support new operation mocking and private field & method access
## v0.1.0
- move generated agent jar to class folder
- now support mock method of any object
## v0.0.2
- add support of member method mocking by compile time code modification
## v0.0.5
- use dynamically runtime modification to replace static `e.java` file
- get rid of unit test framework dependence
- add testable ref field in test class at runtime instead of compile time
## v0.0.4
- use runtime byte code rewrite to invoke testable setup method
- add `TestableUtil` class to fetch current test case and invocation source
## v0.0.3
- use global method invoke to access private members instead of modification in place
- use `e.java` replace `testable` class make code more readable
- introduce `agent` module, use runtime byte code modification to support new operation and member method mocking
## v0.0.4
- use runtime byte code rewrite to invoke testable setup method
- add `TestableUtil` class to fetch current test case and invocation source
## v0.0.2
- add support of member method mocking by compile time code modification
## v0.0.5
- use dynamically runtime modification to replace static `e.java` file
- get rid of unit test framework dependence
- add testable ref field in test class at runtime instead of compile time
## v0.0.1
- PoC version
- use compile time code modification to support new operation mocking and private field & method access