#667 Run all processor tests with both the JDK compiler and the Eclipse compiler.

All generated sources and compilation results are kept in a new directory structure, making manual inspection easier, and also simplifying parallel test-class execution.
This commit is contained in:
Andreas Gudian 2015-10-22 20:41:21 +02:00
parent af9b54fa4f
commit aba26328ba
22 changed files with 714 additions and 304 deletions

View File

@ -64,7 +64,7 @@
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>0.21.0</version>
<version>${org.eclipse.tyco.compiler-jdt.version}</version>
</dependency>
</dependencies>
</plugin>

View File

@ -46,6 +46,7 @@
<org.apache.maven.plugins.surefire.version>2.18.1</org.apache.maven.plugins.surefire.version>
<org.apache.maven.plugins.javadoc.version>2.10.3</org.apache.maven.plugins.javadoc.version>
<org.springframework.version>4.0.3.RELEASE</org.springframework.version>
<org.eclipse.tyco.compiler-jdt.version>0.23.1</org.eclipse.tyco.compiler-jdt.version>
<add.release.arguments />
<forkCount>1</forkCount>
</properties>
@ -192,6 +193,23 @@
<version>2.9</version>
</dependency>
<!-- Plexus Eclipse Compiler -->
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>${org.eclipse.tyco.compiler-jdt.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>3.0.20</version>
</dependency>
<!-- Project modules -->
<dependency>
<groupId>${project.groupId}</groupId>

View File

@ -84,6 +84,17 @@
<artifactId>javax.inject</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<scope>test</scope>
</dependency>
<!-- plexus-container-default is a runtime-dependency of the tyco-compiler -->
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
@ -109,7 +120,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -126,11 +136,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<mapper.test.output.dir>compilation-tests_fork-${surefire.forkNumber}</mapper.test.output.dir>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -18,6 +18,10 @@
*/
package org.mapstruct.ap.internal.processor;
import static org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism.AUTO_INHERIT_FROM_CONFIG;
import static org.mapstruct.ap.internal.util.Collections.first;
import static org.mapstruct.ap.internal.util.Collections.join;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -60,10 +64,6 @@ import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.ap.internal.version.VersionInformation;
import static org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism.AUTO_INHERIT_FROM_CONFIG;
import static org.mapstruct.ap.internal.util.Collections.first;
import static org.mapstruct.ap.internal.util.Collections.join;
/**
* A {@link ModelElementProcessor} which creates a {@link Mapper} from the given
* list of {@link SourceMethod}s.
@ -173,7 +173,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
TypeElement decoratorElement = (TypeElement) typeUtils.asElement( decoratorPrism.value() );
if ( !typeUtils.isAssignable( decoratorElement.asType(), element.asType() ) ) {
messager.printMessage( element, decoratorPrism.mirror, Message.DECORATOR_NO_SUBTYPE);
messager.printMessage( element, decoratorPrism.mirror, Message.DECORATOR_NO_SUBTYPE );
}
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>( methods.size() );
@ -491,8 +491,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else if ( nameFilteredcandidates.size() > 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, reversePrism );
}
if ( resultMethod == null ) {
else {
reportErrorWhenAmbigousReverseMapping( candidates, method, reversePrism );
}
}
@ -571,8 +570,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else if ( nameFilteredcandidates.size() > 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism );
}
if ( resultMethod == null ) {
else {
reportErrorWhenAmbigousMapping( candidates, method, forwardPrism );
}
}
@ -675,7 +673,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
prism.mirror,
Message.INHERITCONFIGURATION_DUPLICATE_MATCHES,
prism.name(),
Strings.join( candidates, "(), " )
Strings.join( candidates, ", " )
);
}

View File

@ -100,11 +100,11 @@ public enum Message {
INHERITCONFIGURATION_BOTH( "Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration." ),
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
INHERITINVERSECONFIGURATION_INVALID_NAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
INHERITINVERSECONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s." ),
INHERITINVERSECONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
INHERITCONFIGURATION_DUPLICATES( "Several matching methods exist: %s(). Specify a name explicitly." ),
INHERITCONFIGURATION_INVALIDNAME( "None of the candidates %s() matches given name: \"%s\"." ),
INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s()." ),
INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s." ),
INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ),
INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: %s." ),
INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" );
@ -113,12 +113,12 @@ public enum Message {
private final String description;
private final Diagnostic.Kind kind;
private Message(String description) {
Message(String description) {
this.description = description;
this.kind = Diagnostic.Kind.ERROR;
}
private Message(String description, Diagnostic.Kind kind) {
Message(String description, Diagnostic.Kind kind) {
this.description = description;
this.kind = kind;
}

View File

@ -18,6 +18,8 @@
*/
package org.mapstruct.ap.test.defaultvalue;
import java.text.ParseException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
@ -27,8 +29,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import java.text.ParseException;
import static org.fest.assertions.Assertions.assertThat;
@IssueKey( "600" )
@ -136,6 +136,10 @@ public class DefaultValueTest {
line = 33,
messageRegExp = "Constant and default value are both defined in @Mapping,"
+ " either define a defaultValue or a constant." ),
@Diagnostic(type = ErroneousMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 33,
messageRegExp = "Can't map property \".*Region region\" to \".*String region\"\\. Consider")
}
)
public void errorOnDefaultValueAndConstant() throws ParseException {
@ -154,6 +158,10 @@ public class DefaultValueTest {
line = 33,
messageRegExp = "Expression and default value are both defined in @Mapping,"
+ " either define a defaultValue or an expression." ),
@Diagnostic(type = ErroneousMapper2.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 33,
messageRegExp = "Can't map property \".*Region region\" to \".*String region\"\\. Consider")
}
)
public void errorOnDefaultValueAndExpression() throws ParseException {

View File

@ -19,6 +19,7 @@
package org.mapstruct.ap.test.erroneous.annotationnotfound;
import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
@ -45,9 +46,7 @@ public class AnnotationNotFoundTest {
@Diagnostic( type = ErroneousMapper.class,
kind = Kind.ERROR,
line = 30,
messageRegExp = "cannot find symbol\n"
+ " symbol: class NotFoundAnnotation\n"
+ " location: interface org.mapstruct.ap.test.erroneous.annotationnotfound.ErroneousMapper" )
messageRegExp = "NotFoundAnnotation")
}
)
public void shouldFailToGenerateMappings() {

View File

@ -108,8 +108,8 @@ public class InheritInverseConfigurationTest {
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.ERROR,
line = 50,
messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward\\(.*\\), "
+ ".*forward\\(.*\\)"),
messageRegExp = "Given name \"forward\" matches several candidate methods: .*forward\\(.+\\), "
+ ".*forward\\(.+\\)"),
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.WARNING,
line = 55,

