mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#132 speed up tests by avoiding unneccessary compilations, allow parallel executions (with -DforkCount > 1 or with -Dparallel=classes -DthreadCount > 1)
This commit is contained in:
parent
da1050ab34
commit
25849adfee
@ -44,6 +44,8 @@
|
|||||||
<com.jolira.hickory.version>1.0.0</com.jolira.hickory.version>
|
<com.jolira.hickory.version>1.0.0</com.jolira.hickory.version>
|
||||||
<org.apache.maven.plugins.enforcer.version>1.2</org.apache.maven.plugins.enforcer.version>
|
<org.apache.maven.plugins.enforcer.version>1.2</org.apache.maven.plugins.enforcer.version>
|
||||||
<org.springframework.version>3.2.3.RELEASE</org.springframework.version>
|
<org.springframework.version>3.2.3.RELEASE</org.springframework.version>
|
||||||
|
|
||||||
|
<forkCount>1</forkCount>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -299,6 +301,9 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.17</version>
|
<version>2.17</version>
|
||||||
|
<configuration>
|
||||||
|
<forkCount>${forkCount}</forkCount>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.mycila.maven-license-plugin</groupId>
|
<groupId>com.mycila.maven-license-plugin</groupId>
|
||||||
|
@ -83,6 +83,15 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<mapper.test.output.dir>compilation-tests-${surefire.forkNumber}</mapper.test.output.dir>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
@ -33,7 +33,10 @@ import java.util.Comparator;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.tools.DiagnosticCollector;
|
import javax.tools.DiagnosticCollector;
|
||||||
import javax.tools.JavaCompiler;
|
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.annotation.ProcessorOption;
|
||||||
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
|
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
|
||||||
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
|
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
|
||||||
import org.testng.annotations.BeforeClass;
|
|
||||||
import org.testng.annotations.BeforeMethod;
|
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 String LINE_SEPARATOR = System.getProperty( "line.separator" );
|
||||||
private static final DiagnosticDescriptorComparator COMPARATOR = new DiagnosticDescriptorComparator();
|
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<Integer, Integer> threadsWithEnhancedClassloader = new ConcurrentHashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
private static ThreadLocal<Integer> threadNumber = new ThreadLocal<Integer>() {
|
||||||
|
private AtomicInteger highWaterMark = new AtomicInteger( 0 );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer initialValue() {
|
||||||
|
return highWaterMark.getAndIncrement();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ThreadLocal<CompilationCache> compilationCache = new ThreadLocal<CompilationCache>() {
|
||||||
|
@Override
|
||||||
|
protected CompilationCache initialValue() {
|
||||||
|
return new CompilationCache();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private JavaCompiler compiler;
|
private JavaCompiler compiler;
|
||||||
private String sourceDir;
|
private String sourceDir;
|
||||||
@ -80,21 +103,21 @@ public abstract class MapperTestBase {
|
|||||||
private String sourceOutputDir;
|
private String sourceOutputDir;
|
||||||
private List<File> classPath;
|
private List<File> classPath;
|
||||||
private final List<String> libraries;
|
private final List<String> libraries;
|
||||||
private DiagnosticCollector<JavaFileObject> diagnostics;
|
|
||||||
|
|
||||||
public MapperTestBase() {
|
public MapperTestBase() {
|
||||||
this.libraries = Arrays.asList( "mapstruct.jar", "guava.jar", "javax.inject.jar" );
|
this.libraries = Arrays.asList( "mapstruct.jar", "guava.jar", "javax.inject.jar" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeClass
|
protected void setupCompiler() throws Exception {
|
||||||
public void setup() throws Exception {
|
|
||||||
compiler = ToolProvider.getSystemJavaCompiler();
|
compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
|
|
||||||
String basePath = getBasePath();
|
String basePath = getBasePath();
|
||||||
|
|
||||||
|
Integer i = threadNumber.get();
|
||||||
|
|
||||||
sourceDir = basePath + "/src/test/java";
|
sourceDir = basePath + "/src/test/java";
|
||||||
classOutputDir = basePath + "/target/compilation-tests/classes";
|
classOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/classes";
|
||||||
sourceOutputDir = basePath + "/target/compilation-tests/generated-sources/mapping";
|
sourceOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/generated-sources/mapping";
|
||||||
|
|
||||||
String testDependenciesDir = basePath + "/target/test-dependencies/";
|
String testDependenciesDir = basePath + "/target/test-dependencies/";
|
||||||
|
|
||||||
@ -106,7 +129,7 @@ public abstract class MapperTestBase {
|
|||||||
createOutputDirs();
|
createOutputDirs();
|
||||||
|
|
||||||
// TODO #140 Is there a better way to do this?
|
// 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
|
// 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.
|
// been loaded already. Otherwise some tests won't work.
|
||||||
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||||
@ -114,22 +137,20 @@ public abstract class MapperTestBase {
|
|||||||
Method method = clazz.getDeclaredMethod( "addURL", new Class[] { URL.class } );
|
Method method = clazz.getDeclaredMethod( "addURL", new Class[] { URL.class } );
|
||||||
method.setAccessible( true );
|
method.setAccessible( true );
|
||||||
method.invoke( classLoader, new File( classOutputDir ).toURI().toURL() );
|
method.invoke( classLoader, new File( classOutputDir ).toURI().toURL() );
|
||||||
enhancedClassloader = true;
|
|
||||||
|
threadsWithEnhancedClassloader.put( i, i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
public void generateMapperImplementation(Method testMethod) {
|
public void generateMapperImplementation(Method testMethod) throws Exception {
|
||||||
diagnostics = new DiagnosticCollector<JavaFileObject>();
|
CompilationResultHolder compilationResult =
|
||||||
Set<File> sourceFiles = getSourceFiles( getTestClasses( testMethod ) );
|
compile( getTestClasses( testMethod ), getProcessorOptions( testMethod ) );
|
||||||
List<String> processorOptions = getProcessorOptions( testMethod );
|
|
||||||
|
|
||||||
boolean compilationSuccessful = compile( diagnostics, sourceFiles, processorOptions );
|
|
||||||
|
|
||||||
CompilationOutcomeDescriptor actualResult = CompilationOutcomeDescriptor.forResult(
|
CompilationOutcomeDescriptor actualResult = CompilationOutcomeDescriptor.forResult(
|
||||||
sourceDir,
|
sourceDir,
|
||||||
compilationSuccessful,
|
compilationResult.compilationSuccessful,
|
||||||
diagnostics.getDiagnostics()
|
compilationResult.diagnostics.getDiagnostics()
|
||||||
);
|
);
|
||||||
CompilationOutcomeDescriptor expectedResult = CompilationOutcomeDescriptor.forExpectedCompilationResult(
|
CompilationOutcomeDescriptor expectedResult = CompilationOutcomeDescriptor.forExpectedCompilationResult(
|
||||||
testMethod.getAnnotation( ExpectedCompilationOutcome.class )
|
testMethod.getAnnotation( ExpectedCompilationOutcome.class )
|
||||||
@ -137,7 +158,9 @@ public abstract class MapperTestBase {
|
|||||||
|
|
||||||
if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) {
|
if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) {
|
||||||
assertThat( actualResult.getCompilationResult() )
|
assertThat( actualResult.getCompilationResult() )
|
||||||
.describedAs( "Compilation failed. Diagnostics: " + diagnostics.getDiagnostics() )
|
.describedAs(
|
||||||
|
"Compilation failed. Diagnostics: " + compilationResult.diagnostics.getDiagnostics()
|
||||||
|
)
|
||||||
.isEqualTo( CompilationResult.SUCCEEDED );
|
.isEqualTo( CompilationResult.SUCCEEDED );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -260,11 +283,21 @@ public abstract class MapperTestBase {
|
|||||||
return sourceFiles;
|
return sourceFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean compile(DiagnosticCollector<JavaFileObject> diagnostics, Iterable<File> sourceFiles,
|
private CompilationResultHolder compile(Set<Class<?>> sourceClasses, List<String> processorOptions)
|
||||||
List<String> processorOptions) {
|
throws Exception {
|
||||||
|
CompilationRequest request = new CompilationRequest( sourceClasses, processorOptions );
|
||||||
|
|
||||||
|
CompilationCache cache = compilationCache.get();
|
||||||
|
if ( request.equals( cache.lastRequest ) ) {
|
||||||
|
return cache.lastResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCompiler();
|
||||||
|
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||||
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||||
|
|
||||||
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles( sourceFiles );
|
Iterable<? extends JavaFileObject> compilationUnits =
|
||||||
|
fileManager.getJavaFileObjectsFromFiles( getSourceFiles( sourceClasses ) );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
|
fileManager.setLocation( StandardLocation.CLASS_PATH, classPath );
|
||||||
@ -285,7 +318,11 @@ public abstract class MapperTestBase {
|
|||||||
);
|
);
|
||||||
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
|
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() {
|
private String getBasePath() {
|
||||||
@ -345,7 +382,53 @@ public abstract class MapperTestBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isJdk6() {
|
private static class CompilationCache {
|
||||||
return ( Integer.parseInt( System.getProperty( "java.version" ).split( "\\." )[1]) == 6 );
|
private CompilationRequest lastRequest;
|
||||||
|
private CompilationResultHolder lastResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompilationResultHolder {
|
||||||
|
private DiagnosticCollector<JavaFileObject> diagnostics;
|
||||||
|
private boolean compilationSuccessful;
|
||||||
|
|
||||||
|
public CompilationResultHolder(DiagnosticCollector<JavaFileObject> diagnostics, boolean compilationSuccessful) {
|
||||||
|
this.diagnostics = diagnostics;
|
||||||
|
this.compilationSuccessful = compilationSuccessful;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompilationRequest {
|
||||||
|
private final Set<Class<?>> sourceClasses;
|
||||||
|
private final List<String> processorOptions;
|
||||||
|
|
||||||
|
public CompilationRequest(Set<Class<?>> sourceClasses, List<String> 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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user