mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#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:
parent
af9b54fa4f
commit
aba26328ba
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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, ", " )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 { };
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
@ -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 @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 ) );
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user