diff --git a/parent/pom.xml b/parent/pom.xml
index 09c6fb70a..7c5e761a1 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -119,7 +119,7 @@
junit
junit
- 4.11
+ 4.12
diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java
index af1df415e..720485bfd 100644
--- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java
+++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/AnnotationProcessorTestRunner.java
@@ -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;
}
diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java
index c70fcd0a1..45c630331 100644
--- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java
+++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/CompilingStatement.java
@@ -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 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> sourceClasses, List 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();
diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java
index 002ae646e..375e8ae49 100644
--- a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java
+++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ModifiableURLClassLoader.java
@@ -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 addedURLs = new ConcurrentHashMap();
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;
}
diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java
new file mode 100644
index 000000000..37deb9697
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/testutil/runner/ReplacableTestClass.java
@@ -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 getAnnotatedMethods() {
+ if ( null == delegate ) {
+ return super.getAnnotatedMethods();
+ }
+ else {
+ return delegate.getAnnotatedMethods();
+ }
+ }
+
+ @Override
+ public List getAnnotatedMethods(Class extends Annotation> annotationClass) {
+ if ( null == delegate ) {
+ return super.getAnnotatedMethods( annotationClass );
+ }
+ else {
+ return delegate.getAnnotatedMethods( annotationClass );
+ }
+ }
+
+ @Override
+ public List getAnnotatedFields() {
+ if ( null == delegate ) {
+ return super.getAnnotatedFields();
+ }
+ else {
+ return delegate.getAnnotatedFields();
+ }
+ }
+
+ @Override
+ public List 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 getAnnotation(Class annotationType) {
+ if ( null == delegate ) {
+ return super.getAnnotation( annotationType );
+ }
+ else {
+ return delegate.getAnnotation( annotationType );
+ }
+ }
+
+ @Override
+ public List getAnnotatedFieldValues(Object test, Class extends Annotation> annotationClass,
+ Class valueClass) {
+ if ( null == delegate ) {
+ return super.getAnnotatedFieldValues( test, annotationClass, valueClass );
+ }
+ else {
+ return delegate.getAnnotatedFieldValues( test, annotationClass, valueClass );
+ }
+ }
+
+ @Override
+ public List getAnnotatedMethodValues(Object test, Class extends Annotation> annotationClass,
+ Class 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 );
+ }
+ }
+}