/** * Copyright 2012-2017 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.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 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.<Runner> asList( new InnerAnnotationProcessorRunner( klass, singleCompiler.value() ) ); } return Arrays.<Runner> 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 cleanDispayName = removeParameter( description.getDisplayName() ); Description cleanDescription = Description.createSuiteDescription( cleanDispayName, 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 ); } } }