diff --git a/parent/pom.xml b/parent/pom.xml
index 7e0e7118c..6051bcc51 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -44,6 +44,8 @@
1.0.0
1.2
3.2.3.RELEASE
+
+ 1
@@ -299,6 +301,9 @@
org.apache.maven.plugins
maven-surefire-plugin
2.17
+
+ ${forkCount}
+
com.mycila.maven-license-plugin
diff --git a/processor/pom.xml b/processor/pom.xml
index 657ca26fd..eb12ede93 100644
--- a/processor/pom.xml
+++ b/processor/pom.xml
@@ -83,6 +83,15 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ compilation-tests-${surefire.forkNumber}
+
+
+
org.apache.maven.plugins
maven-dependency-plugin
diff --git a/processor/src/test/java/org/mapstruct/ap/testutil/MapperTestBase.java b/processor/src/test/java/org/mapstruct/ap/testutil/MapperTestBase.java
index 4e951d037..46f6f8de5 100644
--- a/processor/src/test/java/org/mapstruct/ap/testutil/MapperTestBase.java
+++ b/processor/src/test/java/org/mapstruct/ap/testutil/MapperTestBase.java
@@ -33,7 +33,10 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
@@ -49,7 +52,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutco
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
-import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
/**
@@ -72,7 +74,28 @@ public abstract class MapperTestBase {
private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
private static final DiagnosticDescriptorComparator COMPARATOR = new DiagnosticDescriptorComparator();
- private static volatile boolean enhancedClassloader = false;
+ private static final String TARGET_COMPILATION_TESTS = "/target/" + System.getProperty(
+ "mapper.test.output.dir",
+ "compilation-tests"
+ ) + "_";
+
+ private static Map threadsWithEnhancedClassloader = new ConcurrentHashMap();
+
+ private static ThreadLocal threadNumber = new ThreadLocal() {
+ private AtomicInteger highWaterMark = new AtomicInteger( 0 );
+
+ @Override
+ protected Integer initialValue() {
+ return highWaterMark.getAndIncrement();
+ }
+ };
+
+ private static ThreadLocal compilationCache = new ThreadLocal() {
+ @Override
+ protected CompilationCache initialValue() {
+ return new CompilationCache();
+ }
+ };
private JavaCompiler compiler;
private String sourceDir;
@@ -80,21 +103,21 @@ public abstract class MapperTestBase {
private String sourceOutputDir;
private List classPath;
private final List libraries;
- private DiagnosticCollector diagnostics;
public MapperTestBase() {
this.libraries = Arrays.asList( "mapstruct.jar", "guava.jar", "javax.inject.jar" );
}
- @BeforeClass
- public void setup() throws Exception {
+ protected void setupCompiler() throws Exception {
compiler = ToolProvider.getSystemJavaCompiler();
String basePath = getBasePath();
+ Integer i = threadNumber.get();
+
sourceDir = basePath + "/src/test/java";
- classOutputDir = basePath + "/target/compilation-tests/classes";
- sourceOutputDir = basePath + "/target/compilation-tests/generated-sources/mapping";
+ classOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/classes";
+ sourceOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/generated-sources/mapping";
String testDependenciesDir = basePath + "/target/test-dependencies/";
@@ -106,7 +129,7 @@ public abstract class MapperTestBase {
createOutputDirs();
// TODO #140 Is there a better way to do this?
- if ( !enhancedClassloader ) {
+ if ( !threadsWithEnhancedClassloader.containsKey( i ) ) {
// we need to make sure that the the generated classes are loaded by the same classloader as the test has
// been loaded already. Otherwise some tests won't work.
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
@@ -114,22 +137,20 @@ public abstract class MapperTestBase {
Method method = clazz.getDeclaredMethod( "addURL", new Class[] { URL.class } );
method.setAccessible( true );
method.invoke( classLoader, new File( classOutputDir ).toURI().toURL() );
- enhancedClassloader = true;
+
+ threadsWithEnhancedClassloader.put( i, i );
}
}
@BeforeMethod
- public void generateMapperImplementation(Method testMethod) {
- diagnostics = new DiagnosticCollector();
- Set sourceFiles = getSourceFiles( getTestClasses( testMethod ) );
- List processorOptions = getProcessorOptions( testMethod );
-
- boolean compilationSuccessful = compile( diagnostics, sourceFiles, processorOptions );
+ public void generateMapperImplementation(Method testMethod) throws Exception {
+ CompilationResultHolder compilationResult =
+ compile( getTestClasses( testMethod ), getProcessorOptions( testMethod ) );
CompilationOutcomeDescriptor actualResult = CompilationOutcomeDescriptor.forResult(
sourceDir,
- compilationSuccessful,
- diagnostics.getDiagnostics()
+ compilationResult.compilationSuccessful,
+ compilationResult.diagnostics.getDiagnostics()
);
CompilationOutcomeDescriptor expectedResult = CompilationOutcomeDescriptor.forExpectedCompilationResult(
testMethod.getAnnotation( ExpectedCompilationOutcome.class )
@@ -137,7 +158,9 @@ public abstract class MapperTestBase {
if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) {
assertThat( actualResult.getCompilationResult() )
- .describedAs( "Compilation failed. Diagnostics: " + diagnostics.getDiagnostics() )
+ .describedAs(
+ "Compilation failed. Diagnostics: " + compilationResult.diagnostics.getDiagnostics()
+ )
.isEqualTo( CompilationResult.SUCCEEDED );
}
else {
@@ -260,11 +283,21 @@ public abstract class MapperTestBase {
return sourceFiles;
}
- private boolean compile(DiagnosticCollector diagnostics, Iterable sourceFiles,
- List processorOptions) {
+ private CompilationResultHolder compile(Set> sourceClasses, List processorOptions)
+ throws Exception {
+ CompilationRequest request = new CompilationRequest( sourceClasses, processorOptions );
+
+ CompilationCache cache = compilationCache.get();
+ if ( request.equals( cache.lastRequest ) ) {
+ return cache.lastResult;
+ }
+
+ setupCompiler();
+ DiagnosticCollector diagnostics = new DiagnosticCollector();
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
- Iterable extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles( sourceFiles );
+ Iterable extends JavaFileObject> compilationUnits =
+ fileManager.getJavaFileObjectsFromFiles( getSourceFiles( sourceClasses ) );
try {
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
@@ -285,7 +318,11 @@ public abstract class MapperTestBase {
);
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
- return task.call();
+ CompilationResultHolder resultHolder = new CompilationResultHolder( diagnostics, task.call() );
+
+ cache.lastRequest = request;
+ cache.lastResult = resultHolder;
+ return resultHolder;
}
private String getBasePath() {
@@ -345,7 +382,53 @@ public abstract class MapperTestBase {
}
}
- private boolean isJdk6() {
- return ( Integer.parseInt( System.getProperty( "java.version" ).split( "\\." )[1]) == 6 );
+ private static class CompilationCache {
+ private CompilationRequest lastRequest;
+ private CompilationResultHolder lastResult;
+ }
+
+ private static class CompilationResultHolder {
+ private DiagnosticCollector diagnostics;
+ private boolean compilationSuccessful;
+
+ public CompilationResultHolder(DiagnosticCollector diagnostics, boolean compilationSuccessful) {
+ this.diagnostics = diagnostics;
+ this.compilationSuccessful = compilationSuccessful;
+ }
+ }
+
+ private static class CompilationRequest {
+ private final Set> sourceClasses;
+ private final List processorOptions;
+
+ public CompilationRequest(Set> sourceClasses, List processorOptions) {
+ this.sourceClasses = sourceClasses;
+ this.processorOptions = processorOptions;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ( ( processorOptions == null ) ? 0 : processorOptions.hashCode() );
+ result = prime * result + ( ( sourceClasses == null ) ? 0 : sourceClasses.hashCode() );
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj ) {
+ return true;
+ }
+ if ( obj == null ) {
+ return false;
+ }
+ if ( getClass() != obj.getClass() ) {
+ return false;
+ }
+ CompilationRequest other = (CompilationRequest) obj;
+
+ return processorOptions.equals( other.processorOptions ) && sourceClasses.equals( other.sourceClasses );
+ }
}
}