Introduces TestAdapter to make maven plugin be aware of different test

frameworks
This commit is contained in:
konsoletyper 2014-02-19 17:52:07 +04:00
parent 301d14e1ab
commit b48cbc98a4
5 changed files with 140 additions and 27 deletions

View File

@ -29,7 +29,7 @@
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.10</version> <version>4.10</version>
<scope>test</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>

View File

@ -0,0 +1,59 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.testing;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import org.junit.Test;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.MethodReader;
import org.teavm.model.ValueType;
/**
*
* @author Alexey Andreev
*/
public class JUnitTestAdapter implements TestAdapter {
@Override
public boolean acceptClass(Class<?> cls) {
for (Method method : cls.getDeclaredMethods()) {
for (Annotation annot : method.getAnnotations()) {
if (annot.annotationType().getName().equals(Test.class.getName())) {
return true;
}
}
}
return false;
}
@Override
public boolean acceptMethod(MethodReader method) {
return method.getAnnotations().get(Test.class.getName()) != null;
}
@Override
public Iterable<String> getExpectedExceptions(MethodReader method) {
AnnotationReader annot = method.getAnnotations().get(Test.class.getName());
AnnotationValue expectedAnnot = annot.getValue("expected");
if (expectedAnnot != null) {
String className = ((ValueType.Object)expectedAnnot.getJavaClass()).getClassName();
return Collections.singletonList(className);
}
return Collections.emptyList();
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.testing;
import org.teavm.model.MethodReader;
/**
*
* @author Alexey Andreev
*/
public interface TestAdapter {
boolean acceptClass(Class<?> cls);
boolean acceptMethod(MethodReader method);
Iterable<String> getExpectedExceptions(MethodReader method);
}

View File

@ -48,6 +48,11 @@
<artifactId>teavm-core</artifactId> <artifactId>teavm-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View File

@ -16,8 +16,8 @@
package org.teavm.maven; package org.teavm.maven;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
@ -33,7 +33,6 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.junit.Test;
import org.teavm.common.FiniteExecutor; import org.teavm.common.FiniteExecutor;
import org.teavm.common.SimpleFiniteExecutor; import org.teavm.common.SimpleFiniteExecutor;
import org.teavm.common.ThreadPoolFiniteExecutor; import org.teavm.common.ThreadPoolFiniteExecutor;
@ -42,6 +41,8 @@ import org.teavm.javascript.JavascriptBuilder;
import org.teavm.javascript.JavascriptBuilderFactory; import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.parsing.ClasspathClassHolderSource; import org.teavm.parsing.ClasspathClassHolderSource;
import org.teavm.testing.JUnitTestAdapter;
import org.teavm.testing.TestAdapter;
/** /**
* *
@ -49,7 +50,7 @@ import org.teavm.parsing.ClasspathClassHolderSource;
*/ */
@Mojo(name = "build-junit", requiresDependencyResolution = ResolutionScope.TEST, @Mojo(name = "build-junit", requiresDependencyResolution = ResolutionScope.TEST,
requiresDependencyCollection = ResolutionScope.TEST) requiresDependencyCollection = ResolutionScope.TEST)
public class BuildJavascriptJUnitMojo extends AbstractMojo { public class BuildJavascriptTestMojo extends AbstractMojo {
private static Set<String> testScopes = new HashSet<>(Arrays.asList( private static Set<String> testScopes = new HashSet<>(Arrays.asList(
Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME, Artifact.SCOPE_COMPILE, Artifact.SCOPE_TEST, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME,
Artifact.SCOPE_PROVIDED)); Artifact.SCOPE_PROVIDED));
@ -57,6 +58,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
private Map<MethodReference, String> fileNames = new HashMap<>(); private Map<MethodReference, String> fileNames = new HashMap<>();
private List<MethodReference> testMethods = new ArrayList<>(); private List<MethodReference> testMethods = new ArrayList<>();
private List<String> testClasses = new ArrayList<>(); private List<String> testClasses = new ArrayList<>();
private TestAdapter adapter;
@Component @Component
private MavenProject project; private MavenProject project;
@ -70,12 +72,18 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.testOutputDirectory}") @Parameter(defaultValue = "${project.build.testOutputDirectory}")
private File testFiles; private File testFiles;
@Parameter
private String[] testFilePatterns = { "*Test", "*UnitTest" };
@Parameter @Parameter
private boolean minifying = true; private boolean minifying = true;
@Parameter @Parameter
private int numThreads = 1; private int numThreads = 1;
@Parameter
private Class<? extends TestAdapter> adapterClass = JUnitTestAdapter.class;
public void setProject(MavenProject project) { public void setProject(MavenProject project) {
this.project = project; this.project = project;
} }
@ -100,9 +108,14 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
this.numThreads = numThreads; this.numThreads = numThreads;
} }
public void setAdapterClass(Class<? extends TestAdapter> adapterClass) {
this.adapterClass = adapterClass;
}
@Override @Override
public void execute() throws MojoExecutionException, MojoFailureException { public void execute() throws MojoExecutionException, MojoFailureException {
Runnable finalizer = null; Runnable finalizer = null;
createAdapter();
try { try {
final ClassLoader classLoader = prepareClassLoader(); final ClassLoader classLoader = prepareClassLoader();
getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'"); getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'");
@ -145,11 +158,13 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
scriptName + "\", expected : ["); scriptName + "\", expected : [");
MethodHolder methodHolder = classSource.get(testClass).getMethod( MethodHolder methodHolder = classSource.get(testClass).getMethod(
methodRef.getDescriptor()); methodRef.getDescriptor());
AnnotationHolder annot = methodHolder.getAnnotations().get("org.junit.Test"); boolean firstException = true;
AnnotationValue expectedAnnot = annot.getValues().get("expected"); for (String exception : adapter.getExpectedExceptions(methodHolder)) {
if (expectedAnnot != null) { if (!firstException) {
String className = ((ValueType.Object)expectedAnnot.getJavaClass()).getClassName(); allTestsWriter.append(", ");
allTestsWriter.append("\"" + className + "\""); }
firstException = false;
allTestsWriter.append("\"" + exception + "\"");
} }
allTestsWriter.append("] }"); allTestsWriter.append("] }");
} }
@ -195,6 +210,23 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
} }
} }
private void createAdapter() throws MojoExecutionException {
Constructor<? extends TestAdapter> cons;
try {
cons = adapterClass.getConstructor();
} catch (NoSuchMethodException e) {
throw new MojoExecutionException("No default constructor found for test adapter " +
adapterClass.getName(), e);
}
try {
adapter = cons.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new MojoExecutionException("Error creating test adapter", e);
} catch (InvocationTargetException e) {
throw new MojoExecutionException("Error creating test adapter", e.getTargetException());
}
}
private ClassLoader prepareClassLoader() throws MojoExecutionException { private ClassLoader prepareClassLoader() throws MojoExecutionException {
try { try {
Log log = getLog(); Log log = getLog();
@ -221,7 +253,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
urls.add(classFiles.toURI().toURL()); urls.add(classFiles.toURI().toURL());
log.info("Using the following classpath for JavaScript JUnit generation: " + classpath); log.info("Using the following classpath for JavaScript JUnit generation: " + classpath);
return new URLClassLoader(urls.toArray(new URL[urls.size()]), return new URLClassLoader(urls.toArray(new URL[urls.size()]),
BuildJavascriptJUnitMojo.class.getClassLoader()); BuildJavascriptTestMojo.class.getClassLoader());
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new MojoExecutionException("Error gathering classpath information", e); throw new MojoExecutionException("Error gathering classpath information", e);
} }
@ -304,12 +336,6 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
} }
private void findTestClasses(ClassLoader classLoader, File folder, String prefix) { private void findTestClasses(ClassLoader classLoader, File folder, String prefix) {
Class<? extends Annotation> testAnnot;
try {
testAnnot = Class.forName(Test.class.getName(), true, classLoader).asSubclass(Annotation.class);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load `" + Test.class.getName() + "` annotation");
}
for (File file : folder.listFiles()) { for (File file : folder.listFiles()) {
if (file.isDirectory()) { if (file.isDirectory()) {
String newPrefix = prefix.isEmpty() ? file.getName() : prefix + "." + file.getName(); String newPrefix = prefix.isEmpty() ? file.getName() : prefix + "." + file.getName();
@ -321,14 +347,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
} }
try { try {
Class<?> candidate = Class.forName(className, true, classLoader); Class<?> candidate = Class.forName(className, true, classLoader);
boolean hasTests = false; if (adapter.acceptClass(candidate)) {
for (Method method : candidate.getDeclaredMethods()) {
if (method.isAnnotationPresent(testAnnot)) {
hasTests = true;
break;
}
}
if (hasTests) {
testClasses.add(candidate.getName()); testClasses.add(candidate.getName());
getLog().info("Test class detected: " + candidate.getName()); getLog().info("Test class detected: " + candidate.getName());
} }
@ -341,7 +360,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
private void findTests(ClassHolder cls) { private void findTests(ClassHolder cls) {
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
if (method.getAnnotations().get("org.junit.Test") != null) { if (adapter.acceptMethod(method)) {
MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor()); MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor());
testMethods.add(ref); testMethods.add(ref);
List<MethodReference> group = groupedMethods.get(cls.getName()); List<MethodReference> group = groupedMethods.get(cls.getName());
@ -355,7 +374,7 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
} }
private void resourceToFile(String resource, String fileName) throws IOException { private void resourceToFile(String resource, String fileName) throws IOException {
try (InputStream input = BuildJavascriptJUnitMojo.class.getClassLoader().getResourceAsStream(resource)) { try (InputStream input = BuildJavascriptTestMojo.class.getClassLoader().getResourceAsStream(resource)) {
try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) { try (OutputStream output = new FileOutputStream(new File(outputDir, fileName))) {
IOUtils.copy(input, output); IOUtils.copy(input, output);
} }