mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#338 update to JUnit 4.12 and fix classloader leaks among the tests.
This commit is contained in:
parent
668f66eb73
commit
6201e6d61d
@ -119,7 +119,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
|
||||
<!-- CDI, Weld, Arquillian -->
|
||||
|
@ -23,6 +23,7 @@ import java.net.URL;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.runners.model.TestClass;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
|
||||
@ -45,6 +46,9 @@ import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
|
||||
*/
|
||||
public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
|
||||
static final ModifiableURLClassLoader TEST_CLASS_LOADER = new ModifiableURLClassLoader();
|
||||
private final Class<?> klass;
|
||||
private Class<?> klassToUse;
|
||||
private ReplacableTestClass replacableTestClass;
|
||||
|
||||
/**
|
||||
* @param klass the test class
|
||||
@ -52,7 +56,8 @@ public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
|
||||
* @throws Exception see {@link BlockJUnit4ClassRunner#BlockJUnit4ClassRunner(Class)}
|
||||
*/
|
||||
public AnnotationProcessorTestRunner(Class<?> klass) throws Exception {
|
||||
super( replaceClassLoaderAndClass( klass ) );
|
||||
super( klass );
|
||||
this.klass = klass;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,17 +68,10 @@ public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
|
||||
* @return the class loaded with the test class loader
|
||||
*/
|
||||
private static Class<?> replaceClassLoaderAndClass(Class<?> klass) {
|
||||
String classFileName = klass.getName().replace( ".", "/" ) + ".class";
|
||||
URL classResource = klass.getClassLoader().getResource( classFileName );
|
||||
String fullyQualifiedUrl = classResource.toExternalForm();
|
||||
String basePath = fullyQualifiedUrl.substring( 0, fullyQualifiedUrl.length() - classFileName.length() );
|
||||
|
||||
TEST_CLASS_LOADER.addURL( basePath );
|
||||
|
||||
Thread.currentThread().setContextClassLoader( TEST_CLASS_LOADER );
|
||||
replaceContextClassLoader( klass );
|
||||
|
||||
try {
|
||||
return TEST_CLASS_LOADER.loadClass( klass.getName() );
|
||||
return Thread.currentThread().getContextClassLoader().loadClass( klass.getName() );
|
||||
}
|
||||
catch ( ClassNotFoundException e ) {
|
||||
throw new RuntimeException( e );
|
||||
@ -81,10 +79,48 @@ public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
|
||||
|
||||
}
|
||||
|
||||
private static void replaceContextClassLoader(Class<?> klass) {
|
||||
String classFileName = klass.getName().replace( ".", "/" ) + ".class";
|
||||
URL classResource = klass.getClassLoader().getResource( classFileName );
|
||||
String fullyQualifiedUrl = classResource.toExternalForm();
|
||||
String basePath = fullyQualifiedUrl.substring( 0, fullyQualifiedUrl.length() - classFileName.length() );
|
||||
|
||||
ModifiableURLClassLoader testClassLoader = new ModifiableURLClassLoader();
|
||||
testClassLoader.addURL( basePath );
|
||||
|
||||
Thread.currentThread().setContextClassLoader( testClassLoader );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TestClass createTestClass(final Class<?> testClass) {
|
||||
replacableTestClass = new ReplacableTestClass( testClass );
|
||||
return replacableTestClass;
|
||||
}
|
||||
|
||||
private FrameworkMethod replaceFrameworkMethod(FrameworkMethod m) {
|
||||
try {
|
||||
return new FrameworkMethod(
|
||||
klassToUse.getDeclaredMethod( m.getName(), m.getMethod().getParameterTypes() ) );
|
||||
}
|
||||
catch ( NoSuchMethodException e ) {
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Statement methodBlock(FrameworkMethod method) {
|
||||
Statement statement = super.methodBlock( method );
|
||||
statement = new CompilingStatement( statement, method, TEST_CLASS_LOADER );
|
||||
CompilingStatement statement = new CompilingStatement( method );
|
||||
if ( statement.needsRecompilation() ) {
|
||||
klassToUse = replaceClassLoaderAndClass( klass );
|
||||
|
||||
replacableTestClass.replaceClass( klassToUse );
|
||||
}
|
||||
|
||||
method = replaceFrameworkMethod( method );
|
||||
|
||||
Statement next = super.methodBlock( method );
|
||||
|
||||
statement.setNextStatement( next );
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
@ -94,20 +95,24 @@ class CompilingStatement extends Statement {
|
||||
"joda-time.jar"
|
||||
);
|
||||
|
||||
private final Statement next;
|
||||
private Statement next;
|
||||
private final FrameworkMethod method;
|
||||
private final ModifiableURLClassLoader classloader;
|
||||
|
||||
private JavaCompiler compiler;
|
||||
private String sourceDir;
|
||||
private String classOutputDir;
|
||||
private String sourceOutputDir;
|
||||
private List<File> classPath;
|
||||
private CompilationRequest compilationRequest;
|
||||
|
||||
public CompilingStatement(Statement next, FrameworkMethod method, ModifiableURLClassLoader classloader) {
|
||||
this.next = next;
|
||||
public CompilingStatement(FrameworkMethod method) {
|
||||
this.method = method;
|
||||
this.classloader = classloader;
|
||||
|
||||
this.compilationRequest = new CompilationRequest( getTestClasses(), getProcessorOptions() );
|
||||
}
|
||||
|
||||
public void setNextStatement(Statement next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,11 +146,11 @@ class CompilingStatement extends Statement {
|
||||
|
||||
createOutputDirs();
|
||||
|
||||
classloader.addOutputDir( classOutputDir );
|
||||
( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).addOutputDir( classOutputDir );
|
||||
}
|
||||
|
||||
protected void generateMapperImplementation() throws Exception {
|
||||
CompilationResultHolder compilationResult = compile( getTestClasses(), getProcessorOptions() );
|
||||
CompilationResultHolder compilationResult = compile();
|
||||
|
||||
CompilationOutcomeDescriptor actualResult =
|
||||
CompilationOutcomeDescriptor.forResult(
|
||||
@ -285,12 +290,11 @@ class CompilingStatement extends Statement {
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
private CompilationResultHolder compile(Set<Class<?>> sourceClasses, List<String> processorOptions)
|
||||
private CompilationResultHolder compile()
|
||||
throws Exception {
|
||||
CompilationRequest request = new CompilationRequest( sourceClasses, processorOptions );
|
||||
|
||||
CompilationCache cache = COMPILATION_CACHE.get();
|
||||
if ( request.equals( cache.lastRequest ) ) {
|
||||
if ( !needsRecompilation() ) {
|
||||
return cache.lastResult;
|
||||
}
|
||||
|
||||
@ -301,7 +305,7 @@ class CompilingStatement extends Statement {
|
||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||
|
||||
Iterable<? extends JavaFileObject> compilationUnits =
|
||||
fileManager.getJavaFileObjectsFromFiles( getSourceFiles( sourceClasses ) );
|
||||
fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.sourceClasses ) );
|
||||
|
||||
try {
|
||||
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
|
||||
@ -313,16 +317,27 @@ class CompilingStatement extends Statement {
|
||||
}
|
||||
|
||||
CompilationTask task =
|
||||
compiler.getTask( null, fileManager, diagnostics, processorOptions, null, compilationUnits );
|
||||
compiler.getTask(
|
||||
null,
|
||||
fileManager,
|
||||
diagnostics,
|
||||
compilationRequest.processorOptions,
|
||||
null,
|
||||
compilationUnits );
|
||||
|
||||
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
|
||||
|
||||
CompilationResultHolder resultHolder = new CompilationResultHolder( diagnostics, task.call() );
|
||||
|
||||
cache.lastRequest = request;
|
||||
cache.lastRequest = compilationRequest;
|
||||
cache.lastResult = resultHolder;
|
||||
return resultHolder;
|
||||
}
|
||||
|
||||
public boolean needsRecompilation() {
|
||||
return !compilationRequest.equals( COMPILATION_CACHE.get().lastRequest );
|
||||
}
|
||||
|
||||
private String getBasePath() {
|
||||
try {
|
||||
return new File( "." ).getCanonicalPath();
|
||||
|
@ -36,6 +36,9 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
|
||||
private static final String ORG_MAPSTRUCT_AP_TEST = "org.mapstruct.ap.test.";
|
||||
|
||||
private static final FilteringParentClassLoader PARENT_CLASS_LOADER = new FilteringParentClassLoader(
|
||||
ORG_MAPSTRUCT_AP_TEST );
|
||||
|
||||
static {
|
||||
tryRegisterAsParallelCapable();
|
||||
}
|
||||
@ -43,7 +46,7 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
private final ConcurrentMap<URL, URL> addedURLs = new ConcurrentHashMap<URL, URL>();
|
||||
|
||||
public ModifiableURLClassLoader() {
|
||||
super( new URL[] { }, new FilteringParentClassLoader() );
|
||||
super( new URL[] { }, PARENT_CLASS_LOADER );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -96,9 +99,15 @@ public class ModifiableURLClassLoader extends URLClassLoader {
|
||||
}
|
||||
|
||||
private static final class FilteringParentClassLoader extends ClassLoader {
|
||||
private String excludedPackage;
|
||||
|
||||
public FilteringParentClassLoader(String excludedPackage) {
|
||||
this.excludedPackage = excludedPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if ( name.startsWith( ORG_MAPSTRUCT_AP_TEST ) ) {
|
||||
if ( name.startsWith( excludedPackage ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||
* and/or other contributors as indicated by the @authors tag. See the
|
||||
* copyright.txt file in the distribution for a full listing of all
|
||||
* contributors.
|
||||
*
|
||||
* 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.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runners.model.FrameworkField;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.TestClass;
|
||||
|
||||
/**
|
||||
* A {@link TestClass} where the wrapped Class can be replaced
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class ReplacableTestClass extends TestClass {
|
||||
private TestClass delegate;
|
||||
|
||||
/**
|
||||
* @see TestClass#TestClass(Class)
|
||||
*/
|
||||
ReplacableTestClass(Class<?> clazz) {
|
||||
super( clazz );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz the new class
|
||||
*/
|
||||
void replaceClass(Class<?> clazz) {
|
||||
delegate = new TestClass( clazz );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkMethod> getAnnotatedMethods() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethods();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethods();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethods( annotationClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethods( annotationClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkField> getAnnotatedFields() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFields();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFields();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkField> getAnnotatedFields(Class<? extends Annotation> annotationClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFields( annotationClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFields( annotationClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getJavaClass() {
|
||||
if ( null == delegate ) {
|
||||
return super.getJavaClass();
|
||||
}
|
||||
else {
|
||||
return delegate.getJavaClass();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if ( null == delegate ) {
|
||||
return super.getName();
|
||||
}
|
||||
else {
|
||||
return delegate.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<?> getOnlyConstructor() {
|
||||
if ( null == delegate ) {
|
||||
return super.getOnlyConstructor();
|
||||
}
|
||||
else {
|
||||
return delegate.getOnlyConstructor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotations();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotation( annotationType );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotation( annotationType );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAnnotatedFieldValues(Object test, Class<? extends Annotation> annotationClass,
|
||||
Class<T> valueClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFieldValues( test, annotationClass, valueClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFieldValues( test, annotationClass, valueClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAnnotatedMethodValues(Object test, Class<? extends Annotation> annotationClass,
|
||||
Class<T> valueClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethodValues( test, annotationClass, valueClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethodValues( test, annotationClass, valueClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if ( null == delegate ) {
|
||||
return super.toString();
|
||||
}
|
||||
else {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublic() {
|
||||
if ( null == delegate ) {
|
||||
return super.isPublic();
|
||||
}
|
||||
else {
|
||||
return delegate.isPublic();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isANonStaticInnerClass() {
|
||||
if ( null == delegate ) {
|
||||
return super.isANonStaticInnerClass();
|
||||
}
|
||||
else {
|
||||
return delegate.isANonStaticInnerClass();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if ( null == delegate ) {
|
||||
return super.hashCode();
|
||||
}
|
||||
else {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( null == delegate ) {
|
||||
return super.equals( obj );
|
||||
}
|
||||
else {
|
||||
return delegate.equals( obj );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user