View File

@ -18,7 +18,6 @@
*/
package org.mapstruct.ap.test.severalsources;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic.Kind;
import org.junit.Before;
@ -145,7 +144,7 @@ public class SeveralSourceParametersTest {
assertThat( deliveryAddress.getZipCode() ).isEqualTo( 12345 );
assertThat( deliveryAddress.getHouseNumber() ).isEqualTo( 42 );
assertThat( deliveryAddress.getDescription() ).isEqualTo( "An actor" );
assertThat( deliveryAddress.getStreet()).isEqualTo( "Main street" );
assertThat( deliveryAddress.getStreet() ).isEqualTo( "Main street" );
}
@Test
@ -154,21 +153,18 @@ public class SeveralSourceParametersTest {
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousSourceTargetMapper.class,
kind = Kind.ERROR,
line = 29,
messageRegExp = "Several possible source properties for target property \"description\".",
javaVersions = { SourceVersion.RELEASE_6 } ),
@Diagnostic(type = ErroneousSourceTargetMapper.class,
kind = Kind.ERROR,
line = 29,
messageRegExp = "Several possible source properties for target property \"zipCode\".",
javaVersions = { SourceVersion.RELEASE_6 } ),
@Diagnostic(type = ErroneousSourceTargetMapper.class,
kind = Kind.ERROR,
line = 29,
messageRegExp = "Several possible source properties for target property \"street\"."),
@Diagnostic(type = ErroneousSourceTargetMapper.class,
kind = Kind.ERROR,
line = 29,
messageRegExp = "Several possible source properties for target property \"zipCode\"."),
@Diagnostic(type = ErroneousSourceTargetMapper.class,
kind = Kind.ERROR,
line = 29,
messageRegExp = "Several possible source properties for target property \"description\".")
})
public void shouldFailToGenerateMappingsForAmbigiousSourceProperty() {
}

View File

