mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Migrate the processor test infrastructure from JUnit 4 to JUnit Jupiter
With JUnit Jupiter it is still not possible to set the ClassLoader for loading the test class. However, since 5.8 M1 there is a way to hook into the launcher discovery process and change the Current Thread ContextClassLoader which would load the classes with our customer ClassLoader. Once JUnit Jupiter 201 is resolved we can simplify this. The CompilationCache is stored in the GlobalCache with the CompilationRequest as key. This means that even when methods are not executed in some particular order if they have same WithClasses then they would reuse the cache.
This commit is contained in:
parent
51cdbd67e3
commit
5bbd1a78ea
@ -28,7 +28,7 @@
|
||||
<org.springframework.version>5.3.3</org.springframework.version>
|
||||
<org.eclipse.tycho.compiler-jdt.version>1.6.0</org.eclipse.tycho.compiler-jdt.version>
|
||||
<com.puppycrawl.tools.checkstyle.version>8.36.1</com.puppycrawl.tools.checkstyle.version>
|
||||
<org.junit.jupiter.version>5.7.0</org.junit.jupiter.version>
|
||||
<org.junit.jupiter.version>5.8.0-M1</org.junit.jupiter.version>
|
||||
<add.release.arguments />
|
||||
<forkCount>1</forkCount>
|
||||
<assertj.version>3.17.2</assertj.version>
|
||||
|
@ -49,8 +49,13 @@
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -104,6 +109,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- There is no compile dependency to Joda-Time; It's only required for testing the Joda conversions -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mapstruct.ap.testutil.runner.Compiler;
|
||||
import org.mapstruct.ap.testutil.runner.ProcessorTestExtension;
|
||||
|
||||
/**
|
||||
* JUnit Jupiter test template for the MapStruct Processor tests.
|
||||
* <p>
|
||||
* Test classes are safe to be executed in parallel, but test methods are not safe to be executed in parallel.
|
||||
* <p>
|
||||
* By default this template would generate tests for the JDK and Eclipse Compiler.
|
||||
* If only a single compiler is needed then specify the compiler in the value.
|
||||
* <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 org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption ProcessorOption}.</li>
|
||||
* <li>The expected compilation outcome and expected diagnostics can be specified via
|
||||
* {@link org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome ExpectedCompilationOutcome}.
|
||||
* If no outcome is specified, a successful compilation is assumed.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@TestTemplate
|
||||
@ExtendWith(ProcessorTestExtension.class)
|
||||
public @interface ProcessorTest {
|
||||
|
||||
Compiler[] value() default {
|
||||
Compiler.JDK,
|
||||
Compiler.ECLIPSE
|
||||
};
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Runner;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
import org.junit.runner.manipulation.NoTestsRemainException;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A JUnit4 runner for Annotation Processor tests.
|
||||
* <p>
|
||||
* Test classes are safe to be executed in parallel, but test methods are not 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
|
||||
*/
|
||||
public class AnnotationProcessorTestRunner extends ParentRunner<Runner> {
|
||||
|
||||
private static final boolean IS_AT_LEAST_JAVA_9 = isIsAtLeastJava9();
|
||||
|
||||
private static boolean isIsAtLeastJava9() {
|
||||
try {
|
||||
Runtime.class.getMethod( "version" );
|
||||
return true;
|
||||
}
|
||||
catch ( NoSuchMethodException e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Runner> runners;
|
||||
|
||||
/**
|
||||
* @param klass the test class
|
||||
*
|
||||
* @throws Exception see {@link BlockJUnit4ClassRunner#BlockJUnit4ClassRunner(Class)}
|
||||
*/
|
||||
public AnnotationProcessorTestRunner(Class<?> klass) throws Exception {
|
||||
super( klass );
|
||||
|
||||
runners = createRunners( klass );
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private List<Runner> createRunners(Class<?> klass) throws Exception {
|
||||
WithSingleCompiler singleCompiler = klass.getAnnotation( WithSingleCompiler.class );
|
||||
|
||||
if (singleCompiler != null) {
|
||||
return Arrays.asList( new InnerAnnotationProcessorRunner( klass, singleCompiler.value() ) );
|
||||
}
|
||||
else if ( IS_AT_LEAST_JAVA_9 ) {
|
||||
// Current tycho-compiler-jdt (0.26.0) is not compatible with Java 11
|
||||
// Updating to latest version 1.3.0 fails some tests
|
||||
// Once https://github.com/mapstruct/mapstruct/pull/1587 is resolved we can remove this line
|
||||
return Arrays.asList(
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.JDK11 ),
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE11 )
|
||||
);
|
||||
}
|
||||
|
||||
return Arrays.asList(
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.JDK ),
|
||||
new InnerAnnotationProcessorRunner( klass, Compiler.ECLIPSE )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Runner> getChildren() {
|
||||
return runners;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Description describeChild(Runner child) {
|
||||
return child.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runChild(Runner child, RunNotifier notifier) {
|
||||
child.run( notifier );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(Filter filter) throws NoTestsRemainException {
|
||||
super.filter( new FilterDecorator( filter ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to only execute selected methods, even if the executing framework is not aware of parameterized tests
|
||||
* (e.g. some versions of IntelliJ, Netbeans, Eclipse).
|
||||
*/
|
||||
private static final class FilterDecorator extends Filter {
|
||||
private final Filter delegate;
|
||||
|
||||
private FilterDecorator(Filter delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRun(Description description) {
|
||||
boolean shouldRun = delegate.shouldRun( description );
|
||||
if ( !shouldRun ) {
|
||||
return delegate.shouldRun( withoutParameterizedName( description ) );
|
||||
}
|
||||
|
||||
return shouldRun;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return delegate.describe();
|
||||
}
|
||||
|
||||
private Description withoutParameterizedName(Description description) {
|
||||
String cleanDisplayName = removeParameter( description.getDisplayName() );
|
||||
Description cleanDescription =
|
||||
Description.createSuiteDescription(
|
||||
cleanDisplayName,
|
||||
description.getAnnotations().toArray( new Annotation[description.getAnnotations().size()] ) );
|
||||
|
||||
for ( Description child : description.getChildren() ) {
|
||||
cleanDescription.addChild( withoutParameterizedName( child ) );
|
||||
}
|
||||
|
||||
return cleanDescription;
|
||||
}
|
||||
|
||||
private String removeParameter(String name) {
|
||||
if ( name.startsWith( "[" ) ) {
|
||||
return name;
|
||||
}
|
||||
|
||||
// remove "[compiler]" from "method[compiler](class)"
|
||||
int open = name.indexOf( '[' );
|
||||
int close = name.indexOf( ']' ) + 1;
|
||||
return name.substring( 0, open ) + name.substring( close );
|
||||
}
|
||||
}
|
||||
}
|
@ -13,11 +13,14 @@ import java.util.Set;
|
||||
* Represents a compilation task for a number of sources with given processor options.
|
||||
*/
|
||||
public class CompilationRequest {
|
||||
private final Compiler compiler;
|
||||
private final Set<Class<?>> sourceClasses;
|
||||
private final Map<Class<?>, Class<?>> services;
|
||||
private final List<String> processorOptions;
|
||||
|
||||
CompilationRequest(Set<Class<?>> sourceClasses, Map<Class<?>, Class<?>> services, List<String> processorOptions) {
|
||||
CompilationRequest(Compiler compiler, Set<Class<?>> sourceClasses, Map<Class<?>, Class<?>> services,
|
||||
List<String> processorOptions) {
|
||||
this.compiler = compiler;
|
||||
this.sourceClasses = sourceClasses;
|
||||
this.services = services;
|
||||
this.processorOptions = processorOptions;
|
||||
@ -27,6 +30,7 @@ public class CompilationRequest {
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( compiler == null ) ? 0 : compiler.hashCode() );
|
||||
result = prime * result + ( ( processorOptions == null ) ? 0 : processorOptions.hashCode() );
|
||||
result = prime * result + ( ( services == null ) ? 0 : services.hashCode() );
|
||||
result = prime * result + ( ( sourceClasses == null ) ? 0 : sourceClasses.hashCode() );
|
||||
@ -46,7 +50,8 @@ public class CompilationRequest {
|
||||
}
|
||||
CompilationRequest other = (CompilationRequest) obj;
|
||||
|
||||
return processorOptions.equals( other.processorOptions )
|
||||
return compiler.equals( other.compiler )
|
||||
&& processorOptions.equals( other.processorOptions )
|
||||
&& services.equals( other.services )
|
||||
&& sourceClasses.equals( other.sourceClasses );
|
||||
}
|
||||
|
@ -5,10 +5,27 @@
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
/**
|
||||
* @author Andreas Gudian
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public enum Compiler {
|
||||
JDK, JDK11, ECLIPSE, ECLIPSE11;
|
||||
JDK,
|
||||
ECLIPSE;
|
||||
|
||||
private final JRE latestSupportedJre;
|
||||
|
||||
Compiler() {
|
||||
this( JRE.OTHER );
|
||||
}
|
||||
|
||||
Compiler(JRE latestSupportedJre) {
|
||||
this.latestSupportedJre = latestSupportedJre;
|
||||
}
|
||||
|
||||
public JRE latestSupportedJre() {
|
||||
return latestSupportedJre;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import org.junit.platform.engine.UniqueId;
|
||||
import org.junit.platform.launcher.LauncherDiscoveryListener;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class CompilerLauncherDiscoveryListener implements LauncherDiscoveryListener {
|
||||
@Override
|
||||
public void engineDiscoveryStarted(UniqueId engineId) {
|
||||
// Currently JUnit Jupiter does not have an SPI for providing a ClassLoader for loading the class
|
||||
// However, we can change the current context class loaded when the engine discovery starts.
|
||||
// This would make sure that JUnit Jupiter uses our ClassLoader to correctly load the mappers
|
||||
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
FilteringParentClassLoader filteringParentClassLoader = new FilteringParentClassLoader(
|
||||
currentClassLoader,
|
||||
"org.mapstruct.ap.test."
|
||||
);
|
||||
ModifiableURLClassLoader newClassLoader = new ModifiableURLClassLoader( filteringParentClassLoader );
|
||||
newClassLoader.withOriginOf( getClass() );
|
||||
Thread.currentThread().setContextClassLoader( newClassLoader );
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
||||
import org.junit.jupiter.api.extension.ExecutionCondition;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
/**
|
||||
* Every compiler is registered with it's max supported JRE that it can run on.
|
||||
* This condition is used to check if the test for a particular compiler can be run with the current JRE.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class CompilerTestEnabledOnJreCondition implements ExecutionCondition {
|
||||
|
||||
static final ConditionEvaluationResult ENABLED_ON_CURRENT_JRE =
|
||||
ConditionEvaluationResult.enabled( "Enabled on JRE version: " + System.getProperty( "java.version" ) );
|
||||
|
||||
static final ConditionEvaluationResult DISABLED_ON_CURRENT_JRE =
|
||||
ConditionEvaluationResult.disabled( "Disabled on JRE version: " + System.getProperty( "java.version" ) );
|
||||
|
||||
protected final Compiler compiler;
|
||||
|
||||
public CompilerTestEnabledOnJreCondition(Compiler compiler) {
|
||||
this.compiler = compiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
|
||||
// If the max JRE is greater or equal to the current version the test is enabled
|
||||
return compiler.latestSupportedJre().compareTo( JRE.currentVersion() ) >= 0 ? ENABLED_ON_CURRENT_JRE :
|
||||
DISABLED_ON_CURRENT_JRE;
|
||||
}
|
||||
}
|
@ -5,16 +5,14 @@
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -26,34 +24,38 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.Checker;
|
||||
import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
|
||||
import com.puppycrawl.tools.checkstyle.DefaultLogger;
|
||||
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
|
||||
import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.WithServiceImplementation;
|
||||
import org.mapstruct.ap.testutil.WithServiceImplementations;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.DisableCheckstyle;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedNote;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOptions;
|
||||
import org.mapstruct.ap.testutil.compilation.model.CompilationOutcomeDescriptor;
|
||||
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.Checker;
|
||||
import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
|
||||
import com.puppycrawl.tools.checkstyle.DefaultLogger;
|
||||
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
|
||||
import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;
|
||||
|
||||
/**
|
||||
* A JUnit4 statement that performs source generation using the annotation processor and compiles those sources.
|
||||
* A JUnit Jupiter Extension that performs source generation using the annotation processor and compiles those sources.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
abstract class CompilingStatement extends Statement {
|
||||
abstract class CompilingExtension implements BeforeEachCallback {
|
||||
|
||||
static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create( new Object() );
|
||||
|
||||
private static final String TARGET_COMPILATION_TESTS = "/target/compilation-tests/";
|
||||
|
||||
@ -67,46 +69,20 @@ abstract class CompilingStatement extends Statement {
|
||||
|
||||
protected static final List<String> PROCESSOR_CLASSPATH = buildProcessorClasspath();
|
||||
|
||||
private final FrameworkMethod method;
|
||||
private final CompilationCache compilationCache;
|
||||
private final boolean runCheckstyle;
|
||||
private Statement next;
|
||||
|
||||
private String classOutputDir;
|
||||
private String sourceOutputDir;
|
||||
private String additionalCompilerClasspath;
|
||||
private CompilationRequest compilationRequest;
|
||||
private final Compiler compiler;
|
||||
|
||||
CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
this.method = method;
|
||||
this.compilationCache = compilationCache;
|
||||
this.runCheckstyle = !method.getMethod().getDeclaringClass().isAnnotationPresent( DisableCheckstyle.class );
|
||||
|
||||
this.compilationRequest = new CompilationRequest( getTestClasses(), getServices(), getProcessorOptions() );
|
||||
protected CompilingExtension(Compiler compiler) {
|
||||
this.compiler = compiler;
|
||||
}
|
||||
|
||||
void setNextStatement(Statement next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
generateMapperImplementation();
|
||||
|
||||
GeneratedSource.setCompilingStatement( this );
|
||||
next.evaluate();
|
||||
GeneratedSource.clearCompilingStatement();
|
||||
}
|
||||
|
||||
String getSourceOutputDir() {
|
||||
return compilationCache.getLastSourceOutputDir();
|
||||
}
|
||||
|
||||
protected void setupDirectories() {
|
||||
protected void setupDirectories(Method testMethod, Class<?> testClass) {
|
||||
String compilationRoot = getBasePath()
|
||||
+ TARGET_COMPILATION_TESTS
|
||||
+ method.getDeclaringClass().getName()
|
||||
+ "/" + method.getName()
|
||||
+ testClass.getName()
|
||||
+ "/" + testMethod.getName()
|
||||
+ getPathSuffix();
|
||||
|
||||
classOutputDir = compilationRoot + "/classes";
|
||||
@ -118,7 +94,9 @@ abstract class CompilingStatement extends Statement {
|
||||
( (ModifiableURLClassLoader) Thread.currentThread().getContextClassLoader() ).withPath( classOutputDir );
|
||||
}
|
||||
|
||||
protected abstract String getPathSuffix();
|
||||
protected String getPathSuffix() {
|
||||
return "_" + compiler.name().toLowerCase();
|
||||
}
|
||||
|
||||
private static List<String> buildTestCompilationClasspath() {
|
||||
String[] whitelist =
|
||||
@ -169,14 +147,21 @@ abstract class CompilingStatement extends Statement {
|
||||
return Stream.of( whitelist ).anyMatch( path::contains );
|
||||
}
|
||||
|
||||
protected void generateMapperImplementation() throws Exception {
|
||||
CompilationOutcomeDescriptor actualResult = compile();
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
CompilationOutcomeDescriptor actualResult = compile( context );
|
||||
assertResult( actualResult, context );
|
||||
}
|
||||
|
||||
private void assertResult(CompilationOutcomeDescriptor actualResult, ExtensionContext context) throws Exception {
|
||||
Method testMethod = context.getRequiredTestMethod();
|
||||
Class<?> testClass = context.getRequiredTestClass();
|
||||
|
||||
CompilationOutcomeDescriptor expectedResult =
|
||||
CompilationOutcomeDescriptor.forExpectedCompilationResult(
|
||||
method.getAnnotation( ExpectedCompilationOutcome.class ),
|
||||
method.getAnnotation( ExpectedNote.ExpectedNotes.class ),
|
||||
method.getAnnotation( ExpectedNote.class )
|
||||
findAnnotation( testMethod, ExpectedCompilationOutcome.class ).orElse( null ),
|
||||
findAnnotation( testMethod, ExpectedNote.ExpectedNotes.class ).orElse( null ),
|
||||
findAnnotation( testMethod, ExpectedNote.class ).orElse( null )
|
||||
);
|
||||
|
||||
if ( expectedResult.getCompilationResult() == CompilationResult.SUCCEEDED ) {
|
||||
@ -195,7 +180,7 @@ abstract class CompilingStatement extends Statement {
|
||||
assertDiagnostics( actualResult.getDiagnostics(), expectedResult.getDiagnostics() );
|
||||
assertNotes( actualResult.getNotes(), expectedResult.getNotes() );
|
||||
|
||||
if ( runCheckstyle ) {
|
||||
if ( !findAnnotation( testClass, DisableCheckstyle.class ).isPresent() ) {
|
||||
assertCheckstyleRules();
|
||||
}
|
||||
}
|
||||
@ -349,18 +334,14 @@ abstract class CompilingStatement extends Statement {
|
||||
*
|
||||
* @return A set containing the classes to be compiled for this test
|
||||
*/
|
||||
private Set<Class<?>> getTestClasses() {
|
||||
private Set<Class<?>> getTestClasses(Method testMethod, Class<?> testClass) {
|
||||
Set<Class<?>> testClasses = new HashSet<>();
|
||||
|
||||
WithClasses withClasses = method.getAnnotation( WithClasses.class );
|
||||
if ( withClasses != null ) {
|
||||
testClasses.addAll( Arrays.asList( withClasses.value() ) );
|
||||
}
|
||||
findAnnotation( testMethod, WithClasses.class )
|
||||
.ifPresent( withClasses -> testClasses.addAll( Arrays.asList( withClasses.value() ) ) );
|
||||
|
||||
withClasses = method.getMethod().getDeclaringClass().getAnnotation( WithClasses.class );
|
||||
if ( withClasses != null ) {
|
||||
testClasses.addAll( Arrays.asList( withClasses.value() ) );
|
||||
}
|
||||
findAnnotation( testClass, WithClasses.class )
|
||||
.ifPresent( withClasses -> testClasses.addAll( Arrays.asList( withClasses.value() ) ) );
|
||||
|
||||
if ( testClasses.isEmpty() ) {
|
||||
throw new IllegalStateException(
|
||||
@ -377,24 +358,19 @@ abstract class CompilingStatement extends Statement {
|
||||
* @return A map containing the package were to look for a resource (key) and the resource (value) to be compiled
|
||||
* for this test
|
||||
*/
|
||||
private Map<Class<?>, Class<?>> getServices() {
|
||||
private Map<Class<?>, Class<?>> getServices(Method testMethod, Class<?> testClass) {
|
||||
Map<Class<?>, Class<?>> services = new HashMap<>();
|
||||
|
||||
addServices( services, method.getAnnotation( WithServiceImplementations.class ) );
|
||||
addService( services, method.getAnnotation( WithServiceImplementation.class ) );
|
||||
addServices( services, findRepeatableAnnotations( testMethod, WithServiceImplementation.class ) );
|
||||
|
||||
Class<?> declaringClass = method.getMethod().getDeclaringClass();
|
||||
addServices( services, declaringClass.getAnnotation( WithServiceImplementations.class ) );
|
||||
addService( services, declaringClass.getAnnotation( WithServiceImplementation.class ) );
|
||||
addServices( services, findRepeatableAnnotations( testClass, WithServiceImplementation.class ) );
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private void addServices(Map<Class<?>, Class<?>> services, WithServiceImplementations withImplementations) {
|
||||
if ( withImplementations != null ) {
|
||||
for ( WithServiceImplementation resource : withImplementations.value() ) {
|
||||
addService( services, resource );
|
||||
}
|
||||
private void addServices(Map<Class<?>, Class<?>> services, List<WithServiceImplementation> withImplementations) {
|
||||
for ( WithServiceImplementation withImplementation : withImplementations ) {
|
||||
addService( services, withImplementation );
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,17 +401,11 @@ abstract class CompilingStatement extends Statement {
|
||||
*
|
||||
* @return A list containing the processor options to be used for this test
|
||||
*/
|
||||
private List<String> getProcessorOptions() {
|
||||
List<ProcessorOption> processorOptions =
|
||||
getProcessorOptions(
|
||||
method.getAnnotation( ProcessorOptions.class ),
|
||||
method.getAnnotation( ProcessorOption.class ) );
|
||||
private List<String> getProcessorOptions(Method testMethod, Class<?> testClass) {
|
||||
List<ProcessorOption> processorOptions = findRepeatableAnnotations( testMethod, ProcessorOption.class );
|
||||
|
||||
if ( processorOptions.isEmpty() ) {
|
||||
processorOptions =
|
||||
getProcessorOptions(
|
||||
method.getMethod().getDeclaringClass().getAnnotation( ProcessorOptions.class ),
|
||||
method.getMethod().getDeclaringClass().getAnnotation( ProcessorOption.class ) );
|
||||
processorOptions = findRepeatableAnnotations( testClass, ProcessorOption.class );
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>( processorOptions.size() );
|
||||
@ -449,17 +419,6 @@ abstract class CompilingStatement extends Statement {
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ProcessorOption> getProcessorOptions(ProcessorOptions options, ProcessorOption option) {
|
||||
if ( options != null ) {
|
||||
return Arrays.asList( options.value() );
|
||||
}
|
||||
else if ( option != null ) {
|
||||
return Arrays.asList( option );
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private String asOptionString(ProcessorOption processorOption) {
|
||||
return String.format( "-A%s=%s", processorOption.name(), processorOption.value() );
|
||||
}
|
||||
@ -479,17 +438,32 @@ abstract class CompilingStatement extends Statement {
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
private CompilationOutcomeDescriptor compile()
|
||||
throws Exception {
|
||||
private CompilationOutcomeDescriptor compile(ExtensionContext context) {
|
||||
Method testMethod = context.getRequiredTestMethod();
|
||||
Class<?> testClass = context.getRequiredTestClass();
|
||||
|
||||
if ( !needsRecompilation() ) {
|
||||
CompilationRequest compilationRequest = new CompilationRequest(
|
||||
compiler,
|
||||
getTestClasses( testMethod, testClass ),
|
||||
getServices( testMethod, testClass ),
|
||||
getProcessorOptions( testMethod, testClass )
|
||||
);
|
||||
|
||||
ExtensionContext.Store rootStore = context.getRoot().getStore( NAMESPACE );
|
||||
|
||||
// We need to put the compilation request in the store, so the GeneratedSource can use it
|
||||
context.getStore( NAMESPACE ).put( context.getUniqueId() + "-compilationRequest", compilationRequest );
|
||||
CompilationCache compilationCache = rootStore
|
||||
.getOrComputeIfAbsent( compilationRequest, request -> new CompilationCache(), CompilationCache.class );
|
||||
|
||||
if ( !needsRecompilation( compilationRequest, compilationCache ) ) {
|
||||
return compilationCache.getLastResult();
|
||||
}
|
||||
|
||||
setupDirectories();
|
||||
setupDirectories( testMethod, testClass );
|
||||
compilationCache.setLastSourceOutputDir( sourceOutputDir );
|
||||
|
||||
boolean needsAdditionalCompilerClasspath = prepareServices();
|
||||
boolean needsAdditionalCompilerClasspath = prepareServices( compilationRequest );
|
||||
CompilationOutcomeDescriptor resultHolder;
|
||||
|
||||
resultHolder = compileWithSpecificCompiler(
|
||||
@ -517,7 +491,7 @@ abstract class CompilingStatement extends Statement {
|
||||
String classOutputDir,
|
||||
String additionalCompilerClasspath);
|
||||
|
||||
boolean needsRecompilation() {
|
||||
boolean needsRecompilation(CompilationRequest compilationRequest, CompilationCache compilationCache) {
|
||||
return !compilationRequest.equals( compilationCache.getLastRequest() );
|
||||
}
|
||||
|
||||
@ -559,7 +533,7 @@ abstract class CompilingStatement extends Statement {
|
||||
path.delete();
|
||||
}
|
||||
|
||||
private boolean prepareServices() {
|
||||
private boolean prepareServices(CompilationRequest compilationRequest) {
|
||||
if ( !compilationRequest.getServices().isEmpty() ) {
|
||||
String servicesDir =
|
||||
additionalCompilerClasspath + File.separator + "META-INF" + File.separator + "services";
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This should be used with care.
|
||||
* This is similar to the JUnit 5 DisabledOnJre (once we have JUnit 5 we can replace this one)
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DisabledOnCompiler {
|
||||
/**
|
||||
* @return The compiler to use.
|
||||
*/
|
||||
Compiler[] value();
|
||||
}
|
@ -16,16 +16,16 @@ 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 Eclipse JDT compiler to compile.
|
||||
* Extension that uses the Eclipse JDT compiler to compile.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
class EclipseCompilingStatement extends CompilingStatement {
|
||||
class EclipseCompilingExtension extends CompilingExtension {
|
||||
|
||||
private static final List<String> ECLIPSE_COMPILER_CLASSPATH = buildEclipseCompilerClasspath();
|
||||
|
||||
@ -35,8 +35,8 @@ class EclipseCompilingStatement extends CompilingStatement {
|
||||
.withPaths( PROCESSOR_CLASSPATH )
|
||||
.withOriginOf( ClassLoaderExecutor.class );
|
||||
|
||||
EclipseCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
super( method, compilationCache );
|
||||
EclipseCompilingExtension() {
|
||||
super( Compiler.ECLIPSE );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -152,9 +152,4 @@ class EclipseCompilingStatement extends CompilingStatement {
|
||||
|
||||
return filterBootClassPath( whitelist );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathSuffix() {
|
||||
return "_eclipse";
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This should be used with care.
|
||||
* This is similar to the JUnit 5 EnabledOnJre (once we have JUnit 5 we can replace this one)
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EnabledOnCompiler {
|
||||
/**
|
||||
* @return The compiler to use.
|
||||
*/
|
||||
Compiler[] value();
|
||||
}
|
@ -25,6 +25,11 @@ final class FilteringParentClassLoader extends ClassLoader {
|
||||
this.excludedPrefixes = new ArrayList<>( Arrays.asList( excludedPrefixes ) );
|
||||
}
|
||||
|
||||
FilteringParentClassLoader(ClassLoader parent, String... excludedPrefixes) {
|
||||
super( parent );
|
||||
this.excludedPrefixes = new ArrayList<>( Arrays.asList( excludedPrefixes ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classes The classes to hide (inner classes are hidden as well)
|
||||
* @return {@code this}
|
||||
|
@ -13,48 +13,60 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.mapstruct.ap.testutil.assertions.JavaFileAssert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mapstruct.ap.testutil.runner.CompilingExtension.NAMESPACE;
|
||||
|
||||
/**
|
||||
* A {@link TestRule} to perform assertions on generated source files.
|
||||
* A {@link org.junit.jupiter.api.extension.RegisterExtension RegisterExtension} to perform assertions on generated
|
||||
* source files.
|
||||
* <p>
|
||||
* To add it to the test, use:
|
||||
*
|
||||
* <pre>
|
||||
* @Rule
|
||||
* public GeneratedSource generatedSources = new GeneratedSource();
|
||||
* @RegisterExtension
|
||||
* final GeneratedSource generatedSources = new GeneratedSource();
|
||||
* </pre>
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
public class GeneratedSource implements TestRule {
|
||||
public class GeneratedSource implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
|
||||
|
||||
private static final String FIXTURES_ROOT = "fixtures/";
|
||||
|
||||
/**
|
||||
* static ThreadLocal, as the {@link CompilingStatement} must inject itself statically for this rule to gain access
|
||||
* to the statement's information. As test execution of different classes in parallel is supported.
|
||||
* ThreadLocal, as the source dir must be injected for this extension to gain access
|
||||
* to the compilation information. As test execution of different classes in parallel is supported.
|
||||
*/
|
||||
private static ThreadLocal<CompilingStatement> compilingStatement = new ThreadLocal<>();
|
||||
private ThreadLocal<String> sourceOutputDir = new ThreadLocal<>();
|
||||
|
||||
private List<Class<?>> fixturesFor = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new GeneratedSourceStatement( base );
|
||||
public void beforeTestExecution(ExtensionContext context) throws Exception {
|
||||
CompilationRequest compilationRequest = context.getStore( NAMESPACE )
|
||||
.get( context.getUniqueId() + "-compilationRequest", CompilationRequest.class );
|
||||
setSourceOutputDir( context.getStore( NAMESPACE )
|
||||
.get( compilationRequest, CompilationCache.class )
|
||||
.getLastSourceOutputDir() );
|
||||
}
|
||||
|
||||
static void setCompilingStatement(CompilingStatement compilingStatement) {
|
||||
GeneratedSource.compilingStatement.set( compilingStatement );
|
||||
@Override
|
||||
public void afterTestExecution(ExtensionContext context) throws Exception {
|
||||
handleFixtureComparison();
|
||||
clearSourceOutputDir();
|
||||
}
|
||||
|
||||
static void clearCompilingStatement() {
|
||||
GeneratedSource.compilingStatement.remove();
|
||||
private void setSourceOutputDir(String sourceOutputDir) {
|
||||
this.sourceOutputDir.set( sourceOutputDir );
|
||||
}
|
||||
|
||||
private void clearSourceOutputDir() {
|
||||
this.sourceOutputDir.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,21 +113,7 @@ 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.get().getSourceOutputDir() + "/" + path ) );
|
||||
}
|
||||
|
||||
private class GeneratedSourceStatement extends Statement {
|
||||
private final Statement next;
|
||||
|
||||
private GeneratedSourceStatement(Statement next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
next.evaluate();
|
||||
handleFixtureComparison();
|
||||
}
|
||||
return new JavaFileAssert( new File( sourceOutputDir.get() + "/" + path ) );
|
||||
}
|
||||
|
||||
private void handleFixtureComparison() throws UnsupportedEncodingException {
|
||||
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.junit.runners.model.TestClass;
|
||||
|
||||
/**
|
||||
* Internal test runner that runs the tests of one class for one specific compiler implementation.
|
||||
*
|
||||
* @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) {
|
||||
ModifiableURLClassLoader testClassLoader = new ModifiableURLClassLoader().withOriginOf( klass );
|
||||
|
||||
Thread.currentThread().setContextClassLoader( testClassLoader );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIgnored(FrameworkMethod child) {
|
||||
return super.isIgnored( child ) || isIgnoredForCompiler( child );
|
||||
}
|
||||
|
||||
protected boolean isIgnoredForCompiler(FrameworkMethod child) {
|
||||
EnabledOnCompiler enabledOnCompiler = child.getAnnotation( EnabledOnCompiler.class );
|
||||
if ( enabledOnCompiler != null ) {
|
||||
for ( Compiler value : enabledOnCompiler.value() ) {
|
||||
if ( value != compiler ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisabledOnCompiler disabledOnCompiler = child.getAnnotation( DisabledOnCompiler.class );
|
||||
if ( disabledOnCompiler != null ) {
|
||||
for ( Compiler value : disabledOnCompiler.value() ) {
|
||||
if ( value == compiler ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@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 if ( compiler == Compiler.JDK11 ) {
|
||||
return new Jdk11CompilingStatement( 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();
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.mapstruct.ap.testutil.compilation.model.DiagnosticDescriptor;
|
||||
|
||||
/**
|
||||
* Statement that uses the JDK compiler to compile.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
class Jdk11CompilingStatement extends JdkCompilingStatement {
|
||||
|
||||
Jdk11CompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
super( method, compilationCache );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The JDK 11 compiler reports all ERROR diagnostics properly. Also when there are multiple per line.
|
||||
*/
|
||||
@Override
|
||||
protected List<DiagnosticDescriptor> filterExpectedDiagnostics(List<DiagnosticDescriptor> expectedDiagnostics) {
|
||||
return expectedDiagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathSuffix() {
|
||||
return "_jdk";
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
@ -22,17 +21,18 @@ import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
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.
|
||||
* Extension that uses the JDK compiler to compile.
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
class JdkCompilingStatement extends CompilingStatement {
|
||||
class JdkCompilingExtension extends CompilingExtension {
|
||||
|
||||
private static final List<File> COMPILER_CLASSPATH_FILES = asFiles( TEST_COMPILATION_CLASSPATH );
|
||||
|
||||
@ -40,8 +40,8 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
new ModifiableURLClassLoader( new FilteringParentClassLoader( "org.mapstruct." ) )
|
||||
.withPaths( PROCESSOR_CLASSPATH );
|
||||
|
||||
JdkCompilingStatement(FrameworkMethod method, CompilationCache compilationCache) {
|
||||
super( method, compilationCache );
|
||||
JdkCompilingExtension() {
|
||||
super( Compiler.JDK );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,14 +107,20 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The JDK 8 compiler needs some special treatment for the diagnostics.
|
||||
* See comment in the function.
|
||||
*/
|
||||
@Override
|
||||
protected List<DiagnosticDescriptor> filterExpectedDiagnostics(List<DiagnosticDescriptor> expectedDiagnostics) {
|
||||
List<DiagnosticDescriptor> filtered = new ArrayList<>( expectedDiagnostics.size() );
|
||||
if ( JRE.currentVersion() != JRE.JAVA_8 ) {
|
||||
// The JDK 8+ compilers report all ERROR diagnostics properly. Also when there are multiple per line.
|
||||
return expectedDiagnostics;
|
||||
}
|
||||
List<DiagnosticDescriptor> filtered = new ArrayList<DiagnosticDescriptor>( expectedDiagnostics.size() );
|
||||
|
||||
// The JDK 8 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.
|
||||
DiagnosticDescriptor previous = null;
|
||||
for ( DiagnosticDescriptor diag : expectedDiagnostics ) {
|
||||
if ( diag.getKind() != Kind.ERROR
|
||||
@ -129,8 +135,4 @@ class JdkCompilingStatement extends CompilingStatement {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathSuffix() {
|
||||
return "_jdk";
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
|
||||
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
|
||||
import org.junit.platform.commons.support.AnnotationSupport;
|
||||
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||
|
||||
/**
|
||||
* The provider of the processor tests based on the defined compilers.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ProcessorTestExtension implements TestTemplateInvocationContextProvider {
|
||||
|
||||
@Override
|
||||
public boolean supportsTestTemplate(ExtensionContext context) {
|
||||
return AnnotationSupport.isAnnotated( context.getTestMethod(), ProcessorTest.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
|
||||
Method testMethod = context.getRequiredTestMethod();
|
||||
ProcessorTest processorTest = AnnotationSupport.findAnnotation( testMethod, ProcessorTest.class )
|
||||
.orElseThrow( () -> new RuntimeException( "Failed to get CompilerTest on " + testMethod ) );
|
||||
|
||||
return Stream.of( processorTest.value() )
|
||||
.map( ProcessorTestInvocationContext::new );
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
|
||||
|
||||
/**
|
||||
* The template invocation processor responsible for providing the appropriate extensions for the different compilers.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ProcessorTestInvocationContext implements TestTemplateInvocationContext {
|
||||
|
||||
protected Compiler compiler;
|
||||
|
||||
public ProcessorTestInvocationContext(Compiler compiler) {
|
||||
this.compiler = compiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(int invocationIndex) {
|
||||
return "[" + compiler.name().toLowerCase() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Extension> getAdditionalExtensions() {
|
||||
List<Extension> extensions = new ArrayList<>();
|
||||
extensions.add( new CompilerTestEnabledOnJreCondition( compiler ) );
|
||||
if ( compiler == Compiler.JDK ) {
|
||||
extensions.add( new JdkCompilingExtension() );
|
||||
}
|
||||
else if ( compiler == Compiler.ECLIPSE ) {
|
||||
extensions.add( new EclipseCompilingExtension() );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException( "Compiler [" + compiler + "] is not known" );
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runners.model.FrameworkField;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.TestClass;
|
||||
|
||||
/**
|
||||
* A {@link TestClass} where the wrapped Class can be replaced
|
||||
*
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
class ReplacableTestClass extends TestClass {
|
||||
private TestClass delegate;
|
||||
|
||||
/**
|
||||
* @see TestClass#TestClass(Class)
|
||||
*/
|
||||
ReplacableTestClass(Class<?> clazz) {
|
||||
super( clazz );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz the new class
|
||||
*/
|
||||
void replaceClass(Class<?> clazz) {
|
||||
delegate = new TestClass( clazz );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkMethod> getAnnotatedMethods() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethods();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethods();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethods( annotationClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethods( annotationClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkField> getAnnotatedFields() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFields();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFields();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FrameworkField> getAnnotatedFields(Class<? extends Annotation> annotationClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFields( annotationClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFields( annotationClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getJavaClass() {
|
||||
if ( null == delegate ) {
|
||||
return super.getJavaClass();
|
||||
}
|
||||
else {
|
||||
return delegate.getJavaClass();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if ( null == delegate ) {
|
||||
return super.getName();
|
||||
}
|
||||
else {
|
||||
return delegate.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<?> getOnlyConstructor() {
|
||||
if ( null == delegate ) {
|
||||
return super.getOnlyConstructor();
|
||||
}
|
||||
else {
|
||||
return delegate.getOnlyConstructor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotations();
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotation( annotationType );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotation( annotationType );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAnnotatedFieldValues(Object test, Class<? extends Annotation> annotationClass,
|
||||
Class<T> valueClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedFieldValues( test, annotationClass, valueClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedFieldValues( test, annotationClass, valueClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAnnotatedMethodValues(Object test, Class<? extends Annotation> annotationClass,
|
||||
Class<T> valueClass) {
|
||||
if ( null == delegate ) {
|
||||
return super.getAnnotatedMethodValues( test, annotationClass, valueClass );
|
||||
}
|
||||
else {
|
||||
return delegate.getAnnotatedMethodValues( test, annotationClass, valueClass );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if ( null == delegate ) {
|
||||
return super.toString();
|
||||
}
|
||||
else {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublic() {
|
||||
if ( null == delegate ) {
|
||||
return super.isPublic();
|
||||
}
|
||||
else {
|
||||
return delegate.isPublic();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isANonStaticInnerClass() {
|
||||
if ( null == delegate ) {
|
||||
return super.isANonStaticInnerClass();
|
||||
}
|
||||
else {
|
||||
return delegate.isANonStaticInnerClass();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if ( null == delegate ) {
|
||||
return super.hashCode();
|
||||
}
|
||||
else {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( null == delegate ) {
|
||||
return super.equals( obj );
|
||||
}
|
||||
else {
|
||||
return delegate.equals( obj );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.testutil.runner;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Temporarily only use the specified compiler for the test during debugging / implementation.
|
||||
* <p>
|
||||
* Do not commit tests with this annotation present!
|
||||
*
|
||||
* @deprecated Do not commit tests with this annotation present. Tests are expected to work with all compilers.
|
||||
* @author Andreas Gudian
|
||||
*/
|
||||
@Deprecated
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WithSingleCompiler {
|
||||
/**
|
||||
* @return The compiler to use.
|
||||
*/
|
||||
Compiler value();
|
||||
}
|
@ -0,0 +1 @@
|
||||
org.mapstruct.ap.testutil.runner.CompilerLauncherDiscoveryListener
|
Loading…
x
Reference in New Issue
Block a user