This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch 1330 in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 41e80e58e97cb846b2cd9ce2ae5d018070570a02 Author: Christian Stein <sormu...@gmail.com> AuthorDate: Thu May 3 11:04:45 2018 +0200 Copy current sources from junit-platform-surefire-provider This commit includes enhancements and bug fixes that were applied since the initial code donation. https://issues.apache.org/jira/browse/SUREFIRE-1330 --- .../junitplatform/JUnitPlatformProvider.java | 137 ++++-- .../surefire/junitplatform/RunListenerAdapter.java | 188 ++++++-- .../surefire/junitplatform/TestMethodFilter.java | 60 +++ .../junitplatform/TestPlanScannerFilter.java | 18 +- .../junitplatform/JUnitPlatformProviderTests.java | 537 +++++++++++++++++++-- .../junitplatform/RunListenerAdapterTests.java | 400 +++++++++++++-- .../junitplatform/TestMethodFilterTests.java | 105 ++++ .../junitplatform/TestPlanScannerFilterTests.java | 30 +- 8 files changed, 1277 insertions(+), 198 deletions(-) diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java index 7f58921..510266e 100644 --- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java +++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java @@ -19,35 +19,45 @@ package org.apache.maven.surefire.junitplatform; * under the License. */ +import static java.util.Collections.emptyMap; +import static java.util.stream.Collectors.toList; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; + +import java.io.IOException; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.apache.maven.surefire.providerapi.AbstractProvider; import org.apache.maven.surefire.providerapi.ProviderParameters; +import org.apache.maven.surefire.report.ConsoleOutputCapture; +import org.apache.maven.surefire.report.ConsoleOutputReceiver; import org.apache.maven.surefire.report.ReporterException; import org.apache.maven.surefire.report.ReporterFactory; import org.apache.maven.surefire.report.RunListener; -import org.apache.maven.surefire.report.SimpleReportEntry; import org.apache.maven.surefire.suite.RunResult; +import org.apache.maven.surefire.testset.TestListResolver; import org.apache.maven.surefire.testset.TestSetFailedException; +import org.apache.maven.surefire.util.ScanResult; import org.apache.maven.surefire.util.TestsToRun; +import org.junit.platform.commons.util.Preconditions; +import org.junit.platform.commons.util.StringUtils; import org.junit.platform.engine.Filter; import org.junit.platform.launcher.Launcher; import org.junit.platform.launcher.LauncherDiscoveryRequest; import org.junit.platform.launcher.TagFilter; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.launcher.core.LauncherFactory; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; -import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; - -/** - * @since 1.0 - */ public class JUnitPlatformProvider extends AbstractProvider { @@ -61,6 +71,8 @@ public class JUnitPlatformProvider static final String INCLUDE_TAGS = "includeTags"; + static final String CONFIGURATION_PARAMETERS = "configurationParameters"; + static final String EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED = "The " + INCLUDE_GROUPS + " and " + INCLUDE_TAGS + " parameters (or the " + EXCLUDE_GROUPS + " and " + EXCLUDE_TAGS + " parameters) are synonyms - " + "only one of each is allowed (though neither is required)."; @@ -69,9 +81,11 @@ public class JUnitPlatformProvider private final Launcher launcher; - final Filter<?>[] includeAndExcludeFilters; + final Filter<?>[] filters; - public JUnitPlatformProvider( ProviderParameters parameters ) + final Map<String, String> configurationParameters; + + JUnitPlatformProvider( ProviderParameters parameters ) { this( parameters, LauncherFactory.create() ); } @@ -80,7 +94,8 @@ public class JUnitPlatformProvider { this.parameters = parameters; this.launcher = launcher; - this.includeAndExcludeFilters = getIncludeAndExcludeFilters(); + this.filters = getFilters(); + this.configurationParameters = getConfigurationParameters(); Logger.getLogger( "org.junit" ).setLevel( Level.WARNING ); } @@ -92,7 +107,7 @@ public class JUnitPlatformProvider @Override public RunResult invoke( Object forkTestSet ) - throws TestSetFailedException, ReporterException, InvocationTargetException + throws TestSetFailedException, ReporterException { if ( forkTestSet instanceof TestsToRun ) { @@ -114,8 +129,9 @@ public class JUnitPlatformProvider private TestsToRun scanClasspath() { - TestsToRun scannedClasses = parameters.getScanResult().applyFilter( - new TestPlanScannerFilter( launcher, includeAndExcludeFilters ), parameters.getTestClassLoader() ); + TestPlanScannerFilter filter = new TestPlanScannerFilter( launcher, filters ); + ScanResult scanResult = parameters.getScanResult(); + TestsToRun scannedClasses = scanResult.applyFilter( filter, parameters.getTestClassLoader() ); return parameters.getRunOrderCalculator().orderTestClasses( scannedClasses ); } @@ -126,12 +142,9 @@ public class JUnitPlatformProvider try { RunListener runListener = reporterFactory.createReporter(); - launcher.registerTestExecutionListeners( new RunListenerAdapter( runListener ) ); - - for ( Class<?> testClass : testsToRun ) - { - invokeSingleClass( testClass, runListener ); - } + ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) runListener ); + LauncherDiscoveryRequest discoveryRequest = buildLauncherDiscoveryRequest( testsToRun ); + launcher.execute( discoveryRequest, new RunListenerAdapter( runListener ) ); } finally { @@ -140,40 +153,70 @@ public class JUnitPlatformProvider return runResult; } - private void invokeSingleClass( Class<?> testClass, RunListener runListener ) + private LauncherDiscoveryRequest buildLauncherDiscoveryRequest( TestsToRun testsToRun ) { - SimpleReportEntry classEntry = new SimpleReportEntry( getClass().getName(), testClass.getName() ); - runListener.testSetStarting( classEntry ); - - LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters( - includeAndExcludeFilters ).build(); - launcher.execute( discoveryRequest ); - - runListener.testSetCompleted( classEntry ); + LauncherDiscoveryRequestBuilder builder = + request().filters( filters ).configurationParameters( configurationParameters ); + for ( Class<?> testClass : testsToRun ) + { + builder.selectors( selectClass( testClass ) ); + } + return builder.build(); } - private Filter<?>[] getIncludeAndExcludeFilters() + private Filter<?>[] getFilters() { List<Filter<?>> filters = new ArrayList<>(); - Optional<List<String>> includes = getGroupsOrTags( getPropertiesList( INCLUDE_GROUPS ), - getPropertiesList( INCLUDE_TAGS ) ); + Optional<List<String>> includes = + getGroupsOrTags( getPropertiesList( INCLUDE_GROUPS ), getPropertiesList( INCLUDE_TAGS ) ); includes.map( TagFilter::includeTags ).ifPresent( filters::add ); - Optional<List<String>> excludes = getGroupsOrTags( getPropertiesList( EXCLUDE_GROUPS ), - getPropertiesList( EXCLUDE_TAGS ) ); + Optional<List<String>> excludes = + getGroupsOrTags( getPropertiesList( EXCLUDE_GROUPS ), getPropertiesList( EXCLUDE_TAGS ) ); excludes.map( TagFilter::excludeTags ).ifPresent( filters::add ); - return filters.toArray( new Filter<?>[filters.size()] ); + TestListResolver testListResolver = parameters.getTestRequest().getTestListResolver(); + if ( !testListResolver.isEmpty() ) + { + filters.add( new TestMethodFilter( testListResolver ) ); + } + + return filters.toArray( new Filter<?>[0] ); + } + + private Map<String, String> getConfigurationParameters() + { + String content = parameters.getProviderProperties().get( CONFIGURATION_PARAMETERS ); + if ( content == null ) + { + return emptyMap(); + } + try ( StringReader reader = new StringReader( content ) ) + { + Map<String, String> result = new HashMap<>(); + Properties props = new Properties(); + props.load( reader ); + props.stringPropertyNames().forEach( key -> result.put( key, props.getProperty( key ) ) ); + return result; + } + catch ( IOException ex ) + { + throw new UncheckedIOException( "Error reading " + CONFIGURATION_PARAMETERS, ex ); + } } private Optional<List<String>> getPropertiesList( String key ) { List<String> compoundProperties = null; String property = parameters.getProviderProperties().get( key ); - if ( property != null ) + if ( StringUtils.isNotBlank( property ) ) { - compoundProperties = Arrays.asList( property.split( "[, ]+" ) ); + compoundProperties = + Arrays.stream( property.split( "[,]+" ) ) + .filter( StringUtils::isNotBlank ) + .map( String::trim ) + .collect( toList() ); } return Optional.ofNullable( compoundProperties ); } @@ -182,10 +225,7 @@ public class JUnitPlatformProvider { Optional<List<String>> elements = Optional.empty(); - if ( groups.isPresent() && tags.isPresent() ) - { - throw new IllegalStateException( EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED ); - } + Preconditions.condition(!groups.isPresent() || !tags.isPresent(), EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED ); if ( groups.isPresent() ) { @@ -198,5 +238,4 @@ public class JUnitPlatformProvider return elements; } - } diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java index 9191864..71a70c3 100644 --- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java +++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java @@ -24,6 +24,8 @@ import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED; import static org.junit.platform.engine.TestExecutionResult.Status.FAILED; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.surefire.report.PojoStackTraceWriter; import org.apache.maven.surefire.report.RunListener; @@ -36,6 +38,7 @@ import org.junit.platform.engine.support.descriptor.MethodSource; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; +import org.junit.platform.launcher.listeners.LegacyReportingUtils; /** * @since 1.0 @@ -46,9 +49,11 @@ final class RunListenerAdapter private final RunListener runListener; - private Optional<TestPlan> testPlan = Optional.empty(); + private TestPlan testPlan; - public RunListenerAdapter( RunListener runListener ) + private Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet(); + + RunListenerAdapter( RunListener runListener ) { this.runListener = runListener; } @@ -56,78 +61,203 @@ final class RunListenerAdapter @Override public void testPlanExecutionStarted( TestPlan testPlan ) { - this.testPlan = Optional.of( testPlan ); + updateTestPlan( testPlan ); } @Override public void testPlanExecutionFinished( TestPlan testPlan ) { - this.testPlan = Optional.empty(); + updateTestPlan( null ); } @Override public void executionStarted( TestIdentifier testIdentifier ) { + if ( testIdentifier.isContainer() + && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent() ) + { + startTestSetIfPossible( testIdentifier ); + } if ( testIdentifier.isTest() ) { - runListener.testStarting( createReportEntry( testIdentifier, Optional.empty() ) ); + ensureTestSetStarted( testIdentifier ); + runListener.testStarting( createReportEntry( testIdentifier ) ); } } @Override public void executionSkipped( TestIdentifier testIdentifier, String reason ) { - String source = getClassName( testIdentifier ).orElseGet( () -> parentDisplayName( testIdentifier ) ); - runListener.testSkipped( ignored( source, testIdentifier.getDisplayName(), reason ) ); + ensureTestSetStarted( testIdentifier ); + String source = getLegacyReportingClassName( testIdentifier ); + runListener.testSkipped( ignored( source, getLegacyReportingName( testIdentifier ), reason ) ); + completeTestSetIfNecessary( testIdentifier ); } @Override - public void executionFinished( TestIdentifier testIdentifier, TestExecutionResult testExecutionResult ) + public void executionFinished( + TestIdentifier testIdentifier, TestExecutionResult testExecutionResult ) { if ( testExecutionResult.getStatus() == ABORTED ) { - runListener.testAssumptionFailure( createReportEntry( testIdentifier, - testExecutionResult.getThrowable() ) ); + runListener.testAssumptionFailure( createReportEntry( testIdentifier, testExecutionResult ) ); } else if ( testExecutionResult.getStatus() == FAILED ) { - runListener.testFailed( createReportEntry( testIdentifier, testExecutionResult.getThrowable() ) ); + reportFailedTest( testIdentifier, testExecutionResult ); } else if ( testIdentifier.isTest() ) { - runListener.testSucceeded( createReportEntry( testIdentifier, Optional.empty() ) ); + runListener.testSucceeded( createReportEntry( testIdentifier ) ); } + completeTestSetIfNecessary( testIdentifier ); } - private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, Optional<Throwable> throwable ) + private void updateTestPlan( TestPlan testPlan ) { - Optional<String> className = getClassName( testIdentifier ); - if ( className.isPresent() ) + this.testPlan = testPlan; + testSetNodes.clear(); + } + + private void ensureTestSetStarted( TestIdentifier testIdentifier ) + { + if ( isTestSetStarted( testIdentifier ) ) + { + return; + } + if ( testIdentifier.isTest() ) { - StackTraceWriter traceWriter = new PojoStackTraceWriter( className.get(), - getMethodName( testIdentifier ).orElse( "" ), throwable.orElse( null ) ); - return new SimpleReportEntry( className.get(), testIdentifier.getDisplayName(), traceWriter, - (Integer) null ); + startTestSet( testPlan.getParent( testIdentifier ).orElse( testIdentifier ) ); } else { - return new SimpleReportEntry( parentDisplayName( testIdentifier ), testIdentifier.getDisplayName(), - (Integer) null ); + startTestSet( testIdentifier ); + } + } + + private boolean isTestSetStarted( TestIdentifier testIdentifier ) + { + return testSetNodes.contains( testIdentifier ) + || testPlan.getParent( testIdentifier ).map( this::isTestSetStarted ).orElse( false ); + } + + private void startTestSetIfPossible( TestIdentifier testIdentifier ) + { + if ( !isTestSetStarted( testIdentifier ) ) + { + startTestSet( testIdentifier ); } } - private Optional<String> getClassName( TestIdentifier testIdentifier ) + private void completeTestSetIfNecessary( TestIdentifier testIdentifier ) + { + if ( testSetNodes.contains( testIdentifier ) ) + { + completeTestSet( testIdentifier ); + } + } + + private void startTestSet( TestIdentifier testIdentifier ) + { + runListener.testSetStarting( createTestSetReportEntry( testIdentifier ) ); + testSetNodes.add( testIdentifier ); + } + + private void completeTestSet( TestIdentifier testIdentifier ) + { + runListener.testSetCompleted( createTestSetReportEntry( testIdentifier ) ); + testSetNodes.remove( testIdentifier ); + } + + private void reportFailedTest( + TestIdentifier testIdentifier, TestExecutionResult testExecutionResult ) + { + SimpleReportEntry reportEntry = createReportEntry( testIdentifier, testExecutionResult ); + if ( testExecutionResult.getThrowable().filter( AssertionError.class::isInstance ).isPresent() ) + { + runListener.testFailed( reportEntry ); + } + else + { + runListener.testError( reportEntry ); + } + } + + private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier ) + { + return new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), testIdentifier.getLegacyReportingName() ); + } + + private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier ) + { + return createReportEntry( testIdentifier, (StackTraceWriter) null ); + } + + private SimpleReportEntry createReportEntry( + TestIdentifier testIdentifier, TestExecutionResult testExecutionResult ) + { + return createReportEntry( + testIdentifier, getStackTraceWriter( testIdentifier, testExecutionResult ) ); + } + + private SimpleReportEntry createReportEntry( + TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter ) + { + String source = getLegacyReportingClassName( testIdentifier ); + String name = getLegacyReportingName( testIdentifier ); + + return SimpleReportEntry.withException( source, name, stackTraceWriter ); + } + + private String getLegacyReportingName( TestIdentifier testIdentifier ) + { + // Surefire cuts off the name at the first '(' character. Thus, we have to pick a different + // character to represent parentheses. "()" are removed entirely to maximize compatibility with + // existing reporting tools because in the old days test methods used to not have parameters. + return testIdentifier + .getLegacyReportingName() + .replace( "()", "" ) + .replace( '(', '{' ) + .replace( ')', '}' ); + } + + private String getLegacyReportingClassName( TestIdentifier testIdentifier ) + { + return LegacyReportingUtils.getClassName( testPlan, testIdentifier ); + } + + private StackTraceWriter getStackTraceWriter( + TestIdentifier testIdentifier, TestExecutionResult testExecutionResult ) + { + Optional<Throwable> throwable = testExecutionResult.getThrowable(); + if ( testExecutionResult.getStatus() == FAILED ) + { + // Failed tests must have a StackTraceWriter, otherwise Surefire will fail + return getStackTraceWriter( testIdentifier, throwable.orElse( null ) ); + } + return throwable.map( t -> getStackTraceWriter( testIdentifier, t ) ).orElse( null ); + } + + private StackTraceWriter getStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable ) + { + String className = getClassName( testIdentifier ); + String methodName = getMethodName( testIdentifier ).orElse( "" ); + return new PojoStackTraceWriter( className, methodName, throwable ); + } + + private String getClassName( TestIdentifier testIdentifier ) { TestSource testSource = testIdentifier.getSource().orElse( null ); if ( testSource instanceof ClassSource ) { - return Optional.of( ( (ClassSource) testSource ).getJavaClass().getName() ); + return ( (ClassSource) testSource ).getJavaClass().getName(); } if ( testSource instanceof MethodSource ) { - return Optional.of( ( (MethodSource) testSource ).getClassName() ); + return ( (MethodSource) testSource ).getClassName(); } - return Optional.empty(); + return testPlan.getParent( testIdentifier ).map( this::getClassName ).orElse( "" ); } private Optional<String> getMethodName( TestIdentifier testIdentifier ) @@ -139,12 +269,4 @@ final class RunListenerAdapter } return Optional.empty(); } - - private String parentDisplayName( TestIdentifier testIdentifier ) - { - return testPlan - .flatMap( plan -> plan.getParent( testIdentifier ) ) - .map( TestIdentifier::getDisplayName ) - .orElseGet( testIdentifier::getUniqueId ); - } } diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java new file mode 100644 index 0000000..45e32db --- /dev/null +++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestMethodFilter.java @@ -0,0 +1,60 @@ +package org.apache.maven.surefire.junitplatform; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.maven.surefire.testset.TestListResolver; +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.support.descriptor.MethodSource; +import org.junit.platform.launcher.PostDiscoveryFilter; + +/** + * @since 1.0.3 + */ +class TestMethodFilter + implements PostDiscoveryFilter +{ + + private final TestListResolver testListResolver; + + TestMethodFilter( TestListResolver testListResolver ) + { + this.testListResolver = testListResolver; + } + + @Override + public FilterResult apply( TestDescriptor descriptor ) + { + boolean shouldRun = descriptor.getSource() + .filter( MethodSource.class::isInstance ) + .map( MethodSource.class::cast ) + .map( this::shouldRun ) + .orElse( true ); + + return FilterResult.includedIf( shouldRun ); + } + + private boolean shouldRun( MethodSource source ) + { + String testClass = TestListResolver.toClassFileName( source.getClassName() ); + String testMethod = source.getMethodName(); + return this.testListResolver.shouldRun( testClass, testMethod ); + } +} diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java index 4a95d6e..4b488c1 100644 --- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java +++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilter.java @@ -22,13 +22,10 @@ package org.apache.maven.surefire.junitplatform; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; -import java.util.function.Predicate; - import org.apache.maven.surefire.util.ScannerFilter; import org.junit.platform.engine.Filter; import org.junit.platform.launcher.Launcher; import org.junit.platform.launcher.LauncherDiscoveryRequest; -import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; /** @@ -38,14 +35,11 @@ final class TestPlanScannerFilter implements ScannerFilter { - private static final Predicate<TestIdentifier> HAS_TESTS = testIdentifier -> testIdentifier.isTest() - || testIdentifier.isContainer(); - private final Launcher launcher; private final Filter<?>[] includeAndExcludeFilters; - public TestPlanScannerFilter( Launcher launcher, Filter<?>[] includeAndExcludeFilters ) + TestPlanScannerFilter( Launcher launcher, Filter<?>[] includeAndExcludeFilters ) { this.launcher = launcher; this.includeAndExcludeFilters = includeAndExcludeFilters; @@ -55,10 +49,12 @@ final class TestPlanScannerFilter @SuppressWarnings( "rawtypes" ) public boolean accept( Class testClass ) { - LauncherDiscoveryRequest discoveryRequest = request().selectors( selectClass( testClass ) ).filters( - includeAndExcludeFilters ).build(); + LauncherDiscoveryRequest discoveryRequest = request() + .selectors( selectClass( testClass ) ) + .filters( includeAndExcludeFilters ).build(); + TestPlan testPlan = launcher.discover( discoveryRequest ); - return testPlan.countTestIdentifiers( HAS_TESTS ) > 0; - } + return testPlan.containsTests(); + } } diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java index a32aade..e0db97e 100644 --- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java +++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProviderTests.java @@ -19,24 +19,43 @@ package org.apache.maven.surefire.junitplatform; * under the License. */ +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toSet; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.mockito.AdditionalMatchers.gt; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.maven.surefire.providerapi.ProviderParameters; +import org.apache.maven.surefire.report.ConsoleOutputReceiver; +import org.apache.maven.surefire.report.ReportEntry; import org.apache.maven.surefire.report.ReporterFactory; import org.apache.maven.surefire.report.RunListener; +import org.apache.maven.surefire.report.SimpleReportEntry; +import org.apache.maven.surefire.testset.TestListResolver; +import org.apache.maven.surefire.testset.TestRequest; +import org.apache.maven.surefire.testset.TestSetFailedException; import org.apache.maven.surefire.util.RunOrderCalculator; import org.apache.maven.surefire.util.ScanResult; import org.apache.maven.surefire.util.TestsToRun; @@ -44,10 +63,14 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.platform.commons.util.PreconditionViolationException; import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; import org.junit.platform.launcher.listeners.TestExecutionSummary; +import org.junit.platform.launcher.listeners.TestExecutionSummary.Failure; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; /** * Unit tests for {@link JUnitPlatformProvider}. @@ -59,9 +82,10 @@ class JUnitPlatformProviderTests @Test void getSuitesReturnsScannedClasses() - throws Exception + throws Exception { - ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class ); + ProviderParameters providerParameters = + providerParametersMock( TestClass1.class, TestClass2.class ); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); assertThat( provider.getSuites() ).containsOnly( TestClass1.class, TestClass2.class ); @@ -69,17 +93,18 @@ class JUnitPlatformProviderTests @Test void invokeThrowsForWrongForkTestSet() - throws Exception + throws Exception { ProviderParameters providerParameters = providerParametersMock( Integer.class ); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); - assertThrows( IllegalArgumentException.class, () -> provider.invoke( "wrong forkTestSet" ) ); + assertThrows( + IllegalArgumentException.class, () -> invokeProvider( provider, "wrong forkTestSet" ) ); } @Test void allGivenTestsToRunAreInvoked() - throws Exception + throws Exception { Launcher launcher = LauncherFactory.create(); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher ); @@ -88,16 +113,25 @@ class JUnitPlatformProviderTests launcher.registerTestExecutionListeners( executionListener ); TestsToRun testsToRun = newTestsToRun( TestClass1.class, TestClass2.class ); - provider.invoke( testsToRun ); + invokeProvider( provider, testsToRun ); - assertThat( executionListener.summaries ).hasSize( 2 ); - TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) ); - TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) ); + assertThat( executionListener.summaries ).hasSize( 1 ); + TestExecutionSummary summary = executionListener.summaries.get( 0 ); + assertEquals( TestClass1.TESTS_FOUND + TestClass2.TESTS_FOUND, summary.getTestsFoundCount() ); + assertEquals( + TestClass1.TESTS_STARTED + TestClass2.TESTS_STARTED, summary.getTestsStartedCount() ); + assertEquals( + TestClass1.TESTS_SKIPPED + TestClass2.TESTS_SKIPPED, summary.getTestsSkippedCount() ); + assertEquals( + TestClass1.TESTS_SUCCEEDED + TestClass2.TESTS_SUCCEEDED, summary.getTestsSucceededCount() ); + assertEquals( + TestClass1.TESTS_ABORTED + TestClass2.TESTS_ABORTED, summary.getTestsAbortedCount() ); + assertEquals( TestClass1.TESTS_FAILED + TestClass2.TESTS_FAILED, summary.getTestsFailedCount() ); } @Test void singleTestClassIsInvoked() - throws Exception + throws Exception { Launcher launcher = LauncherFactory.create(); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParametersMock(), launcher ); @@ -105,28 +139,94 @@ class JUnitPlatformProviderTests TestPlanSummaryListener executionListener = new TestPlanSummaryListener(); launcher.registerTestExecutionListeners( executionListener ); - provider.invoke( TestClass1.class ); + invokeProvider( provider, TestClass1.class ); assertThat( executionListener.summaries ).hasSize( 1 ); - TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) ); + TestExecutionSummary summary = executionListener.summaries.get( 0 ); + assertEquals( TestClass1.TESTS_FOUND, summary.getTestsFoundCount() ); + assertEquals( TestClass1.TESTS_STARTED, summary.getTestsStartedCount() ); + assertEquals( TestClass1.TESTS_SKIPPED, summary.getTestsSkippedCount() ); + assertEquals( TestClass1.TESTS_SUCCEEDED, summary.getTestsSucceededCount() ); + assertEquals( TestClass1.TESTS_ABORTED, summary.getTestsAbortedCount() ); + assertEquals( TestClass1.TESTS_FAILED, summary.getTestsFailedCount() ); } @Test void allDiscoveredTestsAreInvokedForNullArgument() - throws Exception + throws Exception { - ProviderParameters providerParameters = providerParametersMock( TestClass1.class, TestClass2.class ); + RunListener runListener = runListenerMock(); + ProviderParameters providerParameters = + providerParametersMock( runListener, TestClass1.class, TestClass2.class ); Launcher launcher = LauncherFactory.create(); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters, launcher ); TestPlanSummaryListener executionListener = new TestPlanSummaryListener(); launcher.registerTestExecutionListeners( executionListener ); - provider.invoke( null ); + invokeProvider( provider, null ); + + InOrder inOrder = inOrder( runListener ); + inOrder + .verify( runListener ) + .testSetStarting( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + TestClass1.class.getName() ) ); + inOrder + .verify( runListener ) + .testSetCompleted( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + TestClass1.class.getName() ) ); + inOrder + .verify( runListener ) + .testSetStarting( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + TestClass2.class.getName() ) ); + inOrder + .verify( runListener ) + .testSetCompleted( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + TestClass2.class.getName() ) ); + + assertThat( executionListener.summaries ).hasSize( 1 ); + TestExecutionSummary summary = executionListener.summaries.get( 0 ); + assertEquals( TestClass1.TESTS_FOUND + TestClass2.TESTS_FOUND, summary.getTestsFoundCount() ); + assertEquals( + TestClass1.TESTS_STARTED + TestClass2.TESTS_STARTED, summary.getTestsStartedCount() ); + assertEquals( + TestClass1.TESTS_SKIPPED + TestClass2.TESTS_SKIPPED, summary.getTestsSkippedCount() ); + assertEquals( + TestClass1.TESTS_SUCCEEDED + TestClass2.TESTS_SUCCEEDED, summary.getTestsSucceededCount() ); + assertEquals( + TestClass1.TESTS_ABORTED + TestClass2.TESTS_ABORTED, summary.getTestsAbortedCount() ); + assertEquals( TestClass1.TESTS_FAILED + TestClass2.TESTS_FAILED, summary.getTestsFailedCount() ); + } - assertThat( executionListener.summaries ).hasSize( 2 ); - TestClass1.verifyExecutionSummary( executionListener.summaries.get( 0 ) ); - TestClass2.verifyExecutionSummary( executionListener.summaries.get( 1 ) ); + @Test + void outputIsCaptured() + throws Exception + { + Launcher launcher = LauncherFactory.create(); + RunListener runListener = runListenerMock(); + JUnitPlatformProvider provider = + new JUnitPlatformProvider( providerParametersMock( runListener ), launcher ); + + invokeProvider( provider, VerboseTestClass.class ); + + ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass( byte[].class ); + // @formatter:off + verify( (ConsoleOutputReceiver) runListener ) + .writeTestOutput( captor.capture(), eq( 0 ), gt( 6 ), eq( true ) ); + verify( (ConsoleOutputReceiver) runListener ) + .writeTestOutput( captor.capture(), eq( 0 ), gt( 6 ), eq( false ) ); + assertThat( captor.getAllValues() ) + .extracting( bytes -> new String( bytes, 0, 6 ) ) + .containsExactly( "stdout", "stderr" ); + // @formatter:on } @Test @@ -139,16 +239,17 @@ class JUnitPlatformProviderTests } @Test - void bothExcludedGroupsAndExcludeTagsThrowsException() { + void bothExcludedGroupsAndExcludeTagsThrowsException() + { Map<String, String> properties = new HashMap<>(); - properties.put(JUnitPlatformProvider.EXCLUDE_GROUPS, "groupOne, groupTwo"); - properties.put(JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo"); - verifyPreconditionViolationException(properties); + properties.put( JUnitPlatformProvider.EXCLUDE_GROUPS, "groupOne, groupTwo" ); + properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo" ); + verifyPreconditionViolationException( properties ); } @Test void onlyGroupsIsDeclared() - throws Exception + throws Exception { Map<String, String> properties = new HashMap<>(); properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "groupOne, groupTwo" ); @@ -158,12 +259,12 @@ class JUnitPlatformProviderTests JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); - assertEquals( 1, provider.includeAndExcludeFilters.length ); + assertEquals( 1, provider.filters.length ); } @Test void onlyExcludeTagsIsDeclared() - throws Exception + throws Exception { Map<String, String> properties = new HashMap<>(); properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagOne, tagTwo" ); @@ -173,12 +274,44 @@ class JUnitPlatformProviderTests JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); - assertEquals( 1, provider.includeAndExcludeFilters.length ); + assertEquals( 1, provider.filters.length ); + } + + @Test + void noFiltersAreCreatedIfTagsAreEmpty() + throws Exception + { + Map<String, String> properties = new HashMap<>(); + properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "" ); + properties.put( JUnitPlatformProvider.INCLUDE_GROUPS, "" ); + + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ).thenReturn( properties ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + assertEquals( 0, provider.filters.length ); + } + + @Test + void filtersWithEmptyTagsAreNotRegistered() + throws Exception + { + Map<String, String> properties = new HashMap<>(); + + // Here only tagOne is registered as a valid tag and other tags are ignored as they are empty + properties.put( JUnitPlatformProvider.EXCLUDE_GROUPS, "tagOne," ); + properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "" ); + + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ).thenReturn( properties ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + assertEquals( 1, provider.filters.length ); } @Test void bothIncludeAndExcludeAreAllowed() - throws Exception + throws Exception { Map<String, String> properties = new HashMap<>(); properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne, tagTwo" ); @@ -189,18 +322,173 @@ class JUnitPlatformProviderTests JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); - assertEquals( 2, provider.includeAndExcludeFilters.length ); + assertEquals( 2, provider.filters.length ); + } + + @Test + void tagExpressionsAreSupportedForIncludeTagsContainingVerticalBar() + { + Map<String, String> properties = new HashMap<>(); + properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne | tagTwo" ); + properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagThree | tagFour" ); + + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ).thenReturn( properties ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + + assertEquals( 2, provider.filters.length ); + } + + @Test + void tagExpressionsAreSupportedForIncludeTagsContainingAmpersand() + { + Map<String, String> properties = new HashMap<>(); + properties.put( JUnitPlatformProvider.INCLUDE_TAGS, "tagOne & !tagTwo" ); + properties.put( JUnitPlatformProvider.EXCLUDE_TAGS, "tagThree & !tagFour" ); + + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ).thenReturn( properties ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + + assertEquals( 2, provider.filters.length ); } @Test void noFiltersAreCreatedIfNoPropertiesAreDeclared() - throws Exception + throws Exception + { + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + + assertEquals( 0, provider.filters.length ); + } + + @Test + void defaultConfigurationParametersAreEmpty() { ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ).thenReturn( emptyMap() ); JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); - assertEquals( 0, provider.includeAndExcludeFilters.length ); + assertTrue( provider.configurationParameters.isEmpty() ); + } + + @Test + void parsesConfigurationParameters() + { + ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); + when( providerParameters.getProviderProperties() ) + .thenReturn( // + singletonMap( + JUnitPlatformProvider.CONFIGURATION_PARAMETERS, + "foo = true\nbar 42\rbaz: *\r\nqux: EOF" ) ); + + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters ); + + assertEquals( 4, provider.configurationParameters.size() ); + assertEquals( "true", provider.configurationParameters.get( "foo" ) ); + assertEquals( "42", provider.configurationParameters.get( "bar" ) ); + assertEquals( "*", provider.configurationParameters.get( "baz" ) ); + assertEquals( "EOF", provider.configurationParameters.get( "qux" ) ); + } + + @Test + void executesSingleTestIncludedByName() + throws Exception + { + // following is equivalent of adding '-Dtest=TestClass3#prefix1Suffix1' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "*TestClass3#prefix1Suffix1"; + + testExecutionOfMatchingTestMethods( TestClass3.class, pattern, "prefix1Suffix1()" ); + } + + @Test + void executesMultipleTestsIncludedByName() + throws Exception + { + // following is equivalent of adding '-Dtest=TestClass3#prefix1Suffix1+prefix2Suffix1' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "*TestClass3#prefix1Suffix1+prefix2Suffix1"; + + testExecutionOfMatchingTestMethods( + TestClass3.class, pattern, "prefix1Suffix1()", "prefix2Suffix1()" ); + } + + @Test + void executesMultipleTestsIncludedByNamePattern() + throws Exception + { + // following is equivalent of adding '-Dtest=TestClass3#prefix1*' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "*TestClass3#prefix1*"; + + testExecutionOfMatchingTestMethods( + TestClass3.class, pattern, "prefix1Suffix1()", "prefix1Suffix2()" ); + } + + @Test + void executesMultipleTestsIncludedByNamePatternWithQuestionMark() + throws Exception + { + // following is equivalent of adding '-Dtest=TestClass3#prefix?Suffix2' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "*TestClass3#prefix?Suffix2"; + + testExecutionOfMatchingTestMethods( + TestClass3.class, pattern, "prefix1Suffix2()", "prefix2Suffix2()" ); + } + + @Test + void doesNotExecuteTestsExcludedByName() + throws Exception + { + // following is equivalent of adding '-Dtest=!TestClass3#prefix1Suffix2' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "!*TestClass3#prefix1Suffix2"; + + testExecutionOfMatchingTestMethods( + TestClass3.class, pattern, "prefix1Suffix1()", "prefix2Suffix1()", "prefix2Suffix2()" ); + } + + @Test + void doesNotExecuteTestsExcludedByNamePattern() + throws Exception + { + // following is equivalent of adding '-Dtest=!TestClass3#prefix2*' + // '*' needed because it's a nested class and thus has name prefixed with '$' + String pattern = "!*TestClass3#prefix2*"; + + testExecutionOfMatchingTestMethods( + TestClass3.class, pattern, "prefix1Suffix1()", "prefix1Suffix2()" ); + } + + void testExecutionOfMatchingTestMethods( + Class<?> testClass, String pattern, String... expectedTestNames ) + throws Exception + { + TestListResolver testListResolver = new TestListResolver( pattern ); + ProviderParameters providerParameters = providerParametersMock( testListResolver, testClass ); + Launcher launcher = LauncherFactory.create(); + JUnitPlatformProvider provider = new JUnitPlatformProvider( providerParameters, launcher ); + + TestPlanSummaryListener executionListener = new TestPlanSummaryListener(); + launcher.registerTestExecutionListeners( executionListener ); + + invokeProvider( provider, null ); + + assertEquals( 1, executionListener.summaries.size() ); + TestExecutionSummary summary = executionListener.summaries.get( 0 ); + int expectedCount = expectedTestNames.length; + assertEquals( expectedCount, summary.getTestsFoundCount() ); + assertEquals( expectedCount, summary.getTestsFailedCount() ); + assertEquals( expectedCount, summary.getFailures().size() ); + + assertThat( failedTestDisplayNames( summary ) ).contains( expectedTestNames ); } private void verifyPreconditionViolationException( Map<String, String> properties ) @@ -208,14 +496,35 @@ class JUnitPlatformProviderTests ProviderParameters providerParameters = providerParametersMock( TestClass1.class ); when( providerParameters.getProviderProperties() ).thenReturn( properties ); - Throwable throwable = assertThrows( PreconditionViolationException.class, () -> - new JUnitPlatformProvider(providerParameters) ); + Throwable throwable = + assertThrows( + PreconditionViolationException.class, + () -> new JUnitPlatformProvider( providerParameters ) ); assertEquals( JUnitPlatformProvider.EXCEPTION_MESSAGE_BOTH_NOT_ALLOWED, throwable.getMessage() ); } private static ProviderParameters providerParametersMock( Class<?>... testClasses ) { + return providerParametersMock( runListenerMock(), testClasses ); + } + + private static ProviderParameters providerParametersMock( + RunListener runListener, Class<?>... testClasses ) + { + TestListResolver testListResolver = new TestListResolver( "" ); + return providerParametersMock( runListener, testListResolver, testClasses ); + } + + private static ProviderParameters providerParametersMock( + TestListResolver testListResolver, Class<?>... testClasses ) + { + return providerParametersMock( runListenerMock(), testListResolver, testClasses ); + } + + private static ProviderParameters providerParametersMock( + RunListener runListener, TestListResolver testListResolver, Class<?>... testClasses ) + { TestsToRun testsToRun = newTestsToRun( testClasses ); ScanResult scanResult = mock( ScanResult.class ); @@ -225,17 +534,37 @@ class JUnitPlatformProviderTests when( runOrderCalculator.orderTestClasses( any() ) ).thenReturn( testsToRun ); ReporterFactory reporterFactory = mock( ReporterFactory.class ); - RunListener runListener = mock( RunListener.class ); when( reporterFactory.createReporter() ).thenReturn( runListener ); + TestRequest testRequest = mock( TestRequest.class ); + when( testRequest.getTestListResolver() ).thenReturn( testListResolver ); + ProviderParameters providerParameters = mock( ProviderParameters.class ); when( providerParameters.getScanResult() ).thenReturn( scanResult ); when( providerParameters.getRunOrderCalculator() ).thenReturn( runOrderCalculator ); when( providerParameters.getReporterFactory() ).thenReturn( reporterFactory ); + when( providerParameters.getTestRequest() ).thenReturn( testRequest ); return providerParameters; } + private static RunListener runListenerMock() + { + return mock( RunListener.class, withSettings().extraInterfaces( ConsoleOutputReceiver.class ) ); + } + + private static Set<String> failedTestDisplayNames( TestExecutionSummary summary ) + { + // @formatter:off + return summary + .getFailures() + .stream() + .map( Failure::getTestIdentifier ) + .map( TestIdentifier::getDisplayName ) + .collect( toSet() ); + // @formatter:on + } + private static TestsToRun newTestsToRun( Class<?>... testClasses ) { List<Class<?>> classesList = Arrays.asList( testClasses ); @@ -243,7 +572,7 @@ class JUnitPlatformProviderTests } private class TestPlanSummaryListener - extends SummaryGeneratingListener + extends SummaryGeneratingListener { final List<TestExecutionSummary> summaries = new ArrayList<>(); @@ -256,9 +585,42 @@ class JUnitPlatformProviderTests } } - private static class TestClass1 + /** + * Invokes the provider, then restores system out and system error. + * + * @see <a href="https://github.com/junit-team/junit5/issues/986">#986</a> + */ + private void invokeProvider( JUnitPlatformProvider provider, Object forkTestSet ) + throws TestSetFailedException, InvocationTargetException + { + PrintStream systemOut = System.out; + PrintStream systemErr = System.err; + try + { + provider.invoke( forkTestSet ); + } + finally + { + System.setOut( systemOut ); + System.setErr( systemErr ); + } + } + + static class TestClass1 { + static final int TESTS_FOUND = 4; + + static final int TESTS_STARTED = 3; + + static final int TESTS_SKIPPED = 1; + + static final int TESTS_SUCCEEDED = 2; + + static final int TESTS_ABORTED = 0; + + static final int TESTS_FAILED = 1; + @Test void test1() { @@ -280,21 +642,23 @@ class JUnitPlatformProviderTests { throw new RuntimeException(); } - - static void verifyExecutionSummary( TestExecutionSummary summary ) - { - assertEquals( 4, summary.getTestsFoundCount() ); - assertEquals( 3, summary.getTestsStartedCount() ); - assertEquals( 2, summary.getTestsSucceededCount() ); - assertEquals( 1, summary.getTestsSkippedCount() ); - assertEquals( 0, summary.getTestsAbortedCount() ); - assertEquals( 1, summary.getTestsFailedCount() ); - } } - private static class TestClass2 + static class TestClass2 { + static final int TESTS_FOUND = 3; + + static final int TESTS_STARTED = 3; + + static final int TESTS_SKIPPED = 0; + + static final int TESTS_SUCCEEDED = 1; + + static final int TESTS_ABORTED = 1; + + static final int TESTS_FAILED = 1; + @Test void test1() { @@ -311,15 +675,82 @@ class JUnitPlatformProviderTests { assumeTrue( false ); } + } + + static class VerboseTestClass + { + @Test + void test() + { + System.out.println( "stdout" ); + System.err.println( "stderr" ); + } + } + + @Test + void usesClassNamesForXmlReport() + throws TestSetFailedException, InvocationTargetException + { + String[] classNames = { Sub1Tests.class.getName(), Sub2Tests.class.getName() }; + ProviderParameters providerParameters = + providerParametersMock( Sub1Tests.class, Sub2Tests.class ); + + JUnitPlatformProvider jUnitPlatformProvider = new JUnitPlatformProvider( providerParameters ); + TestsToRun testsToRun = newTestsToRun( Sub1Tests.class, Sub2Tests.class ); + + invokeProvider( jUnitPlatformProvider, testsToRun ); + RunListener reporter = providerParameters.getReporterFactory().createReporter(); + + ArgumentCaptor<ReportEntry> reportEntryArgumentCaptor = + ArgumentCaptor.forClass( ReportEntry.class ); + verify( reporter, times( 2 ) ).testSucceeded( reportEntryArgumentCaptor.capture() ); + + List<ReportEntry> allValues = reportEntryArgumentCaptor.getAllValues(); + assertThat( allValues ).extracting( ReportEntry::getSourceName ).containsExactly( classNames ); + } + + static class AbstractTestClass + { + @Test + void test() + { + } + } + + static class Sub1Tests + extends AbstractTestClass + { + } + + static class Sub2Tests + extends AbstractTestClass + { + } + + static class TestClass3 + { + @Test + void prefix1Suffix1() + { + throw new RuntimeException(); + } - static void verifyExecutionSummary( TestExecutionSummary summary ) + @Test + void prefix2Suffix1() + { + throw new RuntimeException(); + } + + @Test + void prefix1Suffix2() { - assertEquals( 3, summary.getTestsFoundCount() ); - assertEquals( 3, summary.getTestsStartedCount() ); - assertEquals( 1, summary.getTestsSucceededCount() ); - assertEquals( 0, summary.getTestsSkippedCount() ); - assertEquals( 1, summary.getTestsAbortedCount() ); - assertEquals( 1, summary.getTestsFailedCount() ); + throw new RuntimeException(); + } + + @Test + void prefix2Suffix2() + { + throw new RuntimeException(); } } } diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java index 0a37eb5..d3ec7a4 100644 --- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java +++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/RunListenerAdapterTests.java @@ -19,31 +19,45 @@ package org.apache.maven.surefire.junitplatform; * under the License. */ -import static java.util.Collections.singletonList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.engine.TestExecutionResult.successful; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import java.util.Collections; import java.util.Optional; import org.apache.maven.surefire.report.ReportEntry; import org.apache.maven.surefire.report.RunListener; +import org.apache.maven.surefire.report.SimpleReportEntry; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor; import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestDescriptor.Type; import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.TestSource; import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; +import org.junit.platform.engine.support.descriptor.ClassSource; import org.junit.platform.engine.support.descriptor.EngineDescriptor; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; /** * Unit tests for {@link RunListenerAdapter}. @@ -52,7 +66,9 @@ import org.mockito.ArgumentCaptor; */ class RunListenerAdapterTests { + private RunListener listener; + private RunListenerAdapter adapter; @BeforeEach @@ -60,32 +76,183 @@ class RunListenerAdapterTests { listener = mock( RunListener.class ); adapter = new RunListenerAdapter( listener ); + adapter.testPlanExecutionStarted( TestPlan.from( emptyList() ) ); } @Test void notifiedWithCorrectNamesWhenMethodExecutionStarted() - throws Exception + throws Exception { ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); - adapter.executionStarted( newMethodIdentifier() ); + TestPlan testPlan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); + adapter.testPlanExecutionStarted( testPlan ); + + TestIdentifier methodIdentifier = + identifiersAsParentOnTestPlan( testPlan, newClassDescriptor(), newMethodDescriptor() ); + + adapter.executionStarted( methodIdentifier ); verify( listener ).testStarting( entryCaptor.capture() ); ReportEntry entry = entryCaptor.getValue(); - assertEquals( MY_TEST_METHOD_NAME + "()", entry.getName() ); + assertEquals( MY_TEST_METHOD_NAME, entry.getName() ); assertEquals( MyTestClass.class.getName(), entry.getSourceName() ); - assertNotNull( entry.getStackTraceWriter() ); + assertNull( entry.getStackTraceWriter() ); } @Test - void notNotifiedWhenClassExecutionStarted() + void notifiedWithCompatibleNameForMethodWithArguments() + throws Exception { - adapter.executionStarted( newClassIdentifier() ); - verify( listener, never() ).testStarting( any() ); + ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + + TestPlan testPlan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); + adapter.testPlanExecutionStarted( testPlan ); + + TestIdentifier methodIdentifier = + identifiersAsParentOnTestPlan( + testPlan, newClassDescriptor(), newMethodDescriptor( String.class ) ); + + adapter.executionStarted( methodIdentifier ); + verify( listener ).testStarting( entryCaptor.capture() ); + + ReportEntry entry = entryCaptor.getValue(); + assertEquals( MY_TEST_METHOD_NAME + "{String}", entry.getName() ); + assertEquals( MyTestClass.class.getName(), entry.getSourceName() ); + assertNull( entry.getStackTraceWriter() ); + } + + @Test + void notifiedEagerlyForTestSetWhenClassExecutionStarted() + throws Exception + { + EngineDescriptor engine = newEngineDescriptor(); + TestDescriptor parent = newClassDescriptor(); + engine.addChild( parent ); + TestDescriptor child = newMethodDescriptor(); + parent.addChild( child ); + TestPlan plan = TestPlan.from( Collections.singletonList( engine ) ); + + adapter.testPlanExecutionStarted( plan ); + adapter.executionStarted( TestIdentifier.from( engine ) ); + adapter.executionStarted( TestIdentifier.from( parent ) ); + verify( listener ) + .testSetStarting( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + MyTestClass.class.getName() ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionStarted( TestIdentifier.from( child ) ); + verify( listener ) + .testStarting( new SimpleReportEntry( MyTestClass.class.getName(), MY_TEST_METHOD_NAME ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( child ), successful() ); + verify( listener ) + .testSucceeded( new SimpleReportEntry( MyTestClass.class.getName(), MY_TEST_METHOD_NAME ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( parent ), successful() ); + verify( listener ) + .testSetCompleted( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + MyTestClass.class.getName() ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( engine ), successful() ); + verifyNoMoreInteractions( listener ); + } + + @Test + void notifiedLazilyForTestSetWhenFirstTestWithoutClassDescriptorParentStarted() + { + EngineDescriptor engine = newEngineDescriptor(); + TestDescriptor parent = + newTestDescriptor( + engine.getUniqueId().append( "container", "noClass" ), "parent", + Type.CONTAINER ); + engine.addChild( parent ); + TestDescriptor child1 = + newTestDescriptor( parent.getUniqueId().append( "test", "child1" ), "child1", Type.TEST ); + parent.addChild( child1 ); + TestDescriptor child2 = + newTestDescriptor( parent.getUniqueId().append( "test", "child2" ), "child2", Type.TEST ); + parent.addChild( child2 ); + TestPlan plan = TestPlan.from( Collections.singletonList( engine ) ); + + adapter.testPlanExecutionStarted( plan ); + adapter.executionStarted( TestIdentifier.from( engine ) ); + adapter.executionStarted( TestIdentifier.from( parent ) ); + verifyZeroInteractions( listener ); + + adapter.executionStarted( TestIdentifier.from( child1 ) ); + InOrder inOrder = inOrder( listener ); + inOrder + .verify( listener ) + .testSetStarting( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "parent" ) ); + inOrder.verify( listener ).testStarting( new SimpleReportEntry( "parent", "child1" ) ); + inOrder.verifyNoMoreInteractions(); + + adapter.executionFinished( TestIdentifier.from( child1 ), successful() ); + verify( listener ).testSucceeded( new SimpleReportEntry( "parent", "child1" ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionStarted( TestIdentifier.from( child2 ) ); + verify( listener ).testStarting( new SimpleReportEntry( "parent", "child2" ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( child2 ), successful() ); + verify( listener ).testSucceeded( new SimpleReportEntry( "parent", "child2" ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( parent ), successful() ); + verify( listener ) + .testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "parent" ) ); + verifyNoMoreInteractions( listener ); + + adapter.executionFinished( TestIdentifier.from( engine ), successful() ); + verifyNoMoreInteractions( listener ); + } + + @Test + void notifiedForTestSetForSingleNodeEngine() + { + EngineDescriptor engine = + new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" ) + { + @Override + public Type getType() + { + return Type.TEST; + } + }; + TestPlan plan = TestPlan.from( Collections.singletonList( engine ) ); + + adapter.testPlanExecutionStarted( plan ); + adapter.executionStarted( TestIdentifier.from( engine ) ); + InOrder inOrder = inOrder( listener ); + inOrder + .verify( listener ) + .testSetStarting( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "engine" ) ); + inOrder.verify( listener ).testStarting( new SimpleReportEntry( "<unrooted>", "engine" ) ); + inOrder.verifyNoMoreInteractions(); + + adapter.executionFinished( TestIdentifier.from( engine ), successful() ); + inOrder = inOrder( listener ); + inOrder.verify( listener ).testSucceeded( new SimpleReportEntry( "<unrooted>", "engine" ) ); + inOrder + .verify( listener ) + .testSetCompleted( new SimpleReportEntry( JUnitPlatformProvider.class.getName(), "engine" ) ); + inOrder.verifyNoMoreInteractions(); } @Test void notNotifiedWhenEngineExecutionStarted() + throws Exception { adapter.executionStarted( newEngineIdentifier() ); verify( listener, never() ).testStarting( any() ); @@ -93,7 +260,7 @@ class RunListenerAdapterTests @Test void notifiedWhenMethodExecutionSkipped() - throws Exception + throws Exception { adapter.executionSkipped( newMethodIdentifier(), "test" ); verify( listener ).testSkipped( any() ); @@ -101,19 +268,27 @@ class RunListenerAdapterTests @Test void notifiedWithCorrectNamesWhenClassExecutionSkipped() + throws Exception { ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + TestPlan testPlan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); + adapter.testPlanExecutionStarted( testPlan ); - adapter.executionSkipped( newClassIdentifier(), "test" ); + TestIdentifier classIdentifier = + identifiersAsParentOnTestPlan( testPlan, newEngineDescriptor(), newClassDescriptor() ); + + adapter.executionSkipped( classIdentifier, "test" ); verify( listener ).testSkipped( entryCaptor.capture() ); ReportEntry entry = entryCaptor.getValue(); assertTrue( MyTestClass.class.getTypeName().contains( entry.getName() ) ); - assertEquals( MyTestClass.class.getName(), entry.getSourceName() ); + assertEquals( MyTestClass.class.getTypeName(), entry.getSourceName() ); } @Test void notifiedWhenEngineExecutionSkipped() + throws Exception { adapter.executionSkipped( newEngineIdentifier(), "test" ); verify( listener ).testSkipped( any() ); @@ -121,7 +296,7 @@ class RunListenerAdapterTests @Test void notifiedWhenMethodExecutionAborted() - throws Exception + throws Exception { adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.aborted( null ) ); verify( listener ).testAssumptionFailure( any() ); @@ -129,57 +304,89 @@ class RunListenerAdapterTests @Test void notifiedWhenClassExecutionAborted() + throws Exception { adapter.executionFinished( newClassIdentifier(), TestExecutionResult.aborted( null ) ); verify( listener ).testAssumptionFailure( any() ); } @Test - void notifiedWhenMethodExecutionFailed() - throws Exception + void notifiedWhenMethodExecutionFailedWithAnAssertionError() + throws Exception { - adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) ); + adapter.executionFinished( + newMethodIdentifier(), TestExecutionResult.failed( new AssertionError() ) ); verify( listener ).testFailed( any() ); } @Test + void notifiedWhenMethodExecutionFailedWithANonAssertionError() + throws Exception + { + adapter.executionFinished( + newMethodIdentifier(), TestExecutionResult.failed( new RuntimeException() ) ); + verify( listener ).testError( any() ); + } + + @Test void notifiedWithCorrectNamesWhenClassExecutionFailed() + throws Exception { ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + TestPlan testPlan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); + adapter.testPlanExecutionStarted( testPlan ); - adapter.executionFinished( newClassIdentifier(), TestExecutionResult.failed( new RuntimeException() ) ); + adapter.executionFinished( + identifiersAsParentOnTestPlan( testPlan, newEngineDescriptor(), newClassDescriptor() ), + TestExecutionResult.failed( new AssertionError() ) ); verify( listener ).testFailed( entryCaptor.capture() ); ReportEntry entry = entryCaptor.getValue(); - assertEquals( MyTestClass.class.getName(), entry.getSourceName() ); + assertEquals( MyTestClass.class.getTypeName(), entry.getSourceName() ); assertNotNull( entry.getStackTraceWriter() ); } @Test void notifiedWhenMethodExecutionSucceeded() - throws Exception + throws Exception { - adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.successful() ); + adapter.executionFinished( newMethodIdentifier(), successful() ); verify( listener ).testSucceeded( any() ); } @Test - void notNotifiedWhenClassExecutionSucceeded() + void notifiedForTestSetWhenClassExecutionSucceeded() + throws Exception { - adapter.executionFinished( newClassIdentifier(), TestExecutionResult.successful() ); + EngineDescriptor engineDescriptor = newEngineDescriptor(); + TestDescriptor classDescriptor = newClassDescriptor(); + engineDescriptor.addChild( classDescriptor ); + adapter.testPlanExecutionStarted( TestPlan.from( singleton( engineDescriptor ) ) ); + adapter.executionStarted( TestIdentifier.from( classDescriptor ) ); + + adapter.executionFinished( TestIdentifier.from( classDescriptor ), successful() ); + + verify( listener ) + .testSetCompleted( + new SimpleReportEntry( + JUnitPlatformProvider.class.getName(), + MyTestClass.class.getName() ) ); verify( listener, never() ).testSucceeded( any() ); } @Test void notifiedWithParentDisplayNameWhenTestClassUnknown() + throws Exception { // Set up a test plan - TestPlan plan = TestPlan.from( singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); + TestPlan plan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Luke's Plan" ) ) ); adapter.testPlanExecutionStarted( plan ); // Use the test plan to set up child with parent. final String parentDisplay = "I am your father"; - TestIdentifier child = newSourcelessIdentifierWithParent( plan, parentDisplay ); + TestIdentifier child = newSourcelessChildIdentifierWithParent( plan, parentDisplay, null ); adapter.executionStarted( child ); // Check that the adapter has informed Surefire that the test has been invoked, @@ -189,36 +396,117 @@ class RunListenerAdapterTests assertEquals( parentDisplay, entryCaptor.getValue().getSourceName() ); } + @Test + void stackTraceWriterPresentWhenParentHasSource() + throws Exception + { + TestPlan plan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Some Plan" ) ) ); + adapter.testPlanExecutionStarted( plan ); + + TestIdentifier child = + newSourcelessChildIdentifierWithParent( plan, "Parent", ClassSource.from( MyTestClass.class ) ); + adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException() ) ); + ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + verify( listener ).testError( entryCaptor.capture() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter() ); + } + + @Test + void stackTraceWriterDefaultsToTestClass() + throws Exception + { + TestPlan plan = + TestPlan.from( Collections.singletonList( new EngineDescriptor( newId(), "Some Plan" ) ) ); + adapter.testPlanExecutionStarted( plan ); + + TestIdentifier child = newSourcelessChildIdentifierWithParent( plan, "Parent", null ); + adapter.executionFinished( child, TestExecutionResult.failed( new RuntimeException( "message" ) ) ); + ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + verify( listener ).testError( entryCaptor.capture() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter().smartTrimmedStackTrace() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter().writeTraceToString() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter().writeTrimmedTraceToString() ); + } + + @Test + void stackTraceWriterPresentEvenWithoutException() + throws Exception + { + adapter.executionFinished( newMethodIdentifier(), TestExecutionResult.failed( null ) ); + ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + verify( listener ).testError( entryCaptor.capture() ); + assertNotNull( entryCaptor.getValue().getStackTraceWriter() ); + } + + @Test + void displayNamesIgnoredInReport() + throws NoSuchMethodException + { + TestMethodTestDescriptor descriptor = + new TestMethodTestDescriptor( + newId(), MyTestClass.class, + MyTestClass.class.getDeclaredMethod( "myNamedTestMethod" ) ); + + TestIdentifier factoryIdentifier = TestIdentifier.from( descriptor ); + ArgumentCaptor<ReportEntry> entryCaptor = ArgumentCaptor.forClass( ReportEntry.class ); + + adapter.executionSkipped( factoryIdentifier, "" ); + verify( listener ).testSkipped( entryCaptor.capture() ); + + ReportEntry value = entryCaptor.getValue(); + + assertEquals( "myNamedTestMethod", value.getName() ); + } + private static TestIdentifier newMethodIdentifier() - throws Exception + throws Exception { - TestDescriptor testDescriptor = new TestMethodTestDescriptor( newId(), MyTestClass.class, - MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME ) ); - return TestIdentifier.from( testDescriptor ); + return TestIdentifier.from( newMethodDescriptor() ); + } + + private static TestDescriptor newMethodDescriptor( Class<?>... parameterTypes ) + throws Exception + { + return new TestMethodTestDescriptor( + UniqueId.forEngine( "method" ), + MyTestClass.class, + MyTestClass.class.getDeclaredMethod( MY_TEST_METHOD_NAME, parameterTypes ) ); } private static TestIdentifier newClassIdentifier() { - TestDescriptor testDescriptor = new ClassTestDescriptor( newId(), MyTestClass.class ); - return TestIdentifier.from( testDescriptor ); + return TestIdentifier.from( newClassDescriptor() ); } - private static TestIdentifier newSourcelessIdentifierWithParent( TestPlan testPlan, String parentDisplay ) + private static TestDescriptor newClassDescriptor() + { + return new ClassTestDescriptor( + UniqueId.root( "class", MyTestClass.class.getName() ), MyTestClass.class ); + } + + private static TestIdentifier newSourcelessChildIdentifierWithParent( + TestPlan testPlan, String parentDisplay, TestSource parentTestSource ) { // A parent test identifier with a name. TestDescriptor parent = mock( TestDescriptor.class ); when( parent.getUniqueId() ).thenReturn( newId() ); when( parent.getDisplayName() ).thenReturn( parentDisplay ); + when( parent.getLegacyReportingName() ).thenReturn( parentDisplay ); + when( parent.getSource() ).thenReturn( Optional.ofNullable( parentTestSource ) ); + when( parent.getType() ).thenReturn( Type.CONTAINER ); TestIdentifier parentId = TestIdentifier.from( parent ); // The (child) test case that is to be executed as part of a test plan. TestDescriptor child = mock( TestDescriptor.class ); when( child.getUniqueId() ).thenReturn( newId() ); - when( child.isTest() ).thenReturn( true ); + when( child.getType() ).thenReturn( Type.TEST ); + when( child.getLegacyReportingName() ).thenReturn( "child" ); // Ensure the child source is null yet that there is a parent -- the special case to be tested. when( child.getSource() ).thenReturn( Optional.empty() ); - when( child.getParent() ).thenReturn( Optional.of(parent) ); + when( child.getParent() ).thenReturn( Optional.of( parent ) ); TestIdentifier childId = TestIdentifier.from( child ); testPlan.add( childId ); @@ -229,10 +517,41 @@ class RunListenerAdapterTests private static TestIdentifier newEngineIdentifier() { - TestDescriptor testDescriptor = new EngineDescriptor( newId(), "engine" ); + TestDescriptor testDescriptor = newEngineDescriptor(); return TestIdentifier.from( testDescriptor ); } + private static EngineDescriptor newEngineDescriptor() + { + return new EngineDescriptor( UniqueId.forEngine( "engine" ), "engine" ); + } + + private TestDescriptor newTestDescriptor( UniqueId uniqueId, String displayName, Type type ) + { + return new AbstractTestDescriptor( uniqueId, displayName ) + { + @Override + public Type getType() + { + return type; + } + }; + } + + private static TestIdentifier identifiersAsParentOnTestPlan( + TestPlan plan, TestDescriptor parent, TestDescriptor child ) + { + child.setParent( parent ); + + TestIdentifier parentIdentifier = TestIdentifier.from( parent ); + TestIdentifier childIdentifier = TestIdentifier.from( child ); + + plan.add( parentIdentifier ); + plan.add( childIdentifier ); + + return childIdentifier; + } + private static UniqueId newId() { return UniqueId.forEngine( "engine" ); @@ -240,13 +559,22 @@ class RunListenerAdapterTests private static final String MY_TEST_METHOD_NAME = "myTestMethod"; - private static class MyTestClass { - + private static class MyTestClass + { @Test void myTestMethod() { } - } + @Test + void myTestMethod( String foo ) + { + } + @DisplayName( "name" ) + @Test + void myNamedTestMethod() + { + } + } } diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java new file mode 100644 index 0000000..29ca326 --- /dev/null +++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestMethodFilterTests.java @@ -0,0 +1,105 @@ +package org.apache.maven.surefire.junitplatform; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import static org.apache.maven.surefire.testset.TestListResolver.toClassFileName; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; + +import org.apache.maven.surefire.testset.TestListResolver; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.engine.descriptor.ClassTestDescriptor; +import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor; +import org.junit.platform.engine.FilterResult; +import org.junit.platform.engine.UniqueId; + +/** + * Unit tests for {@link TestMethodFilter}. + * + * @since 1.0.3 + */ +class TestMethodFilterTests +{ + + private final TestListResolver resolver = mock( TestListResolver.class ); + + private final TestMethodFilter filter = new TestMethodFilter( this.resolver ); + + @Test + void includesBasedOnTestListResolver() + throws Exception + { + when( resolver.shouldRun( toClassFileName( TestClass.class ), "testMethod" ) ).thenReturn( true ); + + FilterResult result = filter.apply( newTestMethodDescriptor() ); + + assertTrue( result.included() ); + assertFalse( result.excluded() ); + } + + @Test + void excludesBasedOnTestListResolver() + throws Exception + { + when( resolver.shouldRun( toClassFileName( TestClass.class ), "testMethod" ) ).thenReturn( false ); + + FilterResult result = filter.apply( newTestMethodDescriptor() ); + + assertFalse( result.included() ); + assertTrue( result.excluded() ); + } + + @Test + void includesTestDescriptorWithClassSource() + throws Exception + { + FilterResult result = filter.apply( newClassTestDescriptor() ); + + assertTrue( result.included() ); + assertFalse( result.excluded() ); + } + + private static TestMethodTestDescriptor newTestMethodDescriptor() + throws Exception + { + UniqueId uniqueId = UniqueId.forEngine( "method" ); + Class<TestClass> testClass = TestClass.class; + Method testMethod = testClass.getMethod( "testMethod" ); + return new TestMethodTestDescriptor( uniqueId, testClass, testMethod ); + } + + private static ClassTestDescriptor newClassTestDescriptor() + throws Exception + { + UniqueId uniqueId = UniqueId.forEngine( "class" ); + return new ClassTestDescriptor( uniqueId, TestClass.class ); + } + + public static class TestClass + { + public void testMethod() + { + } + } +} diff --git a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java index 98f5b2b..8ecedc7 100644 --- a/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java +++ b/surefire-providers/surefire-junit-platform/src/test/java/org/apache/maven/surefire/junitplatform/TestPlanScannerFilterTests.java @@ -20,6 +20,7 @@ package org.apache.maven.surefire.junitplatform; */ import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -37,20 +38,20 @@ import org.junit.platform.launcher.core.LauncherFactory; * * @since 1.0 */ -public class TestPlanScannerFilterTests +class TestPlanScannerFilterTests { @Test - void emptyClassAccepted() + void emptyClassIsNotAccepted() { - assertTrue( newFilter().accept( EmptyClass.class ), "accepts empty class because it is a container" ); + assertFalse( newFilter().accept( EmptyClass.class ), "does not accept empty class" ); } @Test - void classWithNoTestMethodsIsAccepted() + void classWithNoTestMethodsIsNotAccepted() { - assertTrue( newFilter().accept( ClassWithMethods.class ), - "accepts class with no @Test methods because it is a container" ); + assertFalse( + newFilter().accept( ClassWithMethods.class ), "does not accept class with no @Test methods" ); } @Test @@ -88,12 +89,11 @@ public class TestPlanScannerFilterTests return new TestPlanScannerFilter( LauncherFactory.create(), new Filter<?>[0] ); } - private static class EmptyClass + static class EmptyClass { } - @SuppressWarnings("unused") - private static class ClassWithMethods + static class ClassWithMethods { void method1() @@ -105,7 +105,7 @@ public class TestPlanScannerFilterTests } } - private static class ClassWithTestMethods + static class ClassWithTestMethods { @Test @@ -119,10 +119,9 @@ public class TestPlanScannerFilterTests } } - private static class ClassWithNestedTestClass + static class ClassWithNestedTestClass { - @SuppressWarnings("unused") void method() { } @@ -138,7 +137,7 @@ public class TestPlanScannerFilterTests } } - private static class ClassWithDeeplyNestedTestClass + static class ClassWithDeeplyNestedTestClass { @Nested @@ -162,7 +161,7 @@ public class TestPlanScannerFilterTests } } - private static class ClassWithTestFactory + static class ClassWithTestFactory { @TestFactory @@ -172,7 +171,7 @@ public class TestPlanScannerFilterTests } } - private static class ClassWithNestedTestFactory + static class ClassWithNestedTestFactory { @Nested @@ -186,5 +185,4 @@ public class TestPlanScannerFilterTests } } } - } -- To stop receiving notification emails like this one, please contact tibordig...@apache.org.