@ -53,7 +53,7 @@ public class InheritConfigurationTest {
assertThat( createdTarget ).isNotNull();
assertThat( createdTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( createdTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested");
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested" );
assertThat( createdTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( createdTarget.getConstantProp() ).isEqualTo( "constant" );
@ -80,7 +80,7 @@ public class InheritConfigurationTest {
assertThat( createdTarget ).isNotNull();
assertThat( createdTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( createdTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested");
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested" );
assertThat( createdTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( createdTarget.getConstantProp() ).isEqualTo( "constant" );
@ -108,7 +108,7 @@ public class InheritConfigurationTest {
assertThat( createdTarget ).isNotNull();
assertThat( createdTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( createdTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested");
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested" );
assertThat( createdTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( createdTarget.getConstantProp() ).isEqualTo( "constant" );
@ -172,7 +172,7 @@ public class InheritConfigurationTest {
kind = Kind.ERROR,
line = 54,
messageRegExp = "Given name \"forwardCreate\" matches several candidate methods: "
+ ".*forwardCreate.*\\(\\), .*forwardCreate.*\\(\\)"),
+ ".*forwardCreate.*, .*forwardCreate.*"),
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.WARNING,
line = 55,

View File

@ -18,7 +18,6 @@
*/
package org.mapstruct.ap.testutil.compilation.annotation;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic.Kind;
/**
@ -58,11 +57,4 @@ public @interface Diagnostic {
* diagnostic.
*/
String messageRegExp() default ".*";
/**
* The java version for which this this diagnostic is valid.
*
* @return versions for which this Diagnostic should be evaluated. Default it evaluates for all
*/
SourceVersion[] javaVersions() default { };
}

View File

@ -21,11 +21,13 @@ package org.mapstruct.ap.testutil.compilation.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import org.codehaus.plexus.compiler.CompilerMessage;
import org.codehaus.plexus.compiler.CompilerResult;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
@ -57,27 +59,13 @@ public class CompilationOutcomeDescriptor {
List<DiagnosticDescriptor> diagnosticDescriptors = new ArrayList<DiagnosticDescriptor>();
for ( org.mapstruct.ap.testutil.compilation.annotation.Diagnostic diagnostic :
expectedCompilationResult.diagnostics() ) {
if ( requiresEvaluation( diagnostic.javaVersions() ) ) {
diagnosticDescriptors.add( DiagnosticDescriptor.forDiagnostic( diagnostic ) );
}
diagnosticDescriptors.add( DiagnosticDescriptor.forDiagnostic( diagnostic ) );
}
return new CompilationOutcomeDescriptor( expectedCompilationResult.value(), diagnosticDescriptors );
}
}
private static boolean requiresEvaluation(SourceVersion[] sourceVersions) {
if ( sourceVersions.length == 0 ) {
return true;
}
for ( SourceVersion sourceVersion : sourceVersions ) {
if ( SourceVersion.latestSupported().equals( sourceVersion ) ) {
return true;
}
}
return false;
}
public static CompilationOutcomeDescriptor forResult(String sourceDir, boolean compilationSuccessful,
List<Diagnostic<? extends JavaFileObject>> diagnostics) {
CompilationResult compilationResult =
@ -94,6 +82,21 @@ public class CompilationOutcomeDescriptor {
return new CompilationOutcomeDescriptor( compilationResult, diagnosticDescriptors );
}
public static CompilationOutcomeDescriptor forResult(String sourceDir, CompilerResult compilerResult) {
CompilationResult compilationResult =
compilerResult.isSuccess() ? CompilationResult.SUCCEEDED : CompilationResult.FAILED;
List<DiagnosticDescriptor> diagnosticDescriptors = new ArrayList<DiagnosticDescriptor>();
for ( CompilerMessage message : compilerResult.getCompilerMessages() ) {
if ( message.getKind() != CompilerMessage.Kind.NOTE ) {
diagnosticDescriptors.add( DiagnosticDescriptor.forCompilerMessage( sourceDir, message ) );
}
}
return new CompilationOutcomeDescriptor( compilationResult, diagnosticDescriptors );
}
public CompilationResult getCompilationResult() {
return compilationResult;
}

View File

@ -22,9 +22,11 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import org.codehaus.plexus.compiler.CompilerMessage;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
/**
@ -68,6 +70,34 @@ public class DiagnosticDescriptor {
);
}
public static DiagnosticDescriptor forCompilerMessage(String sourceDir, CompilerMessage compilerMessage) {
String[] lines = compilerMessage.getMessage().split( System.lineSeparator() );
String message = lines[3];
return new DiagnosticDescriptor(
removeSourceDirPrefix( sourceDir, compilerMessage.getFile() ),
toJavaxKind( compilerMessage.getKind() ),
Long.valueOf( compilerMessage.getStartLine() ),
message );
}
private static Kind toJavaxKind(CompilerMessage.Kind kind) {
switch ( kind ) {
case ERROR:
return Kind.ERROR;
case MANDATORY_WARNING:
return Kind.MANDATORY_WARNING;
case NOTE:
return Kind.NOTE;
case OTHER:
return Kind.OTHER;
case WARNING:
return Kind.WARNING;
default:
return null;
}
}
private static String getSourceName(String sourceDir, javax.tools.Diagnostic<? extends JavaFileObject> diagnostic) {
if ( diagnostic.getSource() == null ) {
return null;
@ -77,15 +107,19 @@ public class DiagnosticDescriptor {
try {
String sourceName = new File( uri ).getCanonicalPath();
return sourceName.length() > sourceDir.length() ?
sourceName.substring( sourceDir.length() + 1 ) :
sourceName;
return removeSourceDirPrefix( sourceDir, sourceName );
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
}
private static String removeSourceDirPrefix(String sourceDir, String sourceName) {
return sourceName.length() > sourceDir.length() ?
sourceName.substring( sourceDir.length() + 1 ) :
sourceName;
}
private static URI getUri(javax.tools.Diagnostic<? extends JavaFileObject> diagnostic) {
URI uri = diagnostic.getSource().toUri();

View File

@ -18,12 +18,14 @@
*/
package org.mapstruct.ap.testutil.runner;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
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.junit.runners.ParentRunner;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
@ -31,7 +33,7 @@ import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
/**
* A JUnit4 runner for Annotation Processor tests.
* <p>
* Test classes and test methods are safe to be executed in parallel.
* Test classes are safe to be executed in parallel, the methods must not executed in parallel.
* <p>
* The classes to be compiled for a given test method must be specified via {@link WithClasses}. In addition the
* following things can be configured optionally :
@ -44,11 +46,8 @@ import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
* @author Gunnar Morling
* @author Andreas Gudian
*/
public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
static final ModifiableURLClassLoader TEST_CLASS_LOADER = new ModifiableURLClassLoader();
private final Class<?> klass;
private Class<?> klassToUse;
private ReplacableTestClass replacableTestClass;
public class AnnotationProcessorTestRunner extends ParentRunner<Runner> {
private final List<Runner> runners;
/**
* @param klass the test class
@ -57,72 +56,24 @@ public class AnnotationProcessorTestRunner extends BlockJUnit4ClassRunner {
*/
public AnnotationProcessorTestRunner(Class<?> klass) throws Exception {
super( klass );
this.klass = klass;
}
/**
* newly loads the class with the test class loader and sets that loader as context class loader of the thread
*
* @param klass the class to replace
*
* @return the class loaded with the test class loader
*/
private static Class<?> replaceClassLoaderAndClass(Class<?> klass) {
replaceContextClassLoader( klass );
try {
return Thread.currentThread().getContextClassLoader().loadClass( klass.getName() );
}
catch ( ClassNotFoundException e ) {
throw new RuntimeException( e );
}
}
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 );
runners = Arrays.<Runner> asList(
new InnerAnnotationProcessorRunner( klass, Compiler.JDK ),
new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE ) );
}
@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 );
}
protected List<Runner> getChildren() {
return runners;
}
@Override
protected Statement methodBlock(FrameworkMethod method) {
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;
protected Description describeChild(Runner child) {
return child.getDescription();
}
@Override
protected void runChild(Runner child, RunNotifier notifier) {
child.run( notifier );
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright 2012-2015 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 org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
class CompilationCache {
private String lastSourceOutputDir;
private CompilationRequest lastRequest;
private CompilationOutcomeDescriptor lastResult;
public String getLastSourceOutputDir() {
return lastSourceOutputDir;
}
public void setLastSourceOutputDir(String lastSourceOutputDir) {
this.lastSourceOutputDir = lastSourceOutputDir;
}
public CompilationRequest getLastRequest() {
return lastRequest;
}
public void update(CompilationRequest lastRequest, CompilationOutcomeDescriptor lastResult) {
this.lastRequest = lastRequest;
this.lastResult = lastResult;
}
public CompilationOutcomeDescriptor getLastResult() {
return lastResult;
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright 2012-2015 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.util.List;
import java.util.Set;
/**
* Represents a compilation task for a number of sources with given processor options.
*/
class CompilationRequest {
private final Set<Class<?>> sourceClasses;
private final List<String> processorOptions;
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 );
}
public Set<Class<?>> getSourceClasses() {
return sourceClasses;
}
public List<String> getProcessorOptions() {
return processorOptions;
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright 2012-2015 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;
/**
* @author Andreas Gudian
*
*/
public enum Compiler {
JDK, ECLIPSE;
}

View File

@ -31,19 +31,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.mapstruct.ap.MappingProcessor;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
@ -67,58 +57,34 @@ import static org.fest.assertions.Assertions.assertThat;
*
* @author Andreas Gudian
*/
class CompilingStatement extends Statement {
abstract class CompilingStatement extends Statement {
/**
* Property to specify the sub-directory below /target/ where the generated files are placed
*/
public static final String MAPPER_TEST_OUTPUT_DIR_PROPERTY = "mapper.test.output.dir";
private static final String TARGET_COMPILATION_TESTS = "/target/"
+ System.getProperty( MAPPER_TEST_OUTPUT_DIR_PROPERTY, "compilation-tests" ) + "_thread-";
private static final String SOURCE_DIR = getBasePath() + "/src/test/java";
private static final String TARGET_COMPILATION_TESTS = "/target/compilation-tests/";
private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
private static final DiagnosticDescriptorComparator COMPARATOR = new DiagnosticDescriptorComparator();
private static final ThreadLocal<Integer> THREAD_NUMBER = new ThreadLocal<Integer>() {
private final AtomicInteger nextThreadId = new AtomicInteger( 0 );
protected static final String SOURCE_DIR = getBasePath() + "/src/test/java";
@Override
protected Integer initialValue() {
return nextThreadId.getAndIncrement();
}
};
protected static final List<String> COMPILER_CLASSPATH = buildCompilerClasspath();
/**
* Caches the outcome of given compilations. That way we avoid the repeated compilation of the same source files for
* several test methods of one test class.
*/
private static final ThreadLocal<CompilationCache> COMPILATION_CACHE = new ThreadLocal<CompilationCache>() {
@Override
protected CompilationCache initialValue() {
return new CompilationCache();
}
};
private static final List<File> COMPILER_CLASSPATH = buildCompilerClasspath();
private Statement next;
private final FrameworkMethod method;
private final CompilationCache compilationCache;
private Statement next;
private JavaCompiler compiler;
private String classOutputDir;
private String sourceOutputDir;
private CompilationRequest compilationRequest;
public CompilingStatement(FrameworkMethod method) {
CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
this.method = method;
this.compilationCache = compilationCache;
this.compilationRequest = new CompilationRequest( getTestClasses(), getProcessorOptions() );
}
public void setNextStatement(Statement next) {
void setNextStatement(Statement next) {
this.next = next;
}
@ -126,29 +92,33 @@ class CompilingStatement extends Statement {
public void evaluate() throws Throwable {
generateMapperImplementation();
GeneratedSource.setCompilingStatement( this );
next.evaluate();
GeneratedSource.clearCompilingStatement();
}
static String getSourceOutputDir() {
return COMPILATION_CACHE.get().lastSourceOutputDir;
String getSourceOutputDir() {
return compilationCache.getLastSourceOutputDir();
}
protected void setupCompiler() throws Exception {
compiler = ToolProvider.getSystemJavaCompiler();
protected void setupDirectories() throws Exception {
String compilationRoot = getBasePath()
+ TARGET_COMPILATION_TESTS
+ method.getDeclaringClass().getName()
+ "/" + method.getName()
+ getPathSuffix();
String basePath = getBasePath();
Integer i = THREAD_NUMBER.get();
classOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/classes";
sourceOutputDir = basePath + TARGET_COMPILATION_TESTS + i + "/generated-sources/mapping";
classOutputDir = compilationRoot + "/classes";
sourceOutputDir = compilationRoot + "/generated-sources";
createOutputDirs();
( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).addOutputDir( classOutputDir );
}
private static List<File> buildCompilerClasspath() {
protected abstract String getPathSuffix();
private static List<String> buildCompilerClasspath() {
String[] bootClasspath =
System.getProperty( "java.class.path" ).split( System.getProperty( "path.separator" ) );
String fs = System.getProperty( "file.separator" );
@ -166,10 +136,10 @@ class CompilingStatement extends Statement {
"spring-context",
"joda-time" };
List<File> classpath = new ArrayList<File>();
List<String> classpath = new ArrayList<String>();
for ( String path : bootClasspath ) {
if ( !path.contains( testClasses ) && isWhitelisted( path, whitelist ) ) {
classpath.add( new File( path ) );
classpath.add( path );
}
}
@ -186,14 +156,8 @@ class CompilingStatement extends Statement {
}
protected void generateMapperImplementation() throws Exception {
CompilationResultHolder compilationResult = compile();
CompilationOutcomeDescriptor actualResult = compile();
CompilationOutcomeDescriptor actualResult =
CompilationOutcomeDescriptor.forResult(
SOURCE_DIR,
compilationResult.compilationSuccessful,
compilationResult.diagnostics.getDiagnostics()
);
CompilationOutcomeDescriptor expectedResult =
CompilationOutcomeDescriptor.forExpectedCompilationResult(
method.getAnnotation( ExpectedCompilationOutcome.class )
@ -201,7 +165,7 @@ class CompilingStatement extends Statement {
if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) {
assertThat( actualResult.getCompilationResult() ).describedAs(
"Compilation failed. Diagnostics: " + compilationResult.diagnostics.getDiagnostics()
"Compilation failed. Diagnostics: " + actualResult.getDiagnostics()
).isEqualTo(
CompilationResult.SUCCEEDED
);
@ -263,6 +227,7 @@ class CompilingStatement extends Statement {
Collections.sort( actualDiagnostics, COMPARATOR );
Collections.sort( expectedDiagnostics, COMPARATOR );
expectedDiagnostics = filterExpectedDiagnostics( expectedDiagnostics );
Iterator<DiagnosticDescriptor> actualIterator = actualDiagnostics.iterator();
Iterator<DiagnosticDescriptor> expectedIterator = expectedDiagnostics.iterator();
@ -299,10 +264,18 @@ class CompilingStatement extends Statement {
actual.getLine(),
actual.getKind()
)
).matches( ".*" + expected.getMessage() + ".*" );
).matches( "(?ms).*" + expected.getMessage() + ".*" );
}
}
/**
* @param expectedDiagnostics expected diagnostics
* @return a possibly filtered list of expected diagnostics
*/
protected List<DiagnosticDescriptor> filterExpectedDiagnostics(List<DiagnosticDescriptor> expectedDiagnostics) {
return expectedDiagnostics;
}
/**
* Returns the classes to be compiled for this test.
*
@ -371,7 +344,7 @@ class CompilingStatement extends Statement {
return String.format( "-A%s=%s", processorOption.name(), processorOption.value() );
}
private Set<File> getSourceFiles(Collection<Class<?>> classes) {
protected Set<File> getSourceFiles(Collection<Class<?>> classes) {
Set<File> sourceFiles = new HashSet<File>( classes.size() );
for ( Class<?> clazz : classes ) {
@ -386,52 +359,30 @@ class CompilingStatement extends Statement {
return sourceFiles;
}
private CompilationResultHolder compile()
private CompilationOutcomeDescriptor compile()
throws Exception {
CompilationCache cache = COMPILATION_CACHE.get();
if ( !needsRecompilation() ) {
return cache.lastResult;
return compilationCache.getLastResult();
}
setupCompiler();
cache.lastSourceOutputDir = sourceOutputDir;
setupDirectories();
compilationCache.setLastSourceOutputDir( sourceOutputDir );
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
CompilationOutcomeDescriptor resultHolder =
compileWithSpecificCompiler( compilationRequest, sourceOutputDir, classOutputDir );
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.sourceClasses ) );
try {
fileManager.setLocation( StandardLocation.CLASS_PATH, COMPILER_CLASSPATH );
fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) );
fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) );
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
CompilationTask task =
compiler.getTask(
null,
fileManager,
diagnostics,
compilationRequest.processorOptions,
null,
compilationUnits );
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
CompilationResultHolder resultHolder = new CompilationResultHolder( diagnostics, task.call() );
cache.lastRequest = compilationRequest;
cache.lastResult = resultHolder;
compilationCache.update( compilationRequest, resultHolder );
return resultHolder;
}
public boolean needsRecompilation() {
return !compilationRequest.equals( COMPILATION_CACHE.get().lastRequest );
protected abstract CompilationOutcomeDescriptor compileWithSpecificCompiler(
CompilationRequest compilationRequest,
String sourceOutputDir,
String classOutputDir);
boolean needsRecompilation() {
return !compilationRequest.equals( compilationCache.getLastRequest() );
}
private static String getBasePath() {
@ -485,66 +436,7 @@ class CompilingStatement extends Statement {
return result;
}
// Using the message is not perfect when using regular expressions,
// but it's better than nothing
return o1.getMessage().compareTo( o2.getMessage() );
}
}
private static class CompilationCache {
private String lastSourceOutputDir;
private CompilationRequest lastRequest;
private CompilationResultHolder lastResult;
}
/**
* Represents the result of a compilation.
*/
private static class CompilationResultHolder {
private final DiagnosticCollector<JavaFileObject> diagnostics;
private final boolean compilationSuccessful;
public CompilationResultHolder(DiagnosticCollector<JavaFileObject> diagnostics, boolean compilationSuccessful) {
this.diagnostics = diagnostics;
this.compilationSuccessful = compilationSuccessful;
}
}
/**
* Represents a compilation task for a number of sources with given processor options.
*/
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 );
return o1.getKind().compareTo( o2.getKind() );
}
}
}

View File

@ -0,0 +1,82 @@
/**
* Copyright 2012-2015 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.io.File;
import org.codehaus.plexus.compiler.CompilerConfiguration;
import org.codehaus.plexus.compiler.CompilerException;
import org.codehaus.plexus.compiler.CompilerResult;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.eclipse.tycho.compiler.jdt.JDTCompiler;
import org.junit.runners.model.FrameworkMethod;
import org.mapstruct.ap.MappingProcessor;
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
/**
* Statement that uses the JDK compiler to compile.
*
* @author Andreas Gudian
*/
class EclipseCompilingStatement extends CompilingStatement {
EclipseCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
super( method, compilationCache );
}
@Override
protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRequest compilationRequest,
String sourceOutputDir,
String classOutputDir) {
JDTCompiler compiler = new JDTCompiler();
compiler.enableLogging( new ConsoleLogger( 5, "JDT-Compiler" ) );
CompilerConfiguration config = new CompilerConfiguration();
config.setClasspathEntries( COMPILER_CLASSPATH );
config.setOutputLocation( classOutputDir );
config.setGeneratedSourcesDirectory( new File( sourceOutputDir ) );
config.setAnnotationProcessors( new String[] { MappingProcessor.class.getName() } );
config.setSourceFiles( getSourceFiles( compilationRequest.getSourceClasses() ) );
config.setShowWarnings( false );
config.setSourceVersion( "1.6" );
config.setTargetVersion( "1.6" );
for ( String option : compilationRequest.getProcessorOptions() ) {
config.addCompilerCustomArgument( option, null );
}
CompilerResult compilerResult;
try {
compilerResult = compiler.performCompile( config );
}
catch ( CompilerException e ) {
throw new RuntimeException( e );
}
return CompilationOutcomeDescriptor.forResult(
SOURCE_DIR,
compilerResult );
}
@Override
protected String getPathSuffix() {
return "_eclipse";
}
}

View File

@ -39,11 +39,21 @@ import org.mapstruct.ap.testutil.assertions.JavaFileAssert;
*/
public class GeneratedSource implements TestRule {
private static ThreadLocal<CompilingStatement> compilingStatement = new ThreadLocal<CompilingStatement>();;
@Override
public Statement apply(Statement base, Description description) {
return base;
}
static void setCompilingStatement(CompilingStatement compilingStatement) {
GeneratedSource.compilingStatement.set( compilingStatement );
}
static void clearCompilingStatement() {
GeneratedSource.compilingStatement.remove();
}
/**
* @param mapperClass the class annotated with {@code &#064;Mapper}
*
@ -60,6 +70,6 @@ public class GeneratedSource implements TestRule {
* @return an assert for the file specified by the given path
*/
public JavaFileAssert forJavaFile(String path) {
return new JavaFileAssert( new File( CompilingStatement.getSourceOutputDir() + "/" + path ) );
return new JavaFileAssert( new File( compilingStatement.get().getSourceOutputDir() + "/" + path ) );
}
}

View File

@ -0,0 +1,150 @@
/**
* Copyright 2012-2015 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.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;
/**
* A JUnit4 runner for Annotation Processor tests.
* <p>
* Test classes and test methods are safe to be executed in parallel.
* <p>
* The classes to be compiled for a given test method must be specified via {@link WithClasses}. In addition the
* following things can be configured optionally :
* <ul>
* <li>Processor options to be considered during compilation via {@link ProcessorOption}.</li>
* <li>The expected compilation outcome and expected diagnostics can be specified via {@link ExpectedCompilationOutcome}
* . If no outcome is specified, a successful compilation is assumed.</li>
* </ul>
*
* @author Gunnar Morling
* @author Andreas Gudian
*/
class InnerAnnotationProcessorRunner extends BlockJUnit4ClassRunner {
static final ModifiableURLClassLoader TEST_CLASS_LOADER = new ModifiableURLClassLoader();
private final Class<?> klass;
private final Compiler compiler;
private final CompilationCache compilationCache;
private Class<?> klassToUse;
private ReplacableTestClass replacableTestClass;
/**
* @param klass the test class
*
* @throws Exception see {@link BlockJUnit4ClassRunner#BlockJUnit4ClassRunner(Class)}
*/
InnerAnnotationProcessorRunner(Class<?> klass, Compiler compiler) throws Exception {
super( klass );
this.klass = klass;
this.compiler = compiler;
this.compilationCache = new CompilationCache();
}
/**
* newly loads the class with the test class loader and sets that loader as context class loader of the thread
*
* @param klass the class to replace
*
* @return the class loaded with the test class loader
*/
private static Class<?> replaceClassLoaderAndClass(Class<?> klass) {
replaceContextClassLoader( klass );
try {
return Thread.currentThread().getContextClassLoader().loadClass( klass.getName() );
}
catch ( ClassNotFoundException e ) {
throw new RuntimeException( e );
}
}
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) {
CompilingStatement statement = createCompilingStatement( method );
if ( statement.needsRecompilation() ) {
klassToUse = replaceClassLoaderAndClass( klass );
replacableTestClass.replaceClass( klassToUse );
}
method = replaceFrameworkMethod( method );
Statement next = super.methodBlock( method );
statement.setNextStatement( next );
return statement;
}
private CompilingStatement createCompilingStatement(FrameworkMethod method) {
if ( compiler == Compiler.JDK ) {
return new JdkCompilingStatement( method, compilationCache );
}
else {
return new EclipseCompilingStatement( method, compilationCache );
}
}
@Override
protected String getName() {
return "[" + compiler.name().toLowerCase() + "]";
}
@Override
protected String testName(FrameworkMethod method) {
return method.getName() + getName();
}
}

View File

@ -0,0 +1,129 @@
/**
* Copyright 2012-2015 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.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.junit.runners.model.FrameworkMethod;
import org.mapstruct.ap.MappingProcessor;
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
/**
* Statement that uses the JDK compiler to compile.
*
* @author Andreas Gudian
*/
class JdkCompilingStatement extends CompilingStatement {
private static final List<File> COMPILER_CLASSPATH_FILES = asFiles( COMPILER_CLASSPATH );
JdkCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
super( method, compilationCache );
}
@Override
protected CompilationOutcomeDescriptor compileWithSpecificCompiler(CompilationRequest compilationRequest,
String sourceOutputDir,
String classOutputDir) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromFiles( getSourceFiles( compilationRequest.getSourceClasses() ) );
try {
fileManager.setLocation( StandardLocation.CLASS_PATH, COMPILER_CLASSPATH_FILES );
fileManager.setLocation( StandardLocation.CLASS_OUTPUT, Arrays.asList( new File( classOutputDir ) ) );
fileManager.setLocation( StandardLocation.SOURCE_OUTPUT, Arrays.asList( new File( sourceOutputDir ) ) );
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
CompilationTask task =
compiler.getTask(
null,
fileManager,
diagnostics,
compilationRequest.getProcessorOptions(),
null,
compilationUnits );
task.setProcessors( Arrays.asList( new MappingProcessor() ) );
Boolean compilationSuccessful = task.call();
return CompilationOutcomeDescriptor.forResult(
SOURCE_DIR,
compilationSuccessful,
diagnostics.getDiagnostics() );
}
private static List<File> asFiles(List<String> paths) {
List<File> classpath = new ArrayList<File>();
for ( String path : paths ) {
classpath.add( new File( path ) );
}
return classpath;
}
/**
* The JDK compiler only reports the first message of kind ERROR that is reported for one source file line, so we
* filter out the surplus diagnostics. The input list is already sorted by file name and line number, with the order
* for the diagnostics in the same line being kept at the order as given in the test.
*/
@Override
protected List<DiagnosticDescriptor> filterExpectedDiagnostics(List<DiagnosticDescriptor> expectedDiagnostics) {
List<DiagnosticDescriptor> filtered = new ArrayList<DiagnosticDescriptor>( expectedDiagnostics.size() );
DiagnosticDescriptor previous = null;
for ( DiagnosticDescriptor diag : expectedDiagnostics ) {
if ( diag.getKind() != Kind.ERROR
|| previous == null
|| !previous.getSourceFileName().equals( diag.getSourceFileName() )
|| !previous.getLine().equals( diag.getLine() ) ) {
filtered.add( diag );
previous = diag;
}
}
return filtered;
}
@Override
protected String getPathSuffix() {
return "_jdk";
}
}