The wildcard character * can be used within the sub parts of those composite identifiers to
+ * do glob-like pattern matching. The classifier may be omitted when matching dependencies without a classifier.
+ *
+ *
Examples:
+ *
+ *
+ *
{@code group} or, equivalently, {@code group:*}
+ *
{@code g*p:*rtifac*}
+ *
{@code group:*:jar}
+ *
{@code group:artifact:*:1.0.0} (no classifier)
+ *
{@code group:*:test-jar:tests}
+ *
{@code *:artifact:*:*:1.0.0}
+ *
+ *
+ *
Since version 2.22.0 you can scan for test classes from a project
+ * dependency of your multi-module project.
+ *
+ *
In versions before 3.0.0-M4, only groupId:artifactId is supported.
*
* @since 2.15
*/
@@ -744,6 +778,9 @@ public abstract class AbstractSurefireMojo
@Component
private RepositorySystem repositorySystem;
+ @Component
+ private DependencyResolver dependencyResolver;
+
private Artifact surefireBooterArtifact;
private Toolchain toolchain;
@@ -777,6 +814,8 @@ public abstract class AbstractSurefireMojo
*/
protected abstract boolean hasSuiteXmlFiles();
+ protected abstract String[] getExcludedEnvironmentVariables();
+
public abstract File[] getSuiteXmlFiles();
public abstract void setSuiteXmlFiles( File[] suiteXmlFiles );
@@ -798,6 +837,8 @@ protected abstract void handleSummary( RunResult summary, Exception firstForkExc
protected abstract void setUseModulePath( boolean useModulePath );
+ protected abstract String getEnableProcessChecker();
+
/**
* This plugin MOJO artifact.
*
@@ -805,7 +846,7 @@ protected abstract void handleSummary( RunResult summary, Exception firstForkExc
*/
protected Artifact getMojoArtifact()
{
- return pluginDescriptor.getPluginArtifact();
+ return getPluginDescriptor().getPluginArtifact();
}
private String getDefaultExcludes()
@@ -813,7 +854,7 @@ private String getDefaultExcludes()
return "**/*$*";
}
- private SurefireDependencyResolver dependencyResolver;
+ private SurefireDependencyResolver surefireDependencyResolver;
private TestListResolver specificTests;
@@ -849,6 +890,11 @@ public void execute()
}
}
+ void setLogger( Logger logger )
+ {
+ this.logger = logger;
+ }
+
@Nonnull
protected final PluginConsoleLogger getConsoleLogger()
{
@@ -867,9 +913,22 @@ protected final PluginConsoleLogger getConsoleLogger()
private void setupStuff()
{
- createDependencyResolver();
- surefireBooterArtifact = getSurefireBooterArtifact();
- toolchain = getToolchain();
+ surefireDependencyResolver = new SurefireDependencyResolver( getRepositorySystem(),
+ getConsoleLogger(), getLocalRepository(),
+ getRemoteRepositories(),
+ getProjectRemoteRepositories(),
+ getPluginName(), getDependencyResolver() );
+
+ surefireBooterArtifact = getBooterArtifact();
+ if ( surefireBooterArtifact == null )
+ {
+ throw new RuntimeException( "Unable to locate surefire-booter in the list of plugin artifacts" );
+ }
+
+ if ( getToolchainManager() != null )
+ {
+ toolchain = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
+ }
}
@Nonnull
@@ -973,6 +1032,7 @@ boolean verifyParameters()
}
else
{
+ ensureEnableProcessChecker();
convertDeprecatedForkMode();
ensureWorkingDirectoryExists();
ensureParallelRunningCompatibility();
@@ -990,7 +1050,8 @@ boolean verifyParameters()
private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanResult )
throws MojoExecutionException, MojoFailureException
{
- List providers = createProviders();
+ TestClassPath testClasspath = generateTestClasspath();
+ List providers = createProviders( testClasspath );
RunResult current = noTestsRun();
@@ -999,7 +1060,7 @@ private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanRe
{
try
{
- current = current.aggregate( executeProvider( provider, scanResult ) );
+ current = current.aggregate( executeProvider( provider, scanResult, testClasspath ) );
}
catch ( SurefireBooterForkException | SurefireExecutionException | TestSetFailedException e )
{
@@ -1018,21 +1079,13 @@ private void executeAfterPreconditionsChecked( @Nonnull DefaultScanResult scanRe
handleSummary( current, firstForkException );
}
- private void createDependencyResolver()
- {
- dependencyResolver = new SurefireDependencyResolver( getRepositorySystem(), getArtifactFactory(),
- getConsoleLogger(), getLocalRepository(),
- getRemoteRepositories(),
- getPluginName() );
- }
-
- protected List createProviders()
+ protected List createProviders( TestClassPath testClasspath )
throws MojoExecutionException
{
Artifact junitDepArtifact = getJunitDepArtifact();
return new ProviderList( new DynamicProviderInfo( null ),
new TestNgProviderInfo( getTestNgArtifact() ),
- new JUnitPlatformProviderInfo( getJunitPlatformArtifact() ),
+ new JUnitPlatformProviderInfo( getJunitPlatformArtifact(), testClasspath ),
new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ),
new JUnit3ProviderInfo() )
@@ -1124,7 +1177,8 @@ private void showToLog( SurefireProperties props, ConsoleLogger log )
}
@Nonnull
- private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult )
+ private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull DefaultScanResult scanResult,
+ @Nonnull TestClassPath testClasspathWrapper )
throws MojoExecutionException, MojoFailureException, SurefireExecutionException, SurefireBooterForkException,
TestSetFailedException
{
@@ -1139,8 +1193,8 @@ private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull Defa
{
createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 ).copyToSystemProperties();
- InPluginVMSurefireStarter surefireStarter =
- createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters, scanResult, platform );
+ InPluginVMSurefireStarter surefireStarter = createInprocessStarter( provider, classLoaderConfiguration,
+ runOrderParameters, scanResult, platform, testClasspathWrapper );
return surefireStarter.runSuitesInProcess( scanResult );
}
else
@@ -1149,6 +1203,7 @@ private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull Defa
if ( getConsoleLogger().isDebugEnabled() )
{
showMap( getEnvironmentVariables(), "environment variable" );
+ showArray( getExcludedEnvironmentVariables(), "excluded environment variable" );
}
Properties originalSystemProperties = (Properties) System.getProperties().clone();
@@ -1156,7 +1211,8 @@ private RunResult executeProvider( @Nonnull ProviderInfo provider, @Nonnull Defa
try
{
forkStarter = createForkStarter( provider, forkConfiguration, classLoaderConfiguration,
- runOrderParameters, getConsoleLogger(), scanResult, platform );
+ runOrderParameters, getConsoleLogger(), scanResult, platform,
+ testClasspathWrapper );
return forkStarter.run( effectiveProperties, scanResult );
}
@@ -1221,16 +1277,14 @@ public void setRepositorySystem( RepositorySystem repositorySystem )
this.repositorySystem = repositorySystem;
}
- final Toolchain getToolchain()
+ public DependencyResolver getDependencyResolver()
{
- Toolchain tc = null;
-
- if ( getToolchainManager() != null )
- {
- tc = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
- }
+ return dependencyResolver;
+ }
- return tc;
+ public void setDependencyResolver( DependencyResolver dependencyResolver )
+ {
+ this.dependencyResolver = dependencyResolver;
}
private boolean existsModuleDescriptor()
@@ -1695,7 +1749,7 @@ private static Map toStringProperties( Properties properties )
return h;
}
- public File getStatisticsFile( String configurationHash )
+ private File getStatisticsFile( String configurationHash )
{
return new File( getBasedir(), ".surefire-" + configurationHash );
}
@@ -1703,7 +1757,8 @@ public File getStatisticsFile( String configurationHash )
private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo provider, boolean isInprocess,
@Nonnull ClassLoaderConfiguration classLoaderConfiguration,
@Nonnull DefaultScanResult scanResult,
- @Nonnull Platform platform )
+ @Nonnull Platform platform,
+ @Nonnull TestClassPath testClasspathWrapper )
throws MojoExecutionException
{
try
@@ -1721,11 +1776,12 @@ private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo p
.toString();
return newStartupConfigWithModularPath( classLoaderConfiguration, providerArtifacts, providerName,
- getModuleDescriptor(), scanResult, javaHome );
+ getModuleDescriptor(), scanResult, javaHome, testClasspathWrapper );
}
else
{
- return newStartupConfigWithClasspath( classLoaderConfiguration, providerArtifacts, providerName );
+ return newStartupConfigWithClasspath( classLoaderConfiguration, providerArtifacts, providerName,
+ testClasspathWrapper );
}
}
catch ( IOException e )
@@ -1736,17 +1792,14 @@ private StartupConfiguration createStartupConfiguration( @Nonnull ProviderInfo p
private StartupConfiguration newStartupConfigWithClasspath(
@Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set providerArtifacts,
- @Nonnull String providerName )
+ @Nonnull String providerName, @Nonnull TestClassPath testClasspathWrapper )
{
- TestClassPath testClasspathWrapper = generateTestClasspath();
Classpath testClasspath = testClasspathWrapper.toClasspath();
- testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
-
- Classpath providerClasspath = ClasspathCache.getCachedClassPath( providerName );
+ Classpath providerClasspath = classpathCache.getCachedClassPath( providerName );
if ( providerClasspath == null )
{
- providerClasspath = ClasspathCache.setCachedClasspath( providerName, providerArtifacts );
+ providerClasspath = classpathCache.setCachedClasspath( providerName, providerArtifacts );
}
getConsoleLogger().debug( testClasspath.getLogMessage( "test classpath:" ) );
@@ -1754,7 +1807,8 @@ private StartupConfiguration newStartupConfigWithClasspath(
getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact) classpath:" ) );
getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact) classpath:" ) );
- Artifact[] additionalInProcArtifacts = { getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() };
+ Artifact[] additionalInProcArtifacts =
+ { getCommonArtifact(), getExtensionsArtifact(), getApiArtifact(), getLoggerApiArtifact() };
Set inProcArtifacts = retainInProcArtifactsUnique( providerArtifacts, additionalInProcArtifacts );
Classpath inProcClasspath = createInProcClasspath( providerClasspath, inProcArtifacts );
getConsoleLogger().debug( inProcClasspath.getLogMessage( "in-process classpath:" ) );
@@ -1764,7 +1818,7 @@ private StartupConfiguration newStartupConfigWithClasspath(
inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation() );
return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, isForking(),
- false );
+ false, ProcessCheckerType.toEnum( getEnableProcessChecker() ) );
}
private static Set retainInProcArtifactsUnique( Set providerArtifacts,
@@ -1776,8 +1830,8 @@ private static Set retainInProcArtifactsUnique( Set provider
boolean contains = false;
for ( Artifact providerArtifact : providerArtifacts )
{
- if ( providerArtifact.getGroupId().equals( inPluginArtifact.getGroupId() )
- && providerArtifact.getArtifactId().equals( inPluginArtifact.getArtifactId() ) )
+ if ( hasGroupArtifactId( providerArtifact.getGroupId(), providerArtifact.getArtifactId(),
+ inPluginArtifact ) )
{
contains = true;
break;
@@ -1791,6 +1845,11 @@ private static Set retainInProcArtifactsUnique( Set provider
return result;
}
+ private static boolean hasGroupArtifactId( String groupId, String artifactId, Artifact artifact )
+ {
+ return groupId.equals( artifact.getGroupId() ) && artifactId.equals( artifact.getArtifactId() );
+ }
+
private static Classpath createInProcClasspath( Classpath providerClasspath, Set newArtifacts )
{
Classpath inprocClasspath = providerClasspath.clone();
@@ -1813,18 +1872,15 @@ private LocationManager getLocationManager()
private StartupConfiguration newStartupConfigWithModularPath(
@Nonnull ClassLoaderConfiguration classLoaderConfiguration, @Nonnull Set providerArtifacts,
@Nonnull String providerName, @Nonnull File moduleDescriptor, @Nonnull DefaultScanResult scanResult,
- @Nonnull String javaHome )
+ @Nonnull String javaHome, @Nonnull TestClassPath testClasspathWrapper )
throws IOException
{
- TestClassPath testClasspathWrapper = generateTestClasspath();
Classpath testClasspath = testClasspathWrapper.toClasspath();
- testClasspathWrapper.avoidArtifactDuplicates( providerArtifacts );
-
- Classpath providerClasspath = ClasspathCache.getCachedClassPath( providerName );
+ Classpath providerClasspath = classpathCache.getCachedClassPath( providerName );
if ( providerClasspath == null )
{
- providerClasspath = ClasspathCache.setCachedClasspath( providerName, providerArtifacts );
+ providerClasspath = classpathCache.setCachedClasspath( providerName, providerArtifacts );
}
ResolvePathsRequest req = ResolvePathsRequest.ofStrings( testClasspath.getClassPath() )
@@ -1849,11 +1905,18 @@ private StartupConfiguration newStartupConfigWithModularPath(
packages.add( substringBeforeLast( className, "." ) );
}
- ModularClasspath modularClasspath = new ModularClasspath( moduleDescriptor, testModulepath.getClassPath(),
- packages, getTestClassesDirectory() );
+ getConsoleLogger().debug( "main module descriptor name: " + result.getMainModuleDescriptor().name() );
+
+ ModularClasspath modularClasspath = new ModularClasspath( result.getMainModuleDescriptor().name(),
+ testModulepath.getClassPath(), packages, getTestClassesDirectory() );
+
+ Artifact[] additionalInProcArtifacts =
+ { getCommonArtifact(), getExtensionsArtifact(), getApiArtifact(), getLoggerApiArtifact() };
+ Set inProcArtifacts = retainInProcArtifactsUnique( providerArtifacts, additionalInProcArtifacts );
+ Classpath inProcClasspath = createInProcClasspath( providerClasspath, inProcArtifacts );
ModularClasspathConfiguration classpathConfiguration = new ModularClasspathConfiguration( modularClasspath,
- testClasspath, providerClasspath, effectiveIsEnableAssertions(), isChildDelegation() );
+ testClasspath, providerClasspath, inProcClasspath, effectiveIsEnableAssertions(), isChildDelegation() );
getConsoleLogger().debug( testClasspath.getLogMessage( "test classpath:" ) );
getConsoleLogger().debug( testModulepath.getLogMessage( "test modulepath:" ) );
@@ -1861,9 +1924,11 @@ private StartupConfiguration newStartupConfigWithModularPath(
getConsoleLogger().debug( testClasspath.getCompactLogMessage( "test(compact) classpath:" ) );
getConsoleLogger().debug( testModulepath.getCompactLogMessage( "test(compact) modulepath:" ) );
getConsoleLogger().debug( providerClasspath.getCompactLogMessage( "provider(compact) classpath:" ) );
+ getConsoleLogger().debug( inProcClasspath.getLogMessage( "in-process classpath:" ) );
+ getConsoleLogger().debug( inProcClasspath.getCompactLogMessage( "in-process(compact) classpath:" ) );
return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration, isForking(),
- false );
+ false, ProcessCheckerType.toEnum( getEnableProcessChecker() ) );
}
private Artifact getCommonArtifact()
@@ -1871,6 +1936,11 @@ private Artifact getCommonArtifact()
return getPluginArtifactMap().get( "org.apache.maven.surefire:maven-surefire-common" );
}
+ private Artifact getExtensionsArtifact()
+ {
+ return getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-extensions-api" );
+ }
+
private Artifact getApiArtifact()
{
return getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-api" );
@@ -1893,12 +1963,26 @@ private Artifact getShadefireArtifact()
private StartupReportConfiguration getStartupReportConfiguration( String configChecksum, boolean isForkMode )
{
+ SurefireStatelessReporter xmlReporter =
+ statelessTestsetReporter == null
+ ? new SurefireStatelessReporter( /*todo call def. constr.*/ isDisableXmlReport(), "3.0" )
+ : statelessTestsetReporter;
+
+ xmlReporter.setDisable( isDisableXmlReport() ); // todo change to Boolean in the version 3.0.0-M6
+
+ SurefireConsoleOutputReporter outReporter =
+ consoleOutputReporter == null ? new SurefireConsoleOutputReporter() : consoleOutputReporter;
+
+ SurefireStatelessTestsetInfoReporter testsetReporter =
+ statelessTestsetInfoReporter == null
+ ? new SurefireStatelessTestsetInfoReporter() : statelessTestsetInfoReporter;
+
return new StartupReportConfiguration( isUseFile(), isPrintSummary(), getReportFormat(),
- isRedirectTestOutputToFile(), isDisableXmlReport(),
+ isRedirectTestOutputToFile(),
getReportsDirectory(), isTrimStackTrace(), getReportNameSuffix(),
getStatisticsFile( configChecksum ), requiresRunHistory(),
getRerunFailingTestsCount(), getReportSchemaLocation(), getEncoding(),
- isForkMode );
+ isForkMode, xmlReporter, outReporter, testsetReporter );
}
private boolean isSpecificTestSpecified()
@@ -2124,14 +2208,19 @@ private Artifact getJunitDepArtifact()
return getProjectArtifactMap().get( "junit:junit-dep" );
}
-
private Artifact getJunitPlatformArtifact()
{
- Artifact artifact = getProjectArtifactMap().get( getJunitPlatformArtifactName() );
- Artifact projectArtifact = project.getArtifact();
- String projectArtifactName = projectArtifact.getGroupId() + ":" + projectArtifact.getArtifactId();
+ Artifact artifact = getProjectArtifactMap().get( "org.junit.platform:junit-platform-commons" );
+ if ( artifact == null )
+ {
+ artifact = getPluginArtifactMap().get( "org.junit.platform:junit-platform-engine" );
+ }
- if ( artifact == null && projectArtifactName.equals( getJunitPlatformArtifactName() ) )
+ Artifact projectArtifact = project.getArtifact();
+ String projectGroupId = projectArtifact.getGroupId();
+ if ( artifact == null && ( "org.junit.platform".equals( projectGroupId )
+ || "org.junit.jupiter".equals( projectGroupId )
+ || "org.junit.vintage".equals( projectGroupId ) ) )
{
artifact = projectArtifact;
}
@@ -2142,11 +2231,12 @@ private Artifact getJunitPlatformArtifact()
private ForkStarter createForkStarter( @Nonnull ProviderInfo provider, @Nonnull ForkConfiguration forkConfiguration,
@Nonnull ClassLoaderConfiguration classLoaderConfiguration,
@Nonnull RunOrderParameters runOrderParameters, @Nonnull ConsoleLogger log,
- @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform )
+ @Nonnull DefaultScanResult scanResult, @Nonnull Platform platform,
+ @Nonnull TestClassPath testClasspathWrapper )
throws MojoExecutionException, MojoFailureException
{
- StartupConfiguration startupConfiguration =
- createStartupConfiguration( provider, false, classLoaderConfiguration, scanResult, platform );
+ StartupConfiguration startupConfiguration = createStartupConfiguration( provider, false,
+ classLoaderConfiguration, scanResult, platform, testClasspathWrapper );
String configChecksum = getConfigChecksum();
StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, true );
ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2158,11 +2248,12 @@ private InPluginVMSurefireStarter createInprocessStarter( @Nonnull ProviderInfo
@Nonnull ClassLoaderConfiguration classLoaderConfig,
@Nonnull RunOrderParameters runOrderParameters,
@Nonnull DefaultScanResult scanResult,
- @Nonnull Platform platform )
+ @Nonnull Platform platform,
+ @Nonnull TestClassPath testClasspathWrapper )
throws MojoExecutionException, MojoFailureException
{
- StartupConfiguration startupConfiguration =
- createStartupConfiguration( provider, true, classLoaderConfig, scanResult, platform );
+ StartupConfiguration startupConfiguration = createStartupConfiguration( provider, true, classLoaderConfig,
+ scanResult, platform, testClasspathWrapper );
String configChecksum = getConfigChecksum();
StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum, false );
ProviderConfiguration providerConfiguration = createProviderConfiguration( runOrderParameters );
@@ -2188,6 +2279,7 @@ private ForkConfiguration createForkConfiguration( Platform platform )
getProject().getModel().getProperties(),
getArgLine(),
getEnvironmentVariables(),
+ getExcludedEnvironmentVariables(),
getConsoleLogger().isDebugEnabled(),
getEffectiveForkCount(),
reuseForks,
@@ -2203,6 +2295,7 @@ else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
getProject().getModel().getProperties(),
getArgLine(),
getEnvironmentVariables(),
+ getExcludedEnvironmentVariables(),
getConsoleLogger().isDebugEnabled(),
getEffectiveForkCount(),
reuseForks,
@@ -2218,6 +2311,7 @@ else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
getProject().getModel().getProperties(),
getArgLine(),
getEnvironmentVariables(),
+ getExcludedEnvironmentVariables(),
getConsoleLogger().isDebugEnabled(),
getEffectiveForkCount(),
reuseForks,
@@ -2226,6 +2320,16 @@ else if ( getClassLoaderConfiguration().isManifestOnlyJarRequestedAndUsable() )
}
}
+ private void ensureEnableProcessChecker() throws MojoFailureException
+ {
+ if ( !ProcessCheckerType.isValid( getEnableProcessChecker() ) )
+ {
+ throw new MojoFailureException( "Unexpected value '"
+ + getEnableProcessChecker()
+ + "' in the configuration parameter 'enableProcessChecker'." );
+ }
+ }
+
private void convertDeprecatedForkMode()
{
String effectiveForkMode = getEffectiveForkMode();
@@ -2354,16 +2458,6 @@ private JdkAttributes getEffectiveJvm() throws MojoFailureException
return new JdkAttributes( jvmToUse, isBuiltInJava9AtLeast() );
}
- private Artifact getSurefireBooterArtifact()
- {
- Artifact artifact = getBooterArtifact();
- if ( artifact == null )
- {
- throw new RuntimeException( "Unable to locate surefire-booter in the list of plugin artifacts" );
- }
- return artifact;
- }
-
/**
* Where surefire stores its own temp files
*
@@ -2435,6 +2529,7 @@ private String getConfigChecksum()
checksum.add( getParallelTestsTimeoutInSeconds() );
checksum.add( getParallelTestsTimeoutForcedInSeconds() );
checksum.add( getEnvironmentVariables() );
+ checksum.add( getExcludedEnvironmentVariables() );
checksum.add( getWorkingDirectory() );
checksum.add( isChildDelegation() );
checksum.add( getGroups() );
@@ -2452,6 +2547,7 @@ private String getConfigChecksum()
checksum.add( isParallelOptimized() );
checksum.add( isTrimStackTrace() );
checksum.add( getRemoteRepositories() );
+ checksum.add( getProjectRemoteRepositories() );
checksum.add( isDisableXmlReport() );
checksum.add( isUseSystemClassLoader() );
checksum.add( isUseManifestOnlyJar() );
@@ -2465,6 +2561,7 @@ private String getConfigChecksum()
checksum.add( getRerunFailingTestsCount() );
checksum.add( getTempDir() );
checksum.add( useModulePath() );
+ checksum.add( getEnableProcessChecker() );
addPluginSpecificChecksumItems( checksum );
return checksum.getSha1();
}
@@ -2505,7 +2602,7 @@ protected ClassLoaderConfiguration getClassLoaderConfiguration()
*/
private TestClassPath generateTestClasspath()
{
- @SuppressWarnings( "unchecked" ) Set classpathArtifacts = getProject().getArtifacts();
+ Set classpathArtifacts = getProject().getArtifacts();
if ( getClasspathDependencyScopeExclude() != null && !getClasspathDependencyScopeExclude().isEmpty() )
{
@@ -2521,7 +2618,7 @@ private TestClassPath generateTestClasspath()
}
return new TestClassPath( classpathArtifacts, getClassesDirectory(),
- getTestClassesDirectory(), getAdditionalClasspathElements(), getConsoleLogger() );
+ getTestClassesDirectory(), getAdditionalClasspathElements() );
}
/**
@@ -2556,13 +2653,23 @@ private void showMap( Map, ?> map, String setting )
}
}
+ private void showArray( T[] array, String setting )
+ {
+ for ( T e : array )
+ {
+ getConsoleLogger().debug( "Setting " + setting + " [" + e + "]" );
+ }
+ }
+
private Classpath getArtifactClasspath( Artifact surefireArtifact )
{
- Classpath existing = ClasspathCache.getCachedClassPath( surefireArtifact.getArtifactId() );
+ Classpath existing = classpathCache.getCachedClassPath( surefireArtifact.getArtifactId() );
if ( existing == null )
{
List items = new ArrayList<>();
- for ( Artifact artifact : dependencyResolver.resolveArtifact( surefireArtifact ).getArtifacts() )
+ Set booterArtifacts =
+ surefireDependencyResolver.resolvePluginArtifact( surefireArtifact ).getArtifacts();
+ for ( Artifact artifact : booterArtifacts )
{
getConsoleLogger().debug(
"Adding to " + getPluginName() + " booter test classpath: " + artifact.getFile().getAbsolutePath()
@@ -2570,7 +2677,7 @@ private Classpath getArtifactClasspath( Artifact surefireArtifact )
items.add( artifact.getFile().getAbsolutePath() );
}
existing = new Classpath( items );
- ClasspathCache.setCachedClasspath( surefireArtifact.getArtifactId(), existing );
+ classpathCache.setCachedClasspath( surefireArtifact.getArtifactId(), existing );
}
return existing;
}
@@ -2666,7 +2773,7 @@ private void warnIfDefunctGroupsCombinations()
+ "is picking up an old junit version" );
}
throw new MojoFailureException( "groups/excludedGroups require TestNG, JUnit48+ or JUnit 5 "
- + "on project test classpath" );
+ + "(a specific engine required on classpath) on project test classpath" );
}
}
@@ -2790,7 +2897,7 @@ public Set getProviderClasspath()
{
Artifact surefireArtifact = getBooterArtifact();
String version = surefireArtifact.getBaseVersion();
- return dependencyResolver.getProviderClasspath( "surefire-testng", version );
+ return surefireDependencyResolver.getProviderClasspath( "surefire-testng", version );
}
}
@@ -2821,7 +2928,7 @@ public Set getProviderClasspath()
// add the JUnit provider as default - it doesn't require JUnit to be present,
// since it supports POJO tests.
String version = surefireBooterArtifact.getBaseVersion();
- return dependencyResolver.getProviderClasspath( "surefire-junit3", version );
+ return surefireDependencyResolver.getProviderClasspath( "surefire-junit3", version );
}
}
@@ -2860,18 +2967,23 @@ public void addProviderProperties()
public Set getProviderClasspath()
{
String version = surefireBooterArtifact.getBaseVersion();
- return dependencyResolver.getProviderClasspath( "surefire-junit4", version );
+ return surefireDependencyResolver.getProviderClasspath( "surefire-junit4", version );
}
}
final class JUnitPlatformProviderInfo
implements ProviderInfo
{
- private final Artifact junitArtifact;
+ private static final String PROVIDER_DEP_GID = "org.junit.platform";
+ private static final String PROVIDER_DEP_AID = "junit-platform-launcher";
+
+ private final Artifact junitPlatformArtifact;
+ private final TestClassPath testClasspath;
- JUnitPlatformProviderInfo( Artifact junitArtifact )
+ JUnitPlatformProviderInfo( Artifact junitPlatformArtifact, TestClassPath testClasspath )
{
- this.junitArtifact = junitArtifact;
+ this.junitPlatformArtifact = junitPlatformArtifact;
+ this.testClasspath = testClasspath;
}
@Override
@@ -2884,7 +2996,7 @@ public String getProviderName()
@Override
public boolean isApplicable()
{
- return junitArtifact != null;
+ return junitPlatformArtifact != null;
}
@Override
@@ -2895,10 +3007,134 @@ public void addProviderProperties()
@Override
@Nonnull
- public Set getProviderClasspath()
+ public Set getProviderClasspath() throws MojoExecutionException
{
- String version = surefireBooterArtifact.getBaseVersion();
- return dependencyResolver.getProviderClasspath( "surefire-junit-platform", version );
+ String surefireVersion = surefireBooterArtifact.getBaseVersion();
+ Map providerArtifacts =
+ surefireDependencyResolver.getProviderClasspathAsMap( "surefire-junit-platform", surefireVersion );
+ Map testDependencies = testClasspath.getTestDependencies();
+
+ if ( hasDependencyPlatformEngine( testDependencies ) )
+ {
+ String filterTestDependency = "org.junit.platform:junit-platform-engine";
+ getConsoleLogger().debug( "Test dependencies contain " + filterTestDependency );
+ narrowProviderDependencies( filterTestDependency, providerArtifacts, testDependencies );
+ }
+ else
+ {
+ ProjectBuildingRequest request = getSession().getProjectBuildingRequest();
+ Collection pluginDependencies = getPluginDescriptor().getPlugin().getDependencies();
+ Set engines =
+ surefireDependencyResolver.resolvePluginDependencies( request, pluginDependencies );
+ if ( hasDependencyPlatformEngine( engines ) )
+ {
+ Map engineArtifacts = artifactMapByVersionlessId( engines );
+ providerArtifacts.putAll( engineArtifacts );
+ alignVersions( providerArtifacts, engineArtifacts );
+ }
+ else if ( hasDependencyJupiterAPI( testDependencies ) )
+ {
+ String engineGroupId = "org.junit.jupiter";
+ String engineArtifactId = "junit-jupiter-engine";
+ String engineCoordinates = engineGroupId + ":" + engineArtifactId;
+ String api = "org.junit.jupiter:junit-jupiter-api";
+ getConsoleLogger().debug( "Test dependencies contain " + api + ". Resolving " + engineCoordinates );
+ String engineVersion = testDependencies.get( api ).getBaseVersion();
+ addEngineByApi( engineGroupId, engineArtifactId, engineVersion,
+ providerArtifacts, testDependencies );
+ }
+ }
+ providerArtifacts.keySet().removeAll( testDependencies.keySet() );
+ return new LinkedHashSet<>( providerArtifacts.values() );
+ }
+
+ private void addEngineByApi( String engineGroupId, String engineArtifactId, String engineVersion,
+ Map providerArtifacts, Map testDependencies )
+ {
+ providerArtifacts.keySet().removeAll( testDependencies.keySet() );
+ for ( Artifact dep : resolve( engineGroupId, engineArtifactId, engineVersion, null, "jar" ) )
+ {
+ String key = dep.getGroupId() + ":" + dep.getArtifactId();
+ if ( !testDependencies.containsKey( key ) )
+ {
+ providerArtifacts.put( key, dep );
+ }
+ }
+ alignVersions( providerArtifacts, testDependencies );
+ }
+
+ private void narrowProviderDependencies( String filterTestDependency,
+ Map providerArtifacts,
+ Map testDependencies )
+ {
+ Artifact engine = testDependencies.get( filterTestDependency );
+ String groupId = engine.getGroupId();
+ String artifactId = engine.getArtifactId();
+ String version = engine.getBaseVersion();
+ String classifier = engine.getClassifier();
+ String type = engine.getType();
+ for ( Artifact engineDep : resolve( groupId, artifactId, version, classifier, type ) )
+ {
+ providerArtifacts.remove( engineDep.getGroupId() + ":" + engineDep.getArtifactId() );
+ getConsoleLogger().debug( "Removed artifact " + engineDep
+ + " from provider. Already appears in test classpath." );
+ }
+ alignVersions( providerArtifacts, testDependencies );
+ }
+
+ private void alignVersions( Map providerArtifacts,
+ Map referencedDependencies )
+ {
+ String version = referencedDependencies.get( "org.junit.platform:junit-platform-commons" ).getBaseVersion();
+ for ( Artifact launcherArtifact : resolve( PROVIDER_DEP_GID, PROVIDER_DEP_AID, version, null, "jar" ) )
+ {
+ String key = launcherArtifact.getGroupId() + ":" + launcherArtifact.getArtifactId();
+ if ( providerArtifacts.containsKey( key ) )
+ {
+ providerArtifacts.put( key, launcherArtifact );
+ }
+ }
+ }
+
+ private Set resolve( String g, String a, String v, String c, String t )
+ {
+ ArtifactHandler handler = junitPlatformArtifact.getArtifactHandler();
+ Artifact artifact = new DefaultArtifact( g, a, v, null, t, c, handler );
+ getConsoleLogger().debug( "Resolving artifact " + g + ":" + a + ":" + v );
+ Set r = surefireDependencyResolver.resolveProjectArtifact( artifact ).getArtifacts();
+ getConsoleLogger().debug( "Resolved artifact " + g + ":" + a + ":" + v + " to " + r );
+ return r;
+ }
+
+ private boolean hasDependencyJupiterAPI( Map dependencies )
+ {
+ return dependencies.containsKey( "org.junit.jupiter:junit-jupiter-api" )
+ || hasGroupArtifactId( "org.junit.jupiter", "junit-jupiter-api", getProject().getArtifact() );
+ }
+
+ private boolean hasDependencyPlatformEngine( Map dependencies )
+ {
+ return dependencies.containsKey( "org.junit.platform:junit-platform-engine" )
+ || hasGroupArtifactId( "org.junit.platform", "junit-platform-engine", getProject().getArtifact() );
+ }
+
+ private boolean hasDependencyPlatformEngine( Collection dependencies )
+ {
+ if ( hasGroupArtifactId( "org.junit.platform", "junit-platform-engine", getProject().getArtifact() ) )
+ {
+ return true;
+ }
+
+ for ( Artifact dependency : dependencies )
+ {
+ if ( dependency.getGroupId().equals( "org.junit.platform" )
+ && dependency.getArtifactId().equals( "junit-platform-engine" ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
}
}
@@ -2946,7 +3182,7 @@ public void addProviderProperties() throws MojoExecutionException
public Set getProviderClasspath()
{
String version = surefireBooterArtifact.getBaseVersion();
- return dependencyResolver.getProviderClasspath( "surefire-junit47", version );
+ return surefireDependencyResolver.getProviderClasspath( "surefire-junit47", version );
}
}
@@ -2994,8 +3230,8 @@ public void addProviderProperties() throws MojoExecutionException
@Nonnull
public Set getProviderClasspath()
{
- return dependencyResolver.addProviderToClasspath( getPluginArtifactMap(), getMojoArtifact(),
- getCommonArtifact(), getApiArtifact(), getLoggerApiArtifact() );
+ return surefireDependencyResolver.addProviderToClasspath( getPluginArtifactMap(), getMojoArtifact(),
+ getApiArtifact(), getLoggerApiArtifact() );
}
}
@@ -3177,7 +3413,6 @@ public void setProjectArtifactMap( Map projectArtifactMap )
this.projectArtifactMap = projectArtifactMap;
}
-
public String getReportNameSuffix()
{
return reportNameSuffix;
@@ -3306,17 +3541,6 @@ public void setJunitArtifactName( String junitArtifactName )
this.junitArtifactName = junitArtifactName;
}
- public String getJunitPlatformArtifactName()
- {
- return junitPlatformArtifactName;
- }
-
- @SuppressWarnings( "UnusedDeclaration" )
- public void setJunitPlatformArtifactName( String junitPlatformArtifactName )
- {
- this.junitPlatformArtifactName = junitPlatformArtifactName;
- }
-
public String getTestNGArtifactName()
{
return testNGArtifactName;
@@ -3424,15 +3648,15 @@ public void setTrimStackTrace( boolean trimStackTrace )
this.trimStackTrace = trimStackTrace;
}
- public ArtifactFactory getArtifactFactory()
+ public List getProjectRemoteRepositories()
{
- return artifactFactory;
+ return projectRemoteRepositories;
}
@SuppressWarnings( "UnusedDeclaration" )
- public void setArtifactFactory( ArtifactFactory artifactFactory )
+ public void setProjectRemoteRepositories( List projectRemoteRepositories )
{
- this.artifactFactory = artifactFactory;
+ this.projectRemoteRepositories = projectRemoteRepositories;
}
public List getRemoteRepositories()
@@ -3530,6 +3754,12 @@ public void setDependenciesToScan( String[] dependenciesToScan )
this.dependenciesToScan = dependenciesToScan;
}
+ @SuppressWarnings( "UnusedDeclaration" )
+ void setPluginDescriptor( PluginDescriptor pluginDescriptor )
+ {
+ this.pluginDescriptor = pluginDescriptor;
+ }
+
public PluginDescriptor getPluginDescriptor()
{
return pluginDescriptor;
@@ -3643,4 +3873,31 @@ else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE )
throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
}
}
+
+ private static final class ClasspathCache
+ {
+ private final Map classpaths = new HashMap<>( 4 );
+
+ private Classpath getCachedClassPath( @Nonnull String artifactId )
+ {
+ return classpaths.get( artifactId );
+ }
+
+ private void setCachedClasspath( @Nonnull String key, @Nonnull Classpath classpath )
+ {
+ classpaths.put( key, classpath );
+ }
+
+ private Classpath setCachedClasspath( @Nonnull String key, @Nonnull Set artifacts )
+ {
+ Collection files = new ArrayList<>();
+ for ( Artifact artifact : artifacts )
+ {
+ files.add( artifact.getFile().getAbsolutePath() );
+ }
+ Classpath classpath = new Classpath( files );
+ setCachedClasspath( key, classpath );
+ return classpath;
+ }
+ }
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ClasspathCache.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ClasspathCache.java
index 7ba7b5475f..6dbaf29f92 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ClasspathCache.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ClasspathCache.java
@@ -32,6 +32,7 @@
/**
* @author Kristian Rosenvold
*/
+@Deprecated
public class ClasspathCache
{
private static final ConcurrentHashMap CLASSPATHS =
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
index 81830ca4d4..835fb89bbc 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
@@ -19,15 +19,17 @@
* under the License.
*/
-import java.io.File;
-import java.lang.reflect.Constructor;
-
+import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
import org.apache.maven.surefire.booter.SurefireReflector;
import org.apache.maven.surefire.util.SurefireReflectionException;
import javax.annotation.Nonnull;
+import java.io.File;
+import java.lang.reflect.Constructor;
import static org.apache.maven.surefire.util.ReflectionUtils.getConstructor;
import static org.apache.maven.surefire.util.ReflectionUtils.instantiateObject;
@@ -40,6 +42,9 @@ public class CommonReflector
{
private final Class> startupReportConfiguration;
private final Class> consoleLogger;
+ private final Class> statelessTestsetReporter;
+ private final Class> consoleOutputReporter;
+ private final Class> statelessTestsetInfoReporter;
private final ClassLoader surefireClassLoader;
public CommonReflector( @Nonnull ClassLoader surefireClassLoader )
@@ -50,6 +55,10 @@ public CommonReflector( @Nonnull ClassLoader surefireClassLoader )
{
startupReportConfiguration = surefireClassLoader.loadClass( StartupReportConfiguration.class.getName() );
consoleLogger = surefireClassLoader.loadClass( ConsoleLogger.class.getName() );
+ statelessTestsetReporter = surefireClassLoader.loadClass( SurefireStatelessReporter.class.getName() );
+ consoleOutputReporter = surefireClassLoader.loadClass( SurefireConsoleOutputReporter.class.getName() );
+ statelessTestsetInfoReporter =
+ surefireClassLoader.loadClass( SurefireStatelessTestsetInfoReporter.class.getName() );
}
catch ( ClassNotFoundException e )
{
@@ -70,18 +79,23 @@ public Object createReportingReporterFactory( @Nonnull StartupReportConfiguratio
private Object createStartupReportConfiguration( @Nonnull StartupReportConfiguration reporterConfiguration )
{
Constructor> constructor = getConstructor( startupReportConfiguration, boolean.class, boolean.class,
- String.class, boolean.class, boolean.class, File.class,
- boolean.class, String.class, File.class, boolean.class,
- int.class, String.class, String.class, boolean.class );
+ String.class, boolean.class, File.class,
+ boolean.class, String.class, File.class, boolean.class,
+ int.class, String.class, String.class, boolean.class,
+ statelessTestsetReporter, consoleOutputReporter,
+ statelessTestsetInfoReporter );
//noinspection BooleanConstructorCall
Object[] params = { reporterConfiguration.isUseFile(), reporterConfiguration.isPrintSummary(),
reporterConfiguration.getReportFormat(), reporterConfiguration.isRedirectTestOutputToFile(),
- reporterConfiguration.isDisableXmlReport(), reporterConfiguration.getReportsDirectory(),
+ reporterConfiguration.getReportsDirectory(),
reporterConfiguration.isTrimStackTrace(), reporterConfiguration.getReportNameSuffix(),
reporterConfiguration.getStatisticsFile(), reporterConfiguration.isRequiresRunHistory(),
reporterConfiguration.getRerunFailingTestsCount(), reporterConfiguration.getXsdSchemaLocation(),
- reporterConfiguration.getEncoding().name(), reporterConfiguration.isForkMode() };
+ reporterConfiguration.getEncoding().name(), reporterConfiguration.isForkMode(),
+ reporterConfiguration.getXmlReporter().clone( surefireClassLoader ),
+ reporterConfiguration.getConsoleOutputReporter().clone( surefireClassLoader ),
+ reporterConfiguration.getTestsetReporter().clone( surefireClassLoader )
+ };
return newInstance( constructor, params );
}
-
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
index cb63cc1a3c..fea74fd000 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ProviderInfo.java
@@ -36,7 +36,7 @@ public interface ProviderInfo
boolean isApplicable();
@Nonnull
- Set getProviderClasspath();
+ Set getProviderClasspath() throws MojoExecutionException;
void addProviderProperties() throws MojoExecutionException;
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
index 5089973ae9..02ec44b8ef 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
@@ -19,26 +19,32 @@
* under the License.
*/
-import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
-import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
-import org.apache.maven.plugin.surefire.report.FileReporter;
-import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
-import org.apache.maven.plugin.surefire.report.TestcycleConsoleOutputReceiver;
+import org.apache.maven.plugin.surefire.extensions.DefaultStatelessReportMojoConfiguration;
+import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.PrintStream;
import java.nio.charset.Charset;
-import java.util.List;
+import java.util.Deque;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.commons.lang3.StringUtils.trimToNull;
import static org.apache.maven.plugin.surefire.SurefireHelper.replaceForkThreadsInPath;
import static org.apache.maven.plugin.surefire.report.ConsoleReporter.BRIEF;
import static org.apache.maven.plugin.surefire.report.ConsoleReporter.PLAIN;
-import static org.apache.commons.lang3.StringUtils.trimToNull;
/**
* All the parameters used to construct reporters
@@ -66,8 +72,6 @@ public final class StartupReportConfiguration
private final boolean redirectTestOutputToFile;
- private final boolean disableXmlReport;
-
private final File reportsDirectory;
private final boolean trimStackTrace;
@@ -76,27 +80,33 @@ public final class StartupReportConfiguration
private final String xsdSchemaLocation;
- private final Map>> testClassMethodRunHistory
- = new ConcurrentHashMap<>();
+ private final Map> testClassMethodRunHistory = new ConcurrentHashMap<>();
private final Charset encoding;
- private boolean isForkMode;
+ private final boolean isForkMode;
+
+ private final SurefireStatelessReporter xmlReporter;
+
+ private final SurefireConsoleOutputReporter consoleOutputReporter;
+
+ private final SurefireStatelessTestsetInfoReporter testsetReporter;
private StatisticsReporter statisticsReporter;
@SuppressWarnings( "checkstyle:parameternumber" )
public StartupReportConfiguration( boolean useFile, boolean printSummary, String reportFormat,
- boolean redirectTestOutputToFile, boolean disableXmlReport,
- @Nonnull File reportsDirectory, boolean trimStackTrace, String reportNameSuffix,
- File statisticsFile, boolean requiresRunHistory, int rerunFailingTestsCount,
- String xsdSchemaLocation, String encoding, boolean isForkMode )
+ boolean redirectTestOutputToFile,
+ @Nonnull File reportsDirectory, boolean trimStackTrace, String reportNameSuffix,
+ File statisticsFile, boolean requiresRunHistory, int rerunFailingTestsCount,
+ String xsdSchemaLocation, String encoding, boolean isForkMode,
+ SurefireStatelessReporter xmlReporter, SurefireConsoleOutputReporter consoleOutputReporter,
+ SurefireStatelessTestsetInfoReporter testsetReporter )
{
this.useFile = useFile;
this.printSummary = printSummary;
this.reportFormat = reportFormat;
this.redirectTestOutputToFile = redirectTestOutputToFile;
- this.disableXmlReport = disableXmlReport;
this.reportsDirectory = reportsDirectory;
this.trimStackTrace = trimStackTrace;
this.reportNameSuffix = reportNameSuffix;
@@ -107,8 +117,11 @@ public StartupReportConfiguration( boolean useFile, boolean printSummary, String
this.rerunFailingTestsCount = rerunFailingTestsCount;
this.xsdSchemaLocation = xsdSchemaLocation;
String charset = trimToNull( encoding );
- this.encoding = charset == null ? Charset.defaultCharset() : Charset.forName( charset );
+ this.encoding = charset == null ? UTF_8 : Charset.forName( charset );
this.isForkMode = isForkMode;
+ this.xmlReporter = xmlReporter;
+ this.consoleOutputReporter = consoleOutputReporter;
+ this.testsetReporter = testsetReporter;
}
public boolean isUseFile()
@@ -136,11 +149,6 @@ public boolean isRedirectTestOutputToFile()
return redirectTestOutputToFile;
}
- public boolean isDisableXmlReport()
- {
- return disableXmlReport;
- }
-
public File getReportsDirectory()
{
return reportsDirectory;
@@ -151,44 +159,54 @@ public int getRerunFailingTestsCount()
return rerunFailingTestsCount;
}
- @Deprecated // rename to stateful
- public StatelessXmlReporter instantiateStatelessXmlReporter( Integer forkNumber )
+ public StatelessReportEventListener instantiateStatelessXmlReporter(
+ Integer forkNumber )
{
- assert forkNumber == null || isForkMode;
+ assert ( forkNumber == null ) == !isForkMode;
// If forking TestNG the suites have same name 'TestSuite' and tend to override report statistics in stateful
// reporter, see Surefire1535TestNGParallelSuitesIT. The testClassMethodRunHistory should be isolated.
// In the in-plugin execution of parallel JUnit4.7 with rerun the map must be shared because reports and
// listeners are in ThreadLocal, see Surefire1122ParallelAndFlakyTestsIT.
- Map>> testClassMethodRunHistory
+ Map> testClassMethodRunHistory
= isForkMode
- ? new ConcurrentHashMap>>()
+ ? new ConcurrentHashMap>()
: this.testClassMethodRunHistory;
- return isDisableXmlReport()
- ? null
- : new StatelessXmlReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, trimStackTrace,
- rerunFailingTestsCount, testClassMethodRunHistory, xsdSchemaLocation );
+ DefaultStatelessReportMojoConfiguration xmlReporterConfig =
+ new DefaultStatelessReportMojoConfiguration( resolveReportsDirectory( forkNumber ), reportNameSuffix,
+ trimStackTrace, rerunFailingTestsCount, xsdSchemaLocation, testClassMethodRunHistory );
+
+ return xmlReporter.isDisable() ? null : xmlReporter.createListener( xmlReporterConfig );
}
- public FileReporter instantiateFileReporter( Integer forkNumber )
+ public StatelessTestsetInfoFileReportEventListener instantiateFileReporter(
+ Integer forkNumber )
{
- return isUseFile() && isBriefOrPlainFormat()
- ? new FileReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, encoding )
+ return !testsetReporter.isDisable() && isUseFile() && isBriefOrPlainFormat()
+ ? testsetReporter.createListener( resolveReportsDirectory( forkNumber ), reportNameSuffix, encoding )
: null;
}
+ public StatelessTestsetInfoConsoleReportEventListener instantiateConsoleReporter(
+ ConsoleLogger consoleLogger )
+ {
+ return !testsetReporter.isDisable() && shouldReportToConsole()
+ ? testsetReporter.createListener( consoleLogger ) : null;
+ }
+
public boolean isBriefOrPlainFormat()
{
String fmt = getReportFormat();
return BRIEF.equals( fmt ) || PLAIN.equals( fmt );
}
- public TestcycleConsoleOutputReceiver instantiateConsoleOutputFileReporter( Integer forkNumber )
+ public ConsoleOutputReportEventListener instantiateConsoleOutputFileReporter( Integer forkNum )
{
- return isRedirectTestOutputToFile()
- ? new ConsoleOutputFileReporter( resolveReportsDirectory( forkNumber ), reportNameSuffix, forkNumber )
- : new DirectConsoleOutput( originalSystemOut, originalSystemErr );
+ ConsoleOutputReportEventListener outputReport = isRedirectTestOutputToFile()
+ ? consoleOutputReporter.createListener( resolveReportsDirectory( forkNum ), reportNameSuffix, forkNum )
+ : consoleOutputReporter.createListener( originalSystemOut, originalSystemErr );
+ return consoleOutputReporter.isDisable() ? null : outputReport;
}
public synchronized StatisticsReporter getStatisticsReporter()
@@ -215,11 +233,6 @@ public boolean isRequiresRunHistory()
return requiresRunHistory;
}
- public PrintStream getOriginalSystemOut()
- {
- return originalSystemOut;
- }
-
public String getXsdSchemaLocation()
{
return xsdSchemaLocation;
@@ -239,4 +252,24 @@ private File resolveReportsDirectory( Integer forkNumber )
{
return forkNumber == null ? reportsDirectory : replaceForkThreadsInPath( reportsDirectory, forkNumber );
}
+
+ public SurefireStatelessReporter getXmlReporter()
+ {
+ return xmlReporter;
+ }
+
+ public SurefireConsoleOutputReporter getConsoleOutputReporter()
+ {
+ return consoleOutputReporter;
+ }
+
+ public SurefireStatelessTestsetInfoReporter getTestsetReporter()
+ {
+ return testsetReporter;
+ }
+
+ private boolean shouldReportToConsole()
+ {
+ return isUseFile() ? isPrintSummary() : isRedirectTestOutputToFile() || isBriefOrPlainFormat();
+ }
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
index e403433951..16cb6c863c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
@@ -19,6 +19,7 @@
* under the License.
*/
+import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -26,24 +27,32 @@
import java.util.Set;
import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
-import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.repository.RepositorySystem;
+import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
+import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
+import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import static java.util.Collections.singletonList;
-import static org.apache.maven.artifact.Artifact.SCOPE_TEST;
-import static org.apache.maven.artifact.versioning.VersionRange.createFromVersion;
+import static java.util.Arrays.asList;
+import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
+import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE_PLUS_RUNTIME;
+import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
+import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
/**
@@ -73,26 +82,31 @@ final class SurefireDependencyResolver
private final RepositorySystem repositorySystem;
- private final ArtifactFactory artifactFactory;
-
private final ConsoleLogger log;
private final ArtifactRepository localRepository;
- private final List remoteRepositories;
+ private final List pluginRemoteRepositories;
+
+ private final List projectRemoteRepositories;
private final String pluginName;
- SurefireDependencyResolver( RepositorySystem repositorySystem, ArtifactFactory artifactFactory, ConsoleLogger log,
- ArtifactRepository localRepository,
- List remoteRepositories, String pluginName )
+ private final DependencyResolver depencencyResolver;
+
+ SurefireDependencyResolver( RepositorySystem repositorySystem, ConsoleLogger log,
+ ArtifactRepository localRepository,
+ List pluginRemoteRepositories,
+ List projectRemoteRepositories, String pluginName,
+ DependencyResolver depencencyResolver )
{
this.repositorySystem = repositorySystem;
- this.artifactFactory = artifactFactory;
this.log = log;
this.localRepository = localRepository;
- this.remoteRepositories = remoteRepositories;
+ this.pluginRemoteRepositories = pluginRemoteRepositories;
+ this.projectRemoteRepositories = projectRemoteRepositories;
this.pluginName = pluginName;
+ this.depencencyResolver = depencencyResolver;
}
static boolean isWithinVersionSpec( @Nullable Artifact artifact, @Nonnull String versionSpec )
@@ -119,33 +133,57 @@ static boolean isWithinVersionSpec( @Nullable Artifact artifact, @Nonnull String
}
}
- ArtifactResolutionResult resolveArtifact( Artifact providerArtifact )
+ Set resolvePluginDependencies( ProjectBuildingRequest request, Collection pluginDependencies )
+ throws MojoExecutionException
+ {
+ try
+ {
+ Iterable resolvedPluginDependencies = depencencyResolver.resolveDependencies( request,
+ pluginDependencies, null, ScopeFilter.including( SCOPE_COMPILE, SCOPE_RUNTIME ) );
+
+ Set resolved = new LinkedHashSet<>();
+ for ( ArtifactResult resolvedPluginDependency : resolvedPluginDependencies )
+ {
+ resolved.add( resolvedPluginDependency.getArtifact() );
+ }
+ return resolved;
+ }
+ catch ( DependencyResolverException e )
+ {
+ throw new MojoExecutionException( e.getLocalizedMessage(), e );
+ }
+ }
+
+ ArtifactResolutionResult resolvePluginArtifact( Artifact artifact )
{
- return resolveArtifact( providerArtifact, null );
+ return resolveArtifact( artifact, pluginRemoteRepositories );
}
- private ArtifactResolutionResult resolveArtifact( Artifact providerArtifact, @Nullable Artifact excludeArtifact )
+ ArtifactResolutionResult resolveProjectArtifact( Artifact artifact )
+ {
+ return resolveArtifact( artifact, projectRemoteRepositories );
+ }
+
+ private ArtifactResolutionResult resolveArtifact( Artifact artifact, List repositories )
{
ArtifactResolutionRequest request = new ArtifactResolutionRequest()
- .setArtifact( providerArtifact )
- .setRemoteRepositories( remoteRepositories )
- .setLocalRepository( localRepository )
- .setResolveTransitively( true );
- if ( excludeArtifact != null )
- {
- String pattern = excludeArtifact.getGroupId() + ":" + excludeArtifact.getArtifactId();
- request.setCollectionFilter( new ExcludesArtifactFilter( singletonList( pattern ) ) );
- }
+ .setArtifact( artifact )
+ .setLocalRepository( localRepository )
+ .setResolveTransitively( true )
+ .setCollectionFilter( new RuntimeArtifactFilter() )
+ .setRemoteRepositories( repositories );
+
return repositorySystem.resolve( request );
}
@Nonnull
Set getProviderClasspath( String providerArtifactId, String providerVersion )
{
- Artifact providerArtifact = artifactFactory.createDependencyArtifact( PROVIDER_GROUP_ID,
- providerArtifactId, createFromVersion( providerVersion ), "jar", null, SCOPE_TEST );
+ Dependency provider = toProviderDependency( providerArtifactId, providerVersion );
+
+ Artifact providerArtifact = repositorySystem.createDependencyArtifact( provider );
- ArtifactResolutionResult result = resolveArtifact( providerArtifact );
+ ArtifactResolutionResult result = resolvePluginArtifact( providerArtifact );
if ( log.isDebugEnabled() )
{
@@ -160,26 +198,27 @@ Set getProviderClasspath( String providerArtifactId, String providerVe
return orderProviderArtifacts( result.getArtifacts() );
}
+ @Nonnull
+ Map getProviderClasspathAsMap( String providerArtifactId, String providerVersion )
+ {
+ return artifactMapByVersionlessId( getProviderClasspath( providerArtifactId, providerVersion ) );
+ }
+
Set addProviderToClasspath( Map pluginArtifactMap, Artifact mojoPluginArtifact,
- Artifact surefireCommon, Artifact surefireApi, Artifact surefireLoggerApi )
+ Artifact surefireApi, Artifact surefireLoggerApi )
{
Set providerArtifacts = new LinkedHashSet<>();
- ArtifactResolutionResult artifactResolutionResult = resolveArtifact( mojoPluginArtifact );
+ ArtifactResolutionResult artifactResolutionResult = resolvePluginArtifact( mojoPluginArtifact );
for ( Artifact artifact : pluginArtifactMap.values() )
{
if ( !artifactResolutionResult.getArtifacts().contains( artifact ) )
{
providerArtifacts.add( artifact );
- for ( Artifact dependency : resolveArtifact( artifact ).getArtifacts() )
+ for ( Artifact dependency : resolvePluginArtifact( artifact ).getArtifacts() )
{
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
- if ( groupId.equals( surefireCommon.getGroupId() )
- && artifactId.equals( surefireCommon.getArtifactId() ) )
- {
- providerArtifacts.add( surefireCommon );
- }
- else if ( groupId.equals( surefireApi.getGroupId() )
+ if ( groupId.equals( surefireApi.getGroupId() )
&& artifactId.equals( surefireApi.getArtifactId() ) )
{
providerArtifacts.add( surefireApi );
@@ -214,4 +253,27 @@ private static Set orderProviderArtifacts( Set providerArtif
orderedProviderArtifacts.addAll( providerArtifacts );
return orderedProviderArtifacts;
}
+
+ private static Dependency toProviderDependency( String providerArtifactId, String providerVersion )
+ {
+ Dependency dependency = new Dependency();
+ dependency.setGroupId( PROVIDER_GROUP_ID );
+ dependency.setArtifactId( providerArtifactId );
+ dependency.setVersion( providerVersion );
+ dependency.setType( "jar" );
+ return dependency;
+ }
+
+ static class RuntimeArtifactFilter implements ArtifactFilter
+ {
+ private static final Collection SCOPES =
+ asList( SCOPE_COMPILE, SCOPE_COMPILE_PLUS_RUNTIME, SCOPE_RUNTIME );
+
+ @Override
+ public boolean include( Artifact artifact )
+ {
+ String scope = artifact.getScope();
+ return !artifact.isOptional() && ( scope == null || SCOPES.contains( scope ) );
+ }
+ }
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
index ee8fadbde6..3a37816c0c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/TestClassPath.java
@@ -20,14 +20,13 @@
*/
import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.surefire.booter.Classpath;
import java.io.File;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import static java.util.Collections.addAll;
import static org.apache.maven.shared.utils.StringUtils.split;
@@ -38,45 +37,26 @@ final class TestClassPath
private final File classesDirectory;
private final File testClassesDirectory;
private final String[] additionalClasspathElements;
- private final ConsoleLogger logger;
TestClassPath( Iterable artifacts,
File classesDirectory,
File testClassesDirectory,
- String[] additionalClasspathElements,
- ConsoleLogger logger )
+ String[] additionalClasspathElements )
{
this.artifacts = artifacts;
this.classesDirectory = classesDirectory;
this.testClassesDirectory = testClassesDirectory;
this.additionalClasspathElements = additionalClasspathElements;
- this.logger = logger;
}
- void avoidArtifactDuplicates( Set providerArtifacts )
+ Map getTestDependencies()
{
+ Map artifactMapping = new LinkedHashMap<>();
for ( Artifact artifact : artifacts )
{
- Iterator it = providerArtifacts.iterator();
- while ( it.hasNext() )
- {
- Artifact providerArtifact = it.next();
- String classifier1 = providerArtifact.getClassifier();
- String classifier2 = artifact.getClassifier();
- if ( providerArtifact.getGroupId().equals( artifact.getGroupId() )
- && providerArtifact.getArtifactId().equals( artifact.getArtifactId() )
- && providerArtifact.getType().equals( artifact.getType() )
- && ( classifier1 == null ? classifier2 == null : classifier1.equals( classifier2 ) ) )
- {
- it.remove();
- if ( logger.isDebugEnabled() )
- {
- logger.debug( "Removed artifact " + providerArtifact + " from provider. "
- + "Already appears in test classpath." );
- }
- }
- }
+ artifactMapping.put( artifact.getGroupId() + ":" + artifact.getArtifactId(), artifact );
}
+ return artifactMapping;
}
Classpath toClasspath()
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
index 6c57ebc315..692f486bde 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/AbstractClasspathForkConfiguration.java
@@ -44,6 +44,7 @@ abstract class AbstractClasspathForkConfiguration
@Nonnull Properties modelProperties,
@Nullable String argLine,
@Nonnull Map environmentVariables,
+ @Nonnull String[] excludedEnvironmentVariables,
boolean debug,
int forkCount,
boolean reuseForks,
@@ -51,7 +52,7 @@ abstract class AbstractClasspathForkConfiguration
@Nonnull ConsoleLogger log )
{
super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
- environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+ environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
}
@Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
index fe72fceefa..7eacb7485b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
@@ -23,6 +23,7 @@
import org.apache.maven.surefire.booter.AbstractPathConfiguration;
import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
import org.apache.maven.surefire.booter.KeyValueSource;
+import org.apache.maven.surefire.booter.ProcessCheckerType;
import org.apache.maven.surefire.booter.ProviderConfiguration;
import org.apache.maven.surefire.booter.StartupConfiguration;
import org.apache.maven.surefire.cli.CommandLineOption;
@@ -53,6 +54,7 @@
import static org.apache.maven.surefire.booter.BooterConstants.ISTRIMSTACKTRACE;
import static org.apache.maven.surefire.booter.BooterConstants.MAIN_CLI_OPTIONS;
import static org.apache.maven.surefire.booter.BooterConstants.PLUGIN_PID;
+import static org.apache.maven.surefire.booter.BooterConstants.PROCESS_CHECKER;
import static org.apache.maven.surefire.booter.BooterConstants.PROVIDER_CONFIGURATION;
import static org.apache.maven.surefire.booter.BooterConstants.REPORTSDIRECTORY;
import static org.apache.maven.surefire.booter.BooterConstants.REQUESTEDTEST;
@@ -98,8 +100,8 @@ class BooterSerializer
/**
* Does not modify sourceProperties
*/
- File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterConfiguration,
- StartupConfiguration providerConfiguration, Object testSet, boolean readTestsFromInStream,
+ File serialize( KeyValueSource sourceProperties, ProviderConfiguration providerConfiguration,
+ StartupConfiguration startupConfiguration, Object testSet, boolean readTestsFromInStream,
Long pid, int forkNumber )
throws IOException
{
@@ -107,13 +109,15 @@ File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterCon
properties.setProperty( PLUGIN_PID, pid );
- AbstractPathConfiguration cp = providerConfiguration.getClasspathConfiguration();
+ AbstractPathConfiguration cp = startupConfiguration.getClasspathConfiguration();
properties.setClasspath( CLASSPATH, cp.getTestClasspath() );
properties.setClasspath( SUREFIRE_CLASSPATH, cp.getProviderClasspath() );
properties.setProperty( ENABLE_ASSERTIONS, toString( cp.isEnableAssertions() ) );
properties.setProperty( CHILD_DELEGATION, toString( cp.isChildDelegation() ) );
+ ProcessCheckerType processChecker = startupConfiguration.getProcessChecker();
+ properties.setNullableProperty( PROCESS_CHECKER, processChecker == null ? null : processChecker.getType() );
- TestArtifactInfo testNg = booterConfiguration.getTestArtifact();
+ TestArtifactInfo testNg = providerConfiguration.getTestArtifact();
if ( testNg != null )
{
properties.setProperty( TESTARTIFACT_VERSION, testNg.getVersion() );
@@ -123,7 +127,7 @@ File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterCon
properties.setProperty( FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM, readTestsFromInStream );
properties.setNullableProperty( FORKTESTSET, getTypeEncoded( testSet ) );
- TestRequest testSuiteDefinition = booterConfiguration.getTestSuiteDefinition();
+ TestRequest testSuiteDefinition = providerConfiguration.getTestSuiteDefinition();
if ( testSuiteDefinition != null )
{
properties.setProperty( SOURCE_DIRECTORY, testSuiteDefinition.getTestSourceDirectory() );
@@ -141,7 +145,7 @@ File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterCon
properties.setNullableProperty( RERUN_FAILING_TESTS_COUNT, toString( rerunFailingTestsCount ) );
}
- DirectoryScannerParameters directoryScannerParameters = booterConfiguration.getDirScannerParams();
+ DirectoryScannerParameters directoryScannerParameters = providerConfiguration.getDirScannerParams();
if ( directoryScannerParameters != null )
{
properties.setProperty( FAILIFNOTESTS, toString( directoryScannerParameters.isFailIfNoTests() ) );
@@ -151,31 +155,31 @@ File serialize( KeyValueSource sourceProperties, ProviderConfiguration booterCon
properties.setProperty( TEST_CLASSES_DIRECTORY, directoryScannerParameters.getTestClassesDirectory() );
}
- final RunOrderParameters runOrderParameters = booterConfiguration.getRunOrderParameters();
+ final RunOrderParameters runOrderParameters = providerConfiguration.getRunOrderParameters();
if ( runOrderParameters != null )
{
properties.setProperty( RUN_ORDER, RunOrder.asString( runOrderParameters.getRunOrder() ) );
properties.setProperty( RUN_STATISTICS_FILE, runOrderParameters.getRunStatisticsFile() );
}
- ReporterConfiguration reporterConfiguration = booterConfiguration.getReporterConfiguration();
+ ReporterConfiguration reporterConfiguration = providerConfiguration.getReporterConfiguration();
boolean rep = reporterConfiguration.isTrimStackTrace();
File reportsDirectory = replaceForkThreadsInPath( reporterConfiguration.getReportsDirectory(), forkNumber );
properties.setProperty( ISTRIMSTACKTRACE, rep );
properties.setProperty( REPORTSDIRECTORY, reportsDirectory );
- ClassLoaderConfiguration classLoaderConfig = providerConfiguration.getClassLoaderConfiguration();
+ ClassLoaderConfiguration classLoaderConfig = startupConfiguration.getClassLoaderConfiguration();
properties.setProperty( USESYSTEMCLASSLOADER, toString( classLoaderConfig.isUseSystemClassLoader() ) );
properties.setProperty( USEMANIFESTONLYJAR, toString( classLoaderConfig.isUseManifestOnlyJar() ) );
- properties.setProperty( FAILIFNOTESTS, toString( booterConfiguration.isFailIfNoTests() ) );
- properties.setProperty( PROVIDER_CONFIGURATION, providerConfiguration.getProviderClassName() );
- properties.setProperty( FAIL_FAST_COUNT, toString( booterConfiguration.getSkipAfterFailureCount() ) );
- properties.setProperty( SHUTDOWN, booterConfiguration.getShutdown().name() );
- List mainCliOptions = booterConfiguration.getMainCliOptions();
+ properties.setProperty( FAILIFNOTESTS, toString( providerConfiguration.isFailIfNoTests() ) );
+ properties.setProperty( PROVIDER_CONFIGURATION, startupConfiguration.getProviderClassName() );
+ properties.setProperty( FAIL_FAST_COUNT, toString( providerConfiguration.getSkipAfterFailureCount() ) );
+ properties.setProperty( SHUTDOWN, providerConfiguration.getShutdown().name() );
+ List mainCliOptions = providerConfiguration.getMainCliOptions();
if ( mainCliOptions != null )
{
properties.addList( mainCliOptions, MAIN_CLI_OPTIONS );
}
- properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, toString( booterConfiguration.getSystemExitTimeout() ) );
+ properties.setNullableProperty( SYSTEM_EXIT_TIMEOUT, toString( providerConfiguration.getSystemExitTimeout() ) );
File surefireTmpDir = forkConfiguration.getTempDirectory();
boolean debug = forkConfiguration.isDebug();
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
index cf6c67e8a7..72aab989cc 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ClasspathForkConfiguration.java
@@ -44,12 +44,14 @@ public final class ClasspathForkConfiguration
public ClasspathForkConfiguration( @Nonnull Classpath bootClasspath, @Nonnull File tempDirectory,
@Nullable String debugLine, @Nonnull File workingDirectory,
@Nonnull Properties modelProperties, @Nullable String argLine,
- @Nonnull Map environmentVariables, boolean debug, int forkCount,
+ @Nonnull Map environmentVariables,
+ @Nonnull String[] excludedEnvironmentVariables,
+ boolean debug, int forkCount,
boolean reuseForks, @Nonnull Platform pluginPlatform,
@Nonnull ConsoleLogger log )
{
super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
- environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+ environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
}
@Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
index fa99451234..4ab44355ef 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/DefaultForkConfiguration.java
@@ -59,6 +59,7 @@ public abstract class DefaultForkConfiguration
@Nonnull private final Properties modelProperties;
@Nullable private final String argLine;
@Nonnull private final Map environmentVariables;
+ @Nonnull private final String[] excludedEnvironmentVariables;
private final boolean debug;
private final int forkCount;
private final boolean reuseForks;
@@ -73,6 +74,7 @@ protected DefaultForkConfiguration( @Nonnull Classpath booterClasspath,
@Nonnull Properties modelProperties,
@Nullable String argLine,
@Nonnull Map environmentVariables,
+ @Nonnull String[] excludedEnvironmentVariables,
boolean debug,
int forkCount,
boolean reuseForks,
@@ -86,6 +88,7 @@ protected DefaultForkConfiguration( @Nonnull Classpath booterClasspath,
this.modelProperties = modelProperties;
this.argLine = argLine;
this.environmentVariables = toImmutable( environmentVariables );
+ this.excludedEnvironmentVariables = excludedEnvironmentVariables;
this.debug = debug;
this.forkCount = forkCount;
this.reuseForks = reuseForks;
@@ -119,7 +122,8 @@ public OutputStreamFlushableCommandline createCommandLine( @Nonnull StartupConfi
@Nonnull File dumpLogDirectory )
throws SurefireBooterForkException
{
- OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
+ OutputStreamFlushableCommandline cli =
+ new OutputStreamFlushableCommandline( getExcludedEnvironmentVariables() );
cli.setWorkingDirectory( getWorkingDirectory( forkNumber ).getAbsolutePath() );
@@ -289,6 +293,13 @@ protected Map getEnvironmentVariables()
return environmentVariables;
}
+ @Nonnull
+ @Override
+ protected String[] getExcludedEnvironmentVariables()
+ {
+ return excludedEnvironmentVariables;
+ }
+
@Override
protected boolean isDebug()
{
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
index d45d13d55a..92bebd0656 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
@@ -45,6 +45,7 @@ public abstract class ForkConfiguration
@Nonnull protected abstract Properties getModelProperties();
@Nullable protected abstract String getArgLine();
@Nonnull protected abstract Map getEnvironmentVariables();
+ @Nonnull protected abstract String[] getExcludedEnvironmentVariables();
protected abstract boolean isDebug();
protected abstract int getForkCount();
protected abstract boolean isReuseForks();
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 2d3cc42845..2570977606 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -92,12 +92,14 @@
import static org.apache.maven.shared.utils.cli.ShutdownHookUtils.addShutDownHook;
import static org.apache.maven.shared.utils.cli.ShutdownHookUtils.removeShutdownHook;
import static org.apache.maven.surefire.booter.SystemPropertyManager.writePropertiesFile;
+import static org.apache.maven.surefire.cli.CommandLineOption.SHOW_ERRORS;
import static org.apache.maven.surefire.suite.RunResult.SUCCESS;
import static org.apache.maven.surefire.suite.RunResult.failure;
import static org.apache.maven.surefire.suite.RunResult.timeout;
import static org.apache.maven.surefire.util.internal.ConcurrencyUtils.countDownToZero;
import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
+import static org.apache.maven.surefire.util.internal.StringUtils.NL;
/**
* Starts the fork or runs in-process.
@@ -650,11 +652,29 @@ else if ( result == null || result != SUCCESS )
if ( forkClient.isErrorInFork() )
{
StackTraceWriter errorInFork = forkClient.getErrorInFork();
- // noinspection ThrowFromFinallyBlock
+ String errorInForkMessage =
+ errorInFork == null ? null : errorInFork.getThrowable().getLocalizedMessage();
+ boolean showStackTrace = providerConfiguration.getMainCliOptions().contains( SHOW_ERRORS );
+ String stackTrace = errorInForkMessage;
+ if ( showStackTrace )
+ {
+ if ( errorInFork != null )
+ {
+ if ( stackTrace == null )
+ {
+ stackTrace = "";
+ }
+ else
+ {
+ stackTrace += NL;
+ }
+ stackTrace += errorInFork.writeTrimmedTraceToString();
+ }
+ }
+ //noinspection ThrowFromFinallyBlock
throw new SurefireBooterForkException( "There was an error in the forked process"
+ detail
- + '\n'
- + errorInFork.getThrowable().getLocalizedMessage(), cause );
+ + ( stackTrace == null ? "" : stackTrace ), cause );
}
if ( !forkClient.isSaidGoodBye() )
{
@@ -759,7 +779,7 @@ private static ScheduledExecutorService createTimeoutCheckScheduler()
private ScheduledFuture> triggerPingTimerForShutdown( final TestLessInputStreamBuilder builder )
{
- return pingThreadScheduler.scheduleAtFixedRate( new Runnable()
+ return pingThreadScheduler.scheduleWithFixedDelay( new Runnable()
{
@Override
public void run()
@@ -771,7 +791,7 @@ public void run()
private ScheduledFuture> triggerPingTimerForShutdown( final Iterable streams )
{
- return pingThreadScheduler.scheduleAtFixedRate( new Runnable()
+ return pingThreadScheduler.scheduleWithFixedDelay( new Runnable()
{
@Override
public void run()
@@ -786,7 +806,7 @@ public void run()
private ScheduledFuture> triggerTimeoutCheck()
{
- return timeoutCheckScheduler.scheduleAtFixedRate( new Runnable()
+ return timeoutCheckScheduler.scheduleWithFixedDelay( new Runnable()
{
@Override
public void run()
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
index 62fa4c19b3..78e915af07 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java
@@ -19,6 +19,9 @@
* under the License.
*/
+import org.apache.commons.compress.archivers.zip.Zip64Mode;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
@@ -28,23 +31,28 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
+import java.util.zip.Deflater;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.isDirectory;
import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
+import static org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration.ClasspathElementUri.absolute;
+import static org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration.ClasspathElementUri.relative;
import static org.apache.maven.surefire.util.internal.StringUtils.NL;
/**
@@ -58,12 +66,14 @@ public final class JarManifestForkConfiguration
public JarManifestForkConfiguration( @Nonnull Classpath bootClasspath, @Nonnull File tempDirectory,
@Nullable String debugLine, @Nonnull File workingDirectory,
@Nonnull Properties modelProperties, @Nullable String argLine,
- @Nonnull Map environmentVariables, boolean debug,
+ @Nonnull Map environmentVariables,
+ @Nonnull String[] excludedEnvironmentVariables,
+ boolean debug,
int forkCount, boolean reuseForks, @Nonnull Platform pluginPlatform,
@Nonnull ConsoleLogger log )
{
super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
- environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+ environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
}
@Override
@@ -105,12 +115,15 @@ private File createJar( @Nonnull List classPath, @Nonnull String startCl
file.deleteOnExit();
}
Path parent = file.getParentFile().toPath();
- FileOutputStream fos = new FileOutputStream( file );
- try ( JarOutputStream jos = new JarOutputStream( fos ) )
+ OutputStream fos = new BufferedOutputStream( new FileOutputStream( file ), 64 * 1024 );
+
+ try ( ZipArchiveOutputStream zos = new ZipArchiveOutputStream( fos ) )
{
- jos.setLevel( JarOutputStream.STORED );
- JarEntry je = new JarEntry( "META-INF/MANIFEST.MF" );
- jos.putNextEntry( je );
+ zos.setUseZip64( Zip64Mode.Never );
+ zos.setLevel( Deflater.NO_COMPRESSION );
+
+ ZipArchiveEntry ze = new ZipArchiveEntry( "META-INF/MANIFEST.MF" );
+ zos.putArchiveEntry( ze );
Manifest man = new Manifest();
@@ -142,10 +155,9 @@ private File createJar( @Nonnull List classPath, @Nonnull String startCl
man.getMainAttributes().putValue( "Class-Path", cp.toString().trim() );
man.getMainAttributes().putValue( "Main-Class", startClassName );
- man.write( jos );
+ man.write( zos );
- jos.closeEntry();
- jos.flush();
+ zos.closeArchiveEntry();
return file;
}
@@ -168,14 +180,11 @@ static ClasspathElementUri toClasspathElementUri( @Nonnull Path parent,
@Nonnull Path classPathElement,
@Nonnull File dumpLogDirectory,
boolean dumpError )
- throws IOException
{
try
{
- String relativeUriPath = relativize( parent, classPathElement )
- .replace( '\\', '/' );
-
- return new ClasspathElementUri( new URI( null, relativeUriPath, null ) );
+ String relativePath = relativize( parent, classPathElement );
+ return relative( escapeUri( relativePath, UTF_8 ) );
}
catch ( IllegalArgumentException e )
{
@@ -190,15 +199,7 @@ static ClasspathElementUri toClasspathElementUri( @Nonnull Path parent,
.dumpStreamText( error, dumpLogDirectory );
}
- return new ClasspathElementUri( toAbsoluteUri( classPathElement ) );
- }
- catch ( URISyntaxException e )
- {
- // This is really unexpected, so fail
- throw new IOException( "Could not create a relative path "
- + classPathElement
- + " against "
- + parent, e );
+ return absolute( toAbsoluteUri( classPathElement ) );
}
}
@@ -207,16 +208,37 @@ static final class ClasspathElementUri
final String uri;
final boolean absolute;
- ClasspathElementUri( String uri )
+ private ClasspathElementUri( String uri, boolean absolute )
{
this.uri = uri;
- absolute = true;
+ this.absolute = absolute;
}
- ClasspathElementUri( URI uri )
+ static ClasspathElementUri absolute( String uri )
+ {
+ return new ClasspathElementUri( uri, true );
+ }
+
+ static ClasspathElementUri relative( String uri )
+ {
+ return new ClasspathElementUri( uri, false );
+ }
+ }
+
+ static String escapeUri( String input, Charset encoding )
+ {
+ try
+ {
+ String uriFormEncoded = URLEncoder.encode( input, encoding.name() );
+
+ String uriPathEncoded = uriFormEncoded.replaceAll( "\\+", "%20" );
+ uriPathEncoded = uriPathEncoded.replaceAll( "%2F|%5C", "/" );
+
+ return uriPathEncoded;
+ }
+ catch ( UnsupportedEncodingException e )
{
- this.uri = uri.toASCIIString();
- absolute = false;
+ throw new IllegalStateException( "avoided by using Charset" );
}
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
index 48e74d9e8a..af38bc5141 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ModularClasspathForkConfiguration.java
@@ -28,15 +28,11 @@
import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
import org.apache.maven.surefire.booter.StartupConfiguration;
import org.apache.maven.surefire.booter.SurefireBooterForkException;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ModuleVisitor;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
@@ -50,7 +46,6 @@
import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
import static org.apache.maven.shared.utils.StringUtils.replace;
import static org.apache.maven.surefire.util.internal.StringUtils.NL;
-import static org.objectweb.asm.Opcodes.ASM7;
/**
* @author Tibor Digana (tibor17)
@@ -67,6 +62,7 @@ public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
@Nonnull Properties modelProperties,
@Nullable String argLine,
@Nonnull Map environmentVariables,
+ @Nonnull String[] excludedEnvironmentVariables,
boolean debug,
@Nonnegative int forkCount,
boolean reuseForks,
@@ -74,7 +70,7 @@ public ModularClasspathForkConfiguration( @Nonnull Classpath bootClasspath,
@Nonnull ConsoleLogger log )
{
super( bootClasspath, tempDirectory, debugLine, workingDirectory, modelProperties, argLine,
- environmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
+ environmentVariables, excludedEnvironmentVariables, debug, forkCount, reuseForks, pluginPlatform, log );
}
@Override
@@ -91,13 +87,13 @@ protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
ModularClasspath modularClasspath = modularClasspathConfiguration.getModularClasspath();
- File descriptor = modularClasspath.getModuleDescriptor();
+ String moduleName = modularClasspath.getModuleNameFromDescriptor();
List modulePath = modularClasspath.getModulePath();
Collection packages = modularClasspath.getPackages();
File patchFile = modularClasspath.getPatchFile();
List classpath = toCompleteClasspath( config );
- File argsFile = createArgsFile( descriptor, modulePath, classpath, packages, patchFile, startClass );
+ File argsFile = createArgsFile( moduleName, modulePath, classpath, packages, patchFile, startClass );
cli.createArg().setValue( "@" + escapeToPlatformPath( argsFile.getAbsolutePath() ) );
}
@@ -111,7 +107,7 @@ protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
}
@Nonnull
- File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List modulePath,
+ File createArgsFile( @Nonnull String moduleName, @Nonnull List modulePath,
@Nonnull List classPath, @Nonnull Collection packages,
@Nonnull File patchFile, @Nonnull String startClassName )
throws IOException
@@ -168,8 +164,6 @@ File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List modul
.append( NL );
}
- final String moduleName = toModuleName( moduleDescriptor );
-
args.append( "--patch-module" )
.append( NL )
.append( moduleName )
@@ -217,27 +211,4 @@ File createArgsFile( @Nonnull File moduleDescriptor, @Nonnull List modul
return surefireArgs;
}
}
-
- @Nonnull
- String toModuleName( @Nonnull File moduleDescriptor ) throws IOException
- {
- if ( !moduleDescriptor.isFile() )
- {
- throw new IOException( "No such Jigsaw module-descriptor exists " + moduleDescriptor.getAbsolutePath() );
- }
-
- final StringBuilder sb = new StringBuilder();
- new ClassReader( new FileInputStream( moduleDescriptor ) ).accept( new ClassVisitor( ASM7 )
- {
- @Override
- public ModuleVisitor visitModule( String name, int access, String version )
- {
- sb.setLength( 0 );
- sb.append( name );
- return super.visitModule( name, access, version );
- }
- }, 0 );
-
- return sb.toString();
- }
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushReceiver.java
new file mode 100644
index 0000000000..131661d205
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushReceiver.java
@@ -0,0 +1,47 @@
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+/*
+ * 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 java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Facade flushing {@link OutputStream} and isolating the stream in client.
+ */
+final class OutputStreamFlushReceiver
+ implements FlushReceiver
+{
+ private final OutputStream outputStream;
+
+ /**
+ * Wraps an output stream in order to delegate a flush.
+ */
+ OutputStreamFlushReceiver( OutputStream outputStream )
+ {
+ this.outputStream = outputStream;
+ }
+
+ @Override
+ public void flush()
+ throws IOException
+ {
+ outputStream.flush();
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java
index eb1ab5b61a..e22b8df2ca 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java
@@ -19,9 +19,13 @@
* under the License.
*/
-import java.io.IOException;
-import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
import org.apache.maven.shared.utils.cli.CommandLineException;
+import org.apache.maven.shared.utils.cli.CommandLineUtils;
import org.apache.maven.shared.utils.cli.Commandline;
/**
@@ -35,29 +39,37 @@ public class OutputStreamFlushableCommandline
extends Commandline
implements FlushReceiverProvider
{
+ private final Collection excludedEnvironmentVariables;
+ private volatile FlushReceiver flushReceiver;
+
/**
- * Wraps an output stream in order to delegate a flush.
+ * for testing purposes only
*/
- private final class OutputStreamFlushReceiver
- implements FlushReceiver
+ public OutputStreamFlushableCommandline()
{
- private final OutputStream outputStream;
+ this( new String[0] );
+ }
- private OutputStreamFlushReceiver( OutputStream outputStream )
- {
- this.outputStream = outputStream;
- }
+ public OutputStreamFlushableCommandline( String[] excludedEnvironmentVariables )
+ {
+ this.excludedEnvironmentVariables = new ConcurrentLinkedDeque<>();
+ Collections.addAll( this.excludedEnvironmentVariables, excludedEnvironmentVariables );
+ }
+
+ @Override
+ public final void addSystemEnvironment()
+ {
+ Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
- @Override
- public void flush()
- throws IOException
+ for ( String key : systemEnvVars.stringPropertyNames() )
{
- outputStream.flush();
+ if ( !excludedEnvironmentVariables.contains( key ) )
+ {
+ addEnvironment( key, systemEnvVars.getProperty( key ) );
+ }
}
}
- private volatile FlushReceiver flushReceiver;
-
@Override
public Process execute()
throws CommandLineException
@@ -77,5 +89,4 @@ public FlushReceiver getFlushReceiver()
{
return flushReceiver;
}
-
-}
\ No newline at end of file
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index 436566f0dc..f2a934f28c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -26,6 +26,7 @@
import org.apache.maven.surefire.report.ConsoleOutputReceiver;
import org.apache.maven.surefire.report.ReportEntry;
import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.RunMode;
import org.apache.maven.surefire.report.StackTraceWriter;
import org.apache.maven.surefire.report.TestSetReportEntry;
@@ -33,44 +34,21 @@
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
-import java.nio.ByteBuffer;
-import java.util.Collections;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-import static java.lang.Integer.decode;
import static java.lang.System.currentTimeMillis;
import static java.util.Collections.unmodifiableMap;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_BYE;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_CONSOLE;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_DEBUG;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_ERROR;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_STDERR;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_STDOUT;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_STOP_ON_NEXT_TEST;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_SYSPROPS;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TESTSET_COMPLETED;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TESTSET_STARTING;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_ERROR;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_FAILED;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_SKIPPED;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_STARTING;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_TEST_SUCCEEDED;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_WARNING;
import static org.apache.maven.surefire.booter.Shutdown.KILL;
import static org.apache.maven.surefire.report.CategorizedReportEntry.reportEntry;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
-import static org.apache.maven.surefire.util.internal.StringUtils.unescapeBytes;
-import static org.apache.maven.surefire.util.internal.StringUtils.unescapeString;
// todo move to the same package with ForkStarter
@@ -95,11 +73,13 @@ public class ForkClient
private final Queue testsInProgress = new ConcurrentLinkedQueue<>();
/**
- * {@code testSetStartedAt} is set to non-zero after received
- * {@link org.apache.maven.surefire.booter.ForkingRunListener#BOOTERCODE_TESTSET_STARTING test-set}.
+ * testSetStartedAt is set to non-zero after received
+ * {@link org.apache.maven.surefire.booter.ForkedChannelEncoder#testSetStarting(ReportEntry, boolean)}.
*/
private final AtomicLong testSetStartedAt = new AtomicLong( START_TIME_ZERO );
+ private final ForkedChannelDecoder decoder = new ForkedChannelDecoder();
+
private final ConsoleLogger log;
/**
@@ -112,6 +92,8 @@ public class ForkClient
/**
* Used by single Thread started by {@link ThreadedStreamConsumer} and therefore does not need to be volatile.
*/
+ private final ForkedChannelDecoderErrorHandler errorHandler;
+
private RunListener testSetReporter;
/**
@@ -129,8 +111,228 @@ public ForkClient( DefaultReporterFactory defaultReporterFactory, NotifiableTest
this.log = log;
this.printedErrorStream = printedErrorStream;
this.forkNumber = forkNumber;
+ decoder.setTestSetStartingListener( new TestSetStartingListener() );
+ decoder.setTestSetCompletedListener( new TestSetCompletedListener() );
+ decoder.setTestStartingListener( new TestStartingListener() );
+ decoder.setTestSucceededListener( new TestSucceededListener() );
+ decoder.setTestFailedListener( new TestFailedListener() );
+ decoder.setTestSkippedListener( new TestSkippedListener() );
+ decoder.setTestErrorListener( new TestErrorListener() );
+ decoder.setTestAssumptionFailureListener( new TestAssumptionFailureListener() );
+ decoder.setSystemPropertiesListener( new SystemPropertiesListener() );
+ decoder.setStdOutListener( new StdOutListener() );
+ decoder.setStdErrListener( new StdErrListener() );
+ decoder.setConsoleInfoListener( new ConsoleListener() );
+ decoder.setAcquireNextTestListener( new AcquireNextTestListener() );
+ decoder.setConsoleErrorListener( new ErrorListener() );
+ decoder.setByeListener( new ByeListener() );
+ decoder.setStopOnNextTestListener( new StopOnNextTestListener() );
+ decoder.setConsoleDebugListener( new DebugListener() );
+ decoder.setConsoleWarningListener( new WarningListener() );
+ errorHandler = new ErrorHandler();
+ }
+
+ private final class ErrorHandler implements ForkedChannelDecoderErrorHandler
+ {
+ @Override
+ public void handledError( String line, Throwable e )
+ {
+ logStreamWarning( line, e );
+ }
+ }
+
+ private final class TestSetStartingListener
+ implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, TestSetReportEntry reportEntry )
+ {
+ getTestSetReporter().testSetStarting( reportEntry );
+ setCurrentStartTime();
+ }
+ }
+
+ private final class TestSetCompletedListener
+ implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, TestSetReportEntry reportEntry )
+ {
+ testsInProgress.clear();
+ TestSetReportEntry entry = reportEntry( reportEntry.getSourceName(), reportEntry.getSourceText(),
+ reportEntry.getName(), reportEntry.getNameText(),
+ reportEntry.getGroup(), reportEntry.getStackTraceWriter(), reportEntry.getElapsed(),
+ reportEntry.getMessage(), getTestVmSystemProperties() );
+ getTestSetReporter().testSetCompleted( entry );
+ }
+ }
+
+ private final class TestStartingListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.offer( reportEntry.getSourceName() );
+ getTestSetReporter().testStarting( reportEntry );
+ }
+ }
+
+ private final class TestSucceededListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.remove( reportEntry.getSourceName() );
+ getTestSetReporter().testSucceeded( reportEntry );
+ }
+ }
+
+ private final class TestFailedListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.remove( reportEntry.getSourceName() );
+ getTestSetReporter().testFailed( reportEntry );
+ }
+ }
+
+ private final class TestSkippedListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.remove( reportEntry.getSourceName() );
+ getTestSetReporter().testSkipped( reportEntry );
+ }
+ }
+
+ private final class TestErrorListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.remove( reportEntry.getSourceName() );
+ getTestSetReporter().testError( reportEntry );
+ }
+ }
+
+ private final class TestAssumptionFailureListener implements ForkedProcessReportEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, ReportEntry reportEntry )
+ {
+ testsInProgress.remove( reportEntry.getSourceName() );
+ getTestSetReporter().testAssumptionFailure( reportEntry );
+ }
+ }
+
+ private final class SystemPropertiesListener implements ForkedProcessPropertyEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, String key, String value )
+ {
+ testVmSystemProperties.put( key, value );
+ }
+ }
+
+ private final class StdOutListener implements ForkedProcessStandardOutErrEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, String output, boolean newLine )
+ {
+ writeTestOutput( output, newLine, true );
+ }
+ }
+
+ private final class StdErrListener implements ForkedProcessStandardOutErrEventListener
+ {
+ @Override
+ public void handle( RunMode runMode, String output, boolean newLine )
+ {
+ writeTestOutput( output, newLine, false );
+ }
+ }
+
+ private final class ConsoleListener implements ForkedProcessStringEventListener
+ {
+ @Override
+ public void handle( String msg )
+ {
+ getOrCreateConsoleLogger()
+ .info( msg );
+ }
+ }
+
+ private final class AcquireNextTestListener implements ForkedProcessEventListener
+ {
+ @Override
+ public void handle()
+ {
+ notifiableTestStream.provideNewTest();
+ }
+ }
+
+ private class ErrorListener implements ForkedProcessStackTraceEventListener
+ {
+ @Override
+ public void handle( String msg, String smartStackTrace, String stackTrace )
+ {
+ if ( errorInFork == null )
+ {
+ errorInFork = deserializeStackTraceWriter( msg, smartStackTrace, stackTrace );
+ if ( msg != null )
+ {
+ getOrCreateConsoleLogger()
+ .error( msg );
+ }
+ }
+ dumpToLoFile( msg, null );
+ }
+ }
+
+ private final class ByeListener implements ForkedProcessEventListener
+ {
+ @Override
+ public void handle()
+ {
+ saidGoodBye = true;
+ notifiableTestStream.acknowledgeByeEventReceived();
+ }
+ }
+
+ private final class StopOnNextTestListener implements ForkedProcessEventListener
+ {
+ @Override
+ public void handle()
+ {
+ stopOnNextTest();
+ }
+ }
+
+ private final class DebugListener implements ForkedProcessStringEventListener
+ {
+ @Override
+ public void handle( String msg )
+ {
+ getOrCreateConsoleLogger()
+ .debug( msg );
+ }
+ }
+
+ private final class WarningListener implements ForkedProcessStringEventListener
+ {
+ @Override
+ public void handle( String msg )
+ {
+ getOrCreateConsoleLogger()
+ .warning( msg );
+ }
}
+ /**
+ * Overridden by a subclass, see {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}.
+ */
protected void stopOnNextTest()
{
}
@@ -204,125 +406,24 @@ private RunListener getTestSetReporter()
private void processLine( String event )
{
- final OperationalData op;
- try
- {
- op = new OperationalData( event );
- }
- catch ( RuntimeException e )
- {
- logStreamWarning( e, event );
- return;
- }
- final String remaining = op.getData();
- switch ( op.getOperationId() )
- {
- case BOOTERCODE_TESTSET_STARTING:
- getTestSetReporter().testSetStarting( createReportEntry( remaining ) );
- setCurrentStartTime();
- break;
- case BOOTERCODE_TESTSET_COMPLETED:
- testsInProgress.clear();
-
- getTestSetReporter().testSetCompleted( createReportEntry( remaining, testVmSystemProperties ) );
- break;
- case BOOTERCODE_TEST_STARTING:
- ReportEntry reportEntry = createReportEntry( remaining );
- testsInProgress.offer( reportEntry.getSourceName() );
-
- getTestSetReporter().testStarting( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_TEST_SUCCEEDED:
- reportEntry = createReportEntry( remaining );
- testsInProgress.remove( reportEntry.getSourceName() );
-
- getTestSetReporter().testSucceeded( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_TEST_FAILED:
- reportEntry = createReportEntry( remaining );
- testsInProgress.remove( reportEntry.getSourceName() );
-
- getTestSetReporter().testFailed( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_TEST_SKIPPED:
- reportEntry = createReportEntry( remaining );
- testsInProgress.remove( reportEntry.getSourceName() );
-
- getTestSetReporter().testSkipped( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_TEST_ERROR:
- reportEntry = createReportEntry( remaining );
- testsInProgress.remove( reportEntry.getSourceName() );
-
- getTestSetReporter().testError( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_TEST_ASSUMPTIONFAILURE:
- reportEntry = createReportEntry( remaining );
- testsInProgress.remove( reportEntry.getSourceName() );
-
- getTestSetReporter().testAssumptionFailure( createReportEntry( remaining ) );
- break;
- case BOOTERCODE_SYSPROPS:
- int keyEnd = remaining.indexOf( "," );
- StringBuilder key = new StringBuilder();
- StringBuilder value = new StringBuilder();
- unescapeString( key, remaining.substring( 0, keyEnd ) );
- unescapeString( value, remaining.substring( keyEnd + 1 ) );
- testVmSystemProperties.put( key.toString(), value.toString() );
- break;
- case BOOTERCODE_STDOUT:
- writeTestOutput( remaining, true );
- break;
- case BOOTERCODE_STDERR:
- writeTestOutput( remaining, false );
- break;
- case BOOTERCODE_CONSOLE:
- getOrCreateConsoleLogger()
- .info( createConsoleMessage( remaining ) );
- break;
- case BOOTERCODE_NEXT_TEST:
- notifiableTestStream.provideNewTest();
- break;
- case BOOTERCODE_ERROR:
- errorInFork = deserializeStackTraceWriter( new StringTokenizer( remaining, "," ) );
- break;
- case BOOTERCODE_BYE:
- saidGoodBye = true;
- notifiableTestStream.acknowledgeByeEventReceived();
- break;
- case BOOTERCODE_STOP_ON_NEXT_TEST:
- stopOnNextTest();
- break;
- case BOOTERCODE_DEBUG:
- getOrCreateConsoleLogger()
- .debug( createConsoleMessage( remaining ) );
- break;
- case BOOTERCODE_WARNING:
- getOrCreateConsoleLogger()
- .warning( createConsoleMessage( remaining ) );
- break;
- default:
- logStreamWarning( event );
- }
+ decoder.handleEvent( event, errorHandler );
}
- private void logStreamWarning( String event )
+ private File dumpToLoFile( String msg, Throwable e )
{
- logStreamWarning( null, event );
+ File reportsDir = defaultReporterFactory.getReportsDirectory();
+ InPluginProcessDumpSingleton util = InPluginProcessDumpSingleton.getSingleton();
+ return e == null
+ ? util.dumpStreamText( msg, reportsDir, forkNumber )
+ : util.dumpStreamException( e, msg, reportsDir, forkNumber );
}
- private void logStreamWarning( Throwable e, String event )
+ private void logStreamWarning( String event, Throwable e )
{
if ( event == null || !event.contains( PRINTABLE_JVM_NATIVE_STREAM ) )
{
String msg = "Corrupted STDOUT by directly writing to native stream in forked JVM " + forkNumber + ".";
-
- InPluginProcessDumpSingleton util = InPluginProcessDumpSingleton.getSingleton();
- File reportsDir = defaultReporterFactory.getReportsDirectory();
- File dump =
- e == null
- ? util.dumpStreamText( msg + " Stream '" + event + "'.", reportsDir, forkNumber )
- : util.dumpStreamException( e, msg + " Stream '" + event + "'.", reportsDir, forkNumber );
+ File dump = dumpToLoFile( msg + " Stream '" + event + "'.", e );
if ( printedErrorStream.compareAndSet( false, true ) )
{
@@ -352,91 +453,29 @@ else if ( log.isInfoEnabled() )
}
}
- private void writeTestOutput( String remaining, boolean isStdout )
+ private void writeTestOutput( String output, boolean newLine, boolean isStdout )
{
- int csNameEnd = remaining.indexOf( ',' );
- String charsetName = remaining.substring( 0, csNameEnd );
- String byteEncoded = remaining.substring( csNameEnd + 1 );
- ByteBuffer unescaped = unescapeBytes( byteEncoded, charsetName );
-
- if ( unescaped.hasArray() )
- {
- byte[] convertedBytes = unescaped.array();
- getOrCreateConsoleOutputReceiver()
- .writeTestOutput( convertedBytes, unescaped.position(), unescaped.remaining(), isStdout );
- }
- else
- {
- byte[] convertedBytes = new byte[unescaped.remaining()];
- unescaped.get( convertedBytes, 0, unescaped.remaining() );
- getOrCreateConsoleOutputReceiver()
- .writeTestOutput( convertedBytes, 0, convertedBytes.length, isStdout );
- }
+ getOrCreateConsoleOutputReceiver()
+ .writeTestOutput( output, newLine, isStdout );
}
public final void consumeMultiLineContent( String s )
throws IOException
{
- BufferedReader stringReader = new BufferedReader( new StringReader( s ) );
- for ( String s1 = stringReader.readLine(); s1 != null; s1 = stringReader.readLine() )
+ if ( isBlank( s ) )
{
- consumeLine( s1 );
+ logStreamWarning( s, null );
}
- }
-
- private String createConsoleMessage( String remaining )
- {
- return unescape( remaining );
- }
-
- private TestSetReportEntry createReportEntry( String untokenized )
- {
- return createReportEntry( untokenized, Collections.emptyMap() );
- }
-
- private TestSetReportEntry createReportEntry( String untokenized, Map systemProperties )
- {
- StringTokenizer tokens = new StringTokenizer( untokenized, "," );
- try
- {
- String source = nullableCsv( tokens.nextToken() );
- String name = nullableCsv( tokens.nextToken() );
- String group = nullableCsv( tokens.nextToken() );
- String message = nullableCsv( tokens.nextToken() );
- String elapsedStr = tokens.nextToken();
- Integer elapsed = "null".equals( elapsedStr ) ? null : decode( elapsedStr );
- final StackTraceWriter stackTraceWriter =
- tokens.hasMoreTokens() ? deserializeStackTraceWriter( tokens ) : null;
-
- return reportEntry( source, name, group, stackTraceWriter, elapsed, message, systemProperties );
- }
- catch ( RuntimeException e )
+ else
{
- throw new RuntimeException( untokenized, e );
+ BufferedReader stringReader = new BufferedReader( new StringReader( s ) );
+ for ( String s1 = stringReader.readLine(); s1 != null; s1 = stringReader.readLine() )
+ {
+ consumeLine( s1 );
+ }
}
}
- private StackTraceWriter deserializeStackTraceWriter( StringTokenizer tokens )
- {
- String stackTraceMessage = nullableCsv( tokens.nextToken() );
- String smartStackTrace = nullableCsv( tokens.nextToken() );
- String stackTrace = tokens.hasMoreTokens() ? nullableCsv( tokens.nextToken() ) : null;
- boolean hasTrace = stackTrace != null;
- return hasTrace ? new DeserializedStacktraceWriter( stackTraceMessage, smartStackTrace, stackTrace ) : null;
- }
-
- private String nullableCsv( String source )
- {
- return "null".equals( source ) ? null : unescape( source );
- }
-
- private String unescape( String source )
- {
- StringBuilder stringBuffer = new StringBuilder( source.length() );
- unescapeString( stringBuffer, source );
- return stringBuffer.toString();
- }
-
public final Map getTestVmSystemProperties()
{
return unmodifiableMap( testVmSystemProperties );
@@ -493,32 +532,10 @@ public boolean hasTestsInProgress()
return !testsInProgress.isEmpty();
}
- private static final class OperationalData
+ private StackTraceWriter deserializeStackTraceWriter( String stackTraceMessage,
+ String smartStackTrace, String stackTrace )
{
- private final byte operationId;
- private final String data;
-
- OperationalData( String event )
- {
- operationId = (byte) event.charAt( 0 );
- int comma = event.indexOf( ",", 3 );
- if ( comma < 0 )
- {
- throw new IllegalArgumentException( "Stream stdin corrupted. Expected comma after third character "
- + "in command '" + event + "'." );
- }
- int rest = event.indexOf( ",", comma );
- data = event.substring( rest + 1 );
- }
-
- byte getOperationId()
- {
- return operationId;
- }
-
- String getData()
- {
- return data;
- }
+ boolean hasTrace = stackTrace != null;
+ return hasTrace ? new DeserializedStacktraceWriter( stackTraceMessage, smartStackTrace, stackTrace ) : null;
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java
new file mode 100644
index 0000000000..b7950d0ba3
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoder.java
@@ -0,0 +1,352 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.commons.codec.binary.Base64;
+import org.apache.maven.surefire.booter.ForkedProcessEvent;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.RunMode;
+import org.apache.maven.surefire.report.StackTraceWriter;
+
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.MAGIC_NUMBER;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_STDERR;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_STDERR_NEW_LINE;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_STDOUT;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_STDOUT_NEW_LINE;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_BYE;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_CONSOLE_DEBUG;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_CONSOLE_INFO;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_CONSOLE_WARNING;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_STOP_ON_NEXT_TEST;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_ERROR;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_FAILED;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_SKIPPED;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_STARTING;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TEST_SUCCEEDED;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TESTSET_COMPLETED;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.BOOTERCODE_TESTSET_STARTING;
+import static org.apache.maven.surefire.booter.ForkedProcessEvent.EVENTS;
+import static org.apache.maven.surefire.report.CategorizedReportEntry.reportEntry;
+import static org.apache.maven.surefire.report.RunMode.MODES;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * magic number : run mode : opcode [: opcode specific data]*
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public final class ForkedChannelDecoder
+{
+ private static final Base64 BASE64 = new Base64();
+
+ private volatile ForkedProcessPropertyEventListener propertyEventListener;
+ private volatile ForkedProcessStackTraceEventListener consoleErrorEventListener;
+ private volatile ForkedProcessExitErrorListener exitErrorEventListener;
+
+ private final ConcurrentMap> reportEventListeners =
+ new ConcurrentHashMap<>();
+
+ private final ConcurrentMap stdOutErrEventListeners =
+ new ConcurrentHashMap<>();
+
+ private final ConcurrentMap consoleEventListeners =
+ new ConcurrentHashMap<>();
+
+ private final ConcurrentMap controlEventListeners =
+ new ConcurrentHashMap<>();
+
+ public void setSystemPropertiesListener( ForkedProcessPropertyEventListener listener )
+ {
+ propertyEventListener = requireNonNull( listener );
+ }
+
+ public void setTestSetStartingListener( ForkedProcessReportEventListener listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TESTSET_STARTING, requireNonNull( listener ) );
+ }
+
+ public void setTestSetCompletedListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TESTSET_COMPLETED, requireNonNull( listener ) );
+ }
+
+ public void setTestStartingListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_STARTING, requireNonNull( listener ) );
+ }
+
+ public void setTestSucceededListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_SUCCEEDED, requireNonNull( listener ) );
+ }
+
+ public void setTestFailedListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_FAILED, requireNonNull( listener ) );
+ }
+
+ public void setTestSkippedListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_SKIPPED, requireNonNull( listener ) );
+ }
+
+ public void setTestErrorListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_ERROR, requireNonNull( listener ) );
+ }
+
+ public void setTestAssumptionFailureListener( ForkedProcessReportEventListener> listener )
+ {
+ reportEventListeners.put( BOOTERCODE_TEST_ASSUMPTIONFAILURE, requireNonNull( listener ) );
+ }
+
+ public void setStdOutListener( ForkedProcessStandardOutErrEventListener listener )
+ {
+ stdOutErrEventListeners.put( BOOTERCODE_STDOUT, requireNonNull( listener ) );
+ stdOutErrEventListeners.put( BOOTERCODE_STDOUT_NEW_LINE, requireNonNull( listener ) );
+ }
+
+ public void setStdErrListener( ForkedProcessStandardOutErrEventListener listener )
+ {
+ stdOutErrEventListeners.put( BOOTERCODE_STDERR, requireNonNull( listener ) );
+ stdOutErrEventListeners.put( BOOTERCODE_STDERR_NEW_LINE, requireNonNull( listener ) );
+ }
+
+ public void setConsoleInfoListener( ForkedProcessStringEventListener listener )
+ {
+ consoleEventListeners.put( BOOTERCODE_CONSOLE_INFO, requireNonNull( listener ) );
+ }
+
+ public void setConsoleErrorListener( ForkedProcessStackTraceEventListener listener )
+ {
+ consoleErrorEventListener = requireNonNull( listener );
+ }
+
+ public void setConsoleDebugListener( ForkedProcessStringEventListener listener )
+ {
+ consoleEventListeners.put( BOOTERCODE_CONSOLE_DEBUG, requireNonNull( listener ) );
+ }
+
+ public void setConsoleWarningListener( ForkedProcessStringEventListener listener )
+ {
+ consoleEventListeners.put( BOOTERCODE_CONSOLE_WARNING, requireNonNull( listener ) );
+ }
+
+ public void setByeListener( ForkedProcessEventListener listener )
+ {
+ controlEventListeners.put( BOOTERCODE_BYE, requireNonNull( listener ) );
+ }
+
+ public void setStopOnNextTestListener( ForkedProcessEventListener listener )
+ {
+ controlEventListeners.put( BOOTERCODE_STOP_ON_NEXT_TEST, requireNonNull( listener ) );
+ }
+
+ public void setAcquireNextTestListener( ForkedProcessEventListener listener )
+ {
+ controlEventListeners.put( BOOTERCODE_NEXT_TEST, requireNonNull( listener ) );
+ }
+
+ public void setExitErrorEventListener( ForkedProcessExitErrorListener listener )
+ {
+ exitErrorEventListener = requireNonNull( listener );
+ }
+
+ public void handleEvent( String line, ForkedChannelDecoderErrorHandler errorHandler )
+ {
+ if ( line == null || !line.startsWith( MAGIC_NUMBER ) )
+ {
+ errorHandler.handledError( line, null );
+ return;
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer( line.substring( MAGIC_NUMBER.length() ), ":" );
+ String opcode = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ ForkedProcessEvent event = opcode == null ? null : EVENTS.get( opcode );
+ if ( event == null )
+ {
+ errorHandler.handledError( line, null );
+ return;
+ }
+
+ try
+ {
+ if ( event.isControlCategory() )
+ {
+ ForkedProcessEventListener listener = controlEventListeners.get( event );
+ if ( listener != null )
+ {
+ listener.handle();
+ }
+ }
+ else if ( event.isConsoleCategory() )
+ {
+ ForkedProcessStringEventListener listener = consoleEventListeners.get( event );
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ if ( listener != null && encoding != null )
+ {
+ String msg = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ listener.handle( msg );
+ }
+ }
+ else if ( event.isConsoleErrorCategory() )
+ {
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ if ( consoleErrorEventListener != null && encoding != null )
+ {
+ String msg = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : null;
+ String smartStackTrace =
+ tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : null;
+ String stackTrace = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : null;
+ consoleErrorEventListener.handle( msg, smartStackTrace, stackTrace );
+ }
+ }
+ else if ( event.isStandardStreamCategory() )
+ {
+ ForkedProcessStandardOutErrEventListener listener = stdOutErrEventListeners.get( event );
+ RunMode mode = tokenizer.hasMoreTokens() ? MODES.get( tokenizer.nextToken() ) : null;
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ if ( listener != null && encoding != null && mode != null )
+ {
+ boolean newLine = event == BOOTERCODE_STDOUT_NEW_LINE || event == BOOTERCODE_STDERR_NEW_LINE;
+ String output = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ listener.handle( mode, output, newLine );
+ }
+ }
+ else if ( event.isSysPropCategory() )
+ {
+ RunMode mode = tokenizer.hasMoreTokens() ? MODES.get( tokenizer.nextToken() ) : null;
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ String key = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ if ( propertyEventListener != null && isNotBlank( key ) )
+ {
+ String value = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ propertyEventListener.handle( mode, key, value );
+ }
+ }
+ else if ( event.isTestCategory() )
+ {
+ ForkedProcessReportEventListener listener = reportEventListeners.get( event );
+ RunMode mode = tokenizer.hasMoreTokens() ? MODES.get( tokenizer.nextToken() ) : null;
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ if ( listener != null && encoding != null && mode != null )
+ {
+ String sourceName = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String sourceText = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String name = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String nameText = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String group = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String message = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String elapsed = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String traceMessage = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String smartTrimmedStackTrace = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ String stackTrace = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
+ ReportEntry reportEntry = toReportEntry( encoding, sourceName, sourceText, name, nameText,
+ group, message, elapsed, traceMessage, smartTrimmedStackTrace, stackTrace );
+ listener.handle( mode, reportEntry );
+ }
+ }
+ else if ( event.isJvmExitError() )
+ {
+ if ( exitErrorEventListener != null )
+ {
+ Charset encoding = tokenizer.hasMoreTokens() ? Charset.forName( tokenizer.nextToken() ) : null;
+ String message = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ String smartTrimmedStackTrace =
+ tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ String stackTrace = tokenizer.hasMoreTokens() ? decode( tokenizer.nextToken(), encoding ) : "";
+ exitErrorEventListener.handle( message, smartTrimmedStackTrace, stackTrace );
+ }
+ }
+ }
+ catch ( IllegalArgumentException e )
+ {
+ errorHandler.handledError( line, e );
+ }
+ }
+
+ static ReportEntry toReportEntry( Charset encoding,
+ // ReportEntry:
+ String encSource, String encSourceText, String encName, String encNameText,
+ String encGroup, String encMessage, String encTimeElapsed,
+ // StackTraceWriter:
+ String encTraceMessage, String encSmartTrimmedStackTrace, String encStackTrace )
+ throws NumberFormatException
+ {
+ if ( encoding == null )
+ {
+ // corrupted or incomplete stream
+ return null;
+ }
+
+ String source = decode( encSource, encoding );
+ String sourceText = decode( encSourceText, encoding );
+ String name = decode( encName, encoding );
+ String nameText = decode( encNameText, encoding );
+ String group = decode( encGroup, encoding );
+ StackTraceWriter stackTraceWriter =
+ decodeTrace( encoding, encTraceMessage, encSmartTrimmedStackTrace, encStackTrace );
+ Integer elapsed = decodeToInteger( encTimeElapsed );
+ String message = decode( encMessage, encoding );
+ return reportEntry( source, sourceText, name, nameText,
+ group, stackTraceWriter, elapsed, message, Collections.emptyMap() );
+ }
+
+ static String decode( String line, Charset encoding )
+ {
+ // ForkedChannelEncoder is encoding the stream with US_ASCII
+ return line == null || "-".equals( line )
+ ? null
+ : new String( BASE64.decode( line.getBytes( US_ASCII ) ), encoding );
+ }
+
+ static Integer decodeToInteger( String line )
+ {
+ return line == null || "-".equals( line ) ? null : Integer.decode( line );
+ }
+
+ private static StackTraceWriter decodeTrace( Charset encoding, String encTraceMessage,
+ String encSmartTrimmedStackTrace, String encStackTrace )
+ {
+ if ( isBlank( encStackTrace ) || "-".equals( encStackTrace ) )
+ {
+ return null;
+ }
+ else
+ {
+ String traceMessage = decode( encTraceMessage, encoding );
+ String stackTrace = decode( encStackTrace, encoding );
+ String smartTrimmedStackTrace = decode( encSmartTrimmedStackTrace, encoding );
+ return new DeserializedStacktraceWriter( traceMessage, smartTrimmedStackTrace, stackTrace );
+ }
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderErrorHandler.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderErrorHandler.java
new file mode 100644
index 0000000000..8faa8035a2
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedChannelDecoderErrorHandler.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedChannelDecoderErrorHandler
+{
+ void handledError( String line, Throwable e );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventListener.java
new file mode 100644
index 0000000000..61482958ae
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventListener.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessEventListener
+{
+ void handle();
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessExitErrorListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessExitErrorListener.java
new file mode 100644
index 0000000000..b14c38c11e
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessExitErrorListener.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessExitErrorListener
+{
+ void handle( String exceptionMessage, String smartTrimmedStackTrace, String stackTrace );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessPropertyEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessPropertyEventListener.java
new file mode 100644
index 0000000000..9ef3e0923c
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessPropertyEventListener.java
@@ -0,0 +1,31 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.report.RunMode;
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessPropertyEventListener
+{
+ void handle( RunMode runMode, String key, String value );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java
new file mode 100644
index 0000000000..06054df61c
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessReportEventListener.java
@@ -0,0 +1,33 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.report.ReportEntry;
+import org.apache.maven.surefire.report.RunMode;
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ * @param report entry type
+ */
+public interface ForkedProcessReportEventListener
+{
+ void handle( RunMode runMode, T reportEntry );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStackTraceEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStackTraceEventListener.java
new file mode 100644
index 0000000000..f54cc409cd
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStackTraceEventListener.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessStackTraceEventListener
+{
+ void handle( String msg, String smartStackTrace, String stackTrace );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
new file mode 100644
index 0000000000..b60d6ed4f8
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
@@ -0,0 +1,31 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.report.RunMode;
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessStandardOutErrEventListener
+{
+ void handle( RunMode runMode, String output, boolean newLine );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStringEventListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStringEventListener.java
new file mode 100644
index 0000000000..9bd863aa3c
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStringEventListener.java
@@ -0,0 +1,29 @@
+package org.apache.maven.plugin.surefire.booterclient.output;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public interface ForkedProcessStringEventListener
+{
+ void handle( String msg );
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
index dad88fd6cc..388f16db33 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
@@ -40,7 +40,7 @@ public final class ThreadedStreamConsumer
{
private static final String END_ITEM = "";
- private static final int ITEM_LIMIT_BEFORE_SLEEP = 10 * 1000;
+ private static final int ITEM_LIMIT_BEFORE_SLEEP = 10_000;
private final BlockingQueue items = new ArrayBlockingQueue<>( ITEM_LIMIT_BEFORE_SLEEP );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java
new file mode 100644
index 0000000000..0eb6b52f9d
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/DefaultStatelessReportMojoConfiguration.java
@@ -0,0 +1,58 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportMojoConfiguration;
+
+import java.io.File;
+import java.util.Deque;
+import java.util.Map;
+
+/**
+ * Why Deprecated: The field {@code testClassMethodRunHistory} makes
+ * {@link org.apache.maven.plugin.surefire.report.StatelessXmlReporter} stateful and overloads reporter by permanently
+ * overriding XML using re-run feature. To fix this issue, the providers should use more events for re-run feature and
+ * events bounding provider's execution. After provider's execution is finished, this reporter should be announced
+ * only once per test class. All test report entries should be cached in
+ * {@link org.apache.maven.plugin.surefire.report.TestSetRunListener} keeping it already stateful.
+ */
+@Deprecated
+public class DefaultStatelessReportMojoConfiguration
+ extends StatelessReportMojoConfiguration
+{
+ private final Map> testClassMethodRunHistory;
+
+ public DefaultStatelessReportMojoConfiguration( File reportsDirectory,
+ String reportNameSuffix,
+ boolean trimStackTrace,
+ int rerunFailingTestsCount,
+ String xsdSchemaLocation,
+ Map> testClassMethodRunHistory )
+ {
+ super( reportsDirectory, reportNameSuffix, trimStackTrace, rerunFailingTestsCount, xsdSchemaLocation );
+ this.testClassMethodRunHistory = testClassMethodRunHistory;
+ }
+
+ public Map> getTestClassMethodRunHistory()
+ {
+ return testClassMethodRunHistory;
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java
new file mode 100644
index 0000000000..e4ac7a6809
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireConsoleOutputReporter.java
@@ -0,0 +1,86 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.plugin.surefire.report.ConsoleOutputFileReporter;
+import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.ConsoleOutputReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+import java.io.File;
+import java.io.PrintStream;
+
+/**
+ * Default implementation for extension of console logger.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * Builds {@link ConsoleOutputReportEventListener listeners}.
+ * The listeners handle test set events.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class SurefireConsoleOutputReporter
+ extends ConsoleOutputReporter
+{
+ @Override
+ public ConsoleOutputReportEventListener createListener( File reportsDirectory, String reportNameSuffix,
+ Integer forkNumber )
+ {
+ return new ConsoleOutputFileReporter( reportsDirectory, reportNameSuffix, false, forkNumber, getEncoding() );
+ }
+
+ @Override
+ public ConsoleOutputReportEventListener createListener( PrintStream out, PrintStream err )
+ {
+ return new DirectConsoleOutput( out, err );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Class> cls = ReflectionUtils.reloadClass( target, this );
+ Object clone = cls.newInstance();
+
+ cls.getMethod( "setDisable", boolean.class )
+ .invoke( clone, isDisable() );
+ cls.getMethod( "setEncoding", String.class )
+ .invoke( clone, getEncoding() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SurefireConsoleOutputReporter{"
+ + "disable=" + isDisable()
+ + ", encoding=" + getEncoding()
+ + '}';
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java
new file mode 100644
index 0000000000..e907bfd184
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessReporter.java
@@ -0,0 +1,98 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+/**
+ * Default implementation for extension of {@link StatelessXmlReporter} in plugin.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * This is a builder of {@link StatelessReportEventListener listener}.
+ * The listener handles testSetCompleted event.
+ *
+ * author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class SurefireStatelessReporter
+ extends StatelessReporter
+{
+ /**
+ * Activated in the injection point of MOJO.
+ */
+ public SurefireStatelessReporter()
+ {
+ this( false, "3.0" );
+ }
+
+ /**
+ * Activated if null injection point in MOJO.
+ * @param disable {@code true} to disable performing the report
+ * @param version (xsd 3.0) version of the schema
+ */
+ public SurefireStatelessReporter( boolean disable, String version )
+ {
+ setDisable( disable );
+ setVersion( version );
+ }
+
+ @Override
+ public StatelessReportEventListener createListener(
+ DefaultStatelessReportMojoConfiguration configuration )
+ {
+ return new StatelessXmlReporter( configuration.getReportsDirectory(),
+ configuration.getReportNameSuffix(),
+ configuration.isTrimStackTrace(),
+ configuration.getRerunFailingTestsCount(),
+ configuration.getTestClassMethodRunHistory(),
+ configuration.getXsdSchemaLocation(),
+ getVersion(),
+ false,
+ false,
+ false,
+ false );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Class> cls = ReflectionUtils.reloadClass( target, this );
+ Object clone = cls.newInstance();
+
+ cls.getMethod( "setDisable", boolean.class )
+ .invoke( clone, isDisable() );
+ cls.getMethod( "setVersion", String.class )
+ .invoke( clone, getVersion() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java
new file mode 100644
index 0000000000..73a099c4d8
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireStatelessTestsetInfoReporter.java
@@ -0,0 +1,87 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * 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.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.ConsoleReporter;
+import org.apache.maven.plugin.surefire.report.FileReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporter;
+import org.apache.maven.surefire.util.ReflectionUtils;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * Default implementation for extension of
+ * {@link StatelessTestsetInfoFileReportEventListener test-set event listener for stateless file and console reporter}
+ * in plugin. Signatures can be changed between major, minor versions or milestones.
+ *
+ * Builds {@link StatelessTestsetInfoFileReportEventListener listeners}.
+ * The listener handles testSetCompleted event.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class SurefireStatelessTestsetInfoReporter
+ extends StatelessTestsetInfoReporter
+{
+ @Override
+ public StatelessTestsetInfoConsoleReportEventListener createListener(
+ ConsoleLogger logger )
+ {
+ return new ConsoleReporter( logger, false, false );
+ }
+
+ @Override
+ public StatelessTestsetInfoFileReportEventListener createListener(
+ File reportsDirectory, String reportNameSuffix, Charset encoding )
+ {
+ return new FileReporter( reportsDirectory, reportNameSuffix, encoding, false, false, false );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Class> cls = ReflectionUtils.reloadClass( target, this );
+ Object clone = cls.newInstance();
+
+ cls.getMethod( "setDisable", boolean.class )
+ .invoke( clone, isDisable() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SurefireStatelessTestsetInfoReporter{disable=" + isDisable() + "}";
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java
new file mode 100644
index 0000000000..f5802b7b08
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5ConsoleOutputReporter.java
@@ -0,0 +1,92 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * 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.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+
+import java.io.File;
+
+/**
+ * The extension of {@link StatelessXmlReporter logger} for JUnit5.
+ * Selectively enables report files upon JUnit5 annotation DisplayName.
+ *
+ * author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class JUnit5ConsoleOutputReporter
+ extends SurefireConsoleOutputReporter
+{
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedFileName;
+
+ public boolean isUsePhrasedFileName()
+ {
+ return usePhrasedFileName;
+ }
+
+ public void setUsePhrasedFileName( boolean usePhrasedFileName )
+ {
+ this.usePhrasedFileName = usePhrasedFileName;
+ }
+
+ @Override
+ public ConsoleOutputReportEventListener createListener( File reportsDirectory, String reportNameSuffix,
+ Integer forkNumber )
+ {
+ return new ConsoleOutputFileReporter( reportsDirectory, reportNameSuffix, isUsePhrasedFileName(), forkNumber,
+ getEncoding() );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Object clone = super.clone( target );
+
+ Class> cls = clone.getClass();
+ cls.getMethod( "setUsePhrasedFileName", boolean.class )
+ .invoke( clone, isUsePhrasedFileName() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "JUnit5ConsoleOutputReporter{"
+ + "disable=" + isDisable()
+ + ", encoding=" + getEncoding()
+ + ", usePhrasedFileName=" + isUsePhrasedFileName()
+ + '}';
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java
new file mode 100644
index 0000000000..3c1b76ba52
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5StatelessTestsetInfoReporter.java
@@ -0,0 +1,148 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * 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.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.ConsoleReporter;
+import org.apache.maven.plugin.surefire.report.FileReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * Extension of {@link StatelessTestsetInfoConsoleReportEventListener file and console reporter of test-set} for JUnit5.
+ * Signatures can be changed between major, minor versions or milestones.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class JUnit5StatelessTestsetInfoReporter
+ extends SurefireStatelessTestsetInfoReporter
+{
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedFileName;
+
+ /**
+ * Phrased class name of test case in the console log (see xxx)
+ * Running xxx or file report log Test set: xxx.
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedClassNameInRunning;
+
+ /**
+ * Phrased class name of test case in the log (see xxx)
+ * Tests run: ., Failures: ., Errors: ., Skipped: ., Time elapsed: . s, - in xxx.
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedClassNameInTestCaseSummary;
+
+ public boolean isUsePhrasedFileName()
+ {
+ return usePhrasedFileName;
+ }
+
+ public void setUsePhrasedFileName( boolean usePhrasedFileName )
+ {
+ this.usePhrasedFileName = usePhrasedFileName;
+ }
+
+ public boolean isUsePhrasedClassNameInRunning()
+ {
+ return usePhrasedClassNameInRunning;
+ }
+
+ public void setUsePhrasedClassNameInRunning( boolean usePhrasedClassNameInRunning )
+ {
+ this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+ }
+
+ public boolean isUsePhrasedClassNameInTestCaseSummary()
+ {
+ return usePhrasedClassNameInTestCaseSummary;
+ }
+
+ public void setUsePhrasedClassNameInTestCaseSummary( boolean usePhrasedClassNameInTestCaseSummary )
+ {
+ this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
+ }
+
+ @Override
+ public StatelessTestsetInfoConsoleReportEventListener createListener(
+ ConsoleLogger logger )
+ {
+ return new ConsoleReporter( logger, isUsePhrasedClassNameInRunning(),
+ isUsePhrasedClassNameInTestCaseSummary() );
+ }
+
+ @Override
+ public StatelessTestsetInfoFileReportEventListener createListener(
+ File reportsDirectory, String reportNameSuffix, Charset encoding )
+ {
+ return new FileReporter( reportsDirectory, reportNameSuffix, encoding, isUsePhrasedFileName(),
+ isUsePhrasedClassNameInRunning(), isUsePhrasedClassNameInTestCaseSummary() );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Object clone = super.clone( target );
+
+ Class> cls = clone.getClass();
+ cls.getMethod( "setUsePhrasedFileName", boolean.class )
+ .invoke( clone, isUsePhrasedFileName() );
+ cls.getMethod( "setUsePhrasedClassNameInTestCaseSummary", boolean.class )
+ .invoke( clone, isUsePhrasedFileName() );
+ cls.getMethod( "setUsePhrasedClassNameInRunning", boolean.class )
+ .invoke( clone, isUsePhrasedFileName() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "JUnit5StatelessTestsetInfoReporter{"
+ + "disable=" + isDisable()
+ + ", usePhrasedFileName=" + isUsePhrasedFileName()
+ + ", usePhrasedClassNameInRunning=" + isUsePhrasedClassNameInRunning()
+ + ", usePhrasedClassNameInTestCaseSummary=" + isUsePhrasedClassNameInTestCaseSummary()
+ + "}";
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java
new file mode 100644
index 0000000000..83dca7da9d
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/junit5/JUnit5Xml30StatelessReporter.java
@@ -0,0 +1,161 @@
+package org.apache.maven.plugin.surefire.extensions.junit5;
+
+/*
+ * 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.plugin.surefire.extensions.DefaultStatelessReportMojoConfiguration;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.report.StatelessXmlReporter;
+import org.apache.maven.plugin.surefire.report.TestSetStats;
+import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+
+/**
+ * The extension of {@link StatelessXmlReporter xml reporter} based on XSD version 3.0 for JUnit5.
+ * Selectively enables phrased classes, methods and report files upon JUnit5 annotation DisplayName.
+ *
+ * author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class JUnit5Xml30StatelessReporter
+ extends SurefireStatelessReporter
+{
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedFileName;
+
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedTestSuiteClassName;
+
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test class annotated DisplayName.
+ */
+ private boolean usePhrasedTestCaseClassName;
+
+ /**
+ * {@code false} by default.
+ *
+ * This action takes effect only in JUnit5 provider together with a test method annotated DisplayName.
+ */
+ private boolean usePhrasedTestCaseMethodName;
+
+ public boolean getUsePhrasedFileName()
+ {
+ return usePhrasedFileName;
+ }
+
+ public void setUsePhrasedFileName( boolean usePhrasedFileName )
+ {
+ this.usePhrasedFileName = usePhrasedFileName;
+ }
+
+ public boolean getUsePhrasedTestSuiteClassName()
+ {
+ return usePhrasedTestSuiteClassName;
+ }
+
+ public void setUsePhrasedTestSuiteClassName( boolean usePhrasedTestSuiteClassName )
+ {
+ this.usePhrasedTestSuiteClassName = usePhrasedTestSuiteClassName;
+ }
+
+ public boolean getUsePhrasedTestCaseClassName()
+ {
+ return usePhrasedTestCaseClassName;
+ }
+
+ public void setUsePhrasedTestCaseClassName( boolean usePhrasedTestCaseClassName )
+ {
+ this.usePhrasedTestCaseClassName = usePhrasedTestCaseClassName;
+ }
+
+ public boolean getUsePhrasedTestCaseMethodName()
+ {
+ return usePhrasedTestCaseMethodName;
+ }
+
+ public void setUsePhrasedTestCaseMethodName( boolean usePhrasedTestCaseMethodName )
+ {
+ this.usePhrasedTestCaseMethodName = usePhrasedTestCaseMethodName;
+ }
+
+ @Override
+ public StatelessReportEventListener createListener(
+ DefaultStatelessReportMojoConfiguration configuration )
+ {
+ return new StatelessXmlReporter( configuration.getReportsDirectory(),
+ configuration.getReportNameSuffix(),
+ configuration.isTrimStackTrace(),
+ configuration.getRerunFailingTestsCount(),
+ configuration.getTestClassMethodRunHistory(),
+ configuration.getXsdSchemaLocation(),
+ getVersion(),
+ getUsePhrasedFileName(),
+ getUsePhrasedTestSuiteClassName(),
+ getUsePhrasedTestCaseClassName(),
+ getUsePhrasedTestCaseMethodName() );
+ }
+
+ @Override
+ public Object clone( ClassLoader target )
+ {
+ try
+ {
+ Object clone = super.clone( target );
+
+ Class> cls = clone.getClass();
+ cls.getMethod( "setUsePhrasedFileName", boolean.class )
+ .invoke( clone, getUsePhrasedFileName() );
+ cls.getMethod( "setUsePhrasedTestSuiteClassName", boolean.class )
+ .invoke( clone, getUsePhrasedTestSuiteClassName() );
+ cls.getMethod( "setUsePhrasedTestCaseClassName", boolean.class )
+ .invoke( clone, getUsePhrasedTestCaseClassName() );
+ cls.getMethod( "setUsePhrasedTestCaseMethodName", boolean.class )
+ .invoke( clone, getUsePhrasedTestCaseMethodName() );
+
+ return clone;
+ }
+ catch ( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( e.getLocalizedMessage() );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "JUnit5Xml30StatelessReporter{"
+ + "version=" + getVersion()
+ + ", disable=" + isDisable()
+ + ", usePhrasedFileName=" + getUsePhrasedFileName()
+ + ", usePhrasedTestSuiteClassName=" + getUsePhrasedTestSuiteClassName()
+ + ", usePhrasedTestCaseClassName=" + getUsePhrasedTestCaseClassName()
+ + ", usePhrasedTestCaseMethodName=" + getUsePhrasedTestCaseMethodName()
+ + '}';
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
index 0ea0a78e90..3bc3f2cd77 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
@@ -21,16 +21,19 @@
import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.ReentrantLock;
import static org.apache.maven.plugin.surefire.report.FileReporter.getReportFile;
+import static org.apache.maven.surefire.util.internal.StringUtils.NL;
/**
* Surefire output consumer proxy that writes test output to a {@link java.io.File} for each test suite.
@@ -41,14 +44,16 @@
public class ConsoleOutputFileReporter
implements TestcycleConsoleOutputReceiver
{
- private static final int STREAM_BUFFER_SIZE = 16 * 1024;
+ private static final int STREAM_BUFFER_SIZE = 64 * 1024;
private static final int OPEN = 0;
private static final int CLOSED_TO_REOPEN = 1;
private static final int CLOSED = 2;
private final File reportsDirectory;
private final String reportNameSuffix;
+ private final boolean usePhrasedFileName;
private final Integer forkNumber;
+ private final String encoding;
private final AtomicStampedReference fileOutputStream =
new AtomicStampedReference<>( null, OPEN );
@@ -57,15 +62,18 @@ public class ConsoleOutputFileReporter
private volatile String reportEntryName;
- public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix, Integer forkNumber )
+ public ConsoleOutputFileReporter( File reportsDirectory, String reportNameSuffix, boolean usePhrasedFileName,
+ Integer forkNumber, String encoding )
{
this.reportsDirectory = reportsDirectory;
this.reportNameSuffix = reportNameSuffix;
+ this.usePhrasedFileName = usePhrasedFileName;
this.forkNumber = forkNumber;
+ this.encoding = encoding;
}
@Override
- public void testSetStarting( ReportEntry reportEntry )
+ public void testSetStarting( TestSetReportEntry reportEntry )
{
lock.lock();
try
@@ -79,7 +87,7 @@ public void testSetStarting( ReportEntry reportEntry )
}
@Override
- public void testSetCompleted( ReportEntry report )
+ public void testSetCompleted( TestSetReportEntry report )
{
}
@@ -99,7 +107,7 @@ public void close()
}
@Override
- public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+ public void writeTestOutput( String output, boolean newLine, boolean stdout )
{
lock.lock();
try
@@ -121,12 +129,23 @@ public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
os = new BufferedOutputStream( new FileOutputStream( file ), STREAM_BUFFER_SIZE );
fileOutputStream.set( os, OPEN );
}
- os.write( buf, off, len );
+
+ if ( output == null )
+ {
+ output = "null";
+ }
+ Charset charset = Charset.forName( encoding );
+ os.write( output.getBytes( charset ) );
+ if ( newLine )
+ {
+ os.write( NL.getBytes( charset ) );
+ }
}
}
catch ( IOException e )
{
dumpException( e );
+ // todo use UncheckedIOException in Java 8
throw new RuntimeException( e );
}
finally
@@ -150,7 +169,7 @@ private void closeNullReportFile( ReportEntry reportEntry )
finally
{
// prepare -output.txt report file
- reportEntryName = reportEntry.getName();
+ reportEntryName = usePhrasedFileName ? reportEntry.getSourceText() : reportEntry.getSourceName();
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
index 36f311bdf5..0bee6879d7 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleReporter.java
@@ -23,8 +23,9 @@
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.shared.utils.logging.MessageBuilder;
-import org.apache.maven.surefire.report.ReportEntry;
import org.apache.maven.plugin.surefire.log.api.Level;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.report.TestSetReportEntry;
import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
import static org.apache.maven.plugin.surefire.report.TestSetStats.concatenateWithTestGroup;
@@ -37,31 +38,33 @@
* @author Kristian Rosenvold
*/
public class ConsoleReporter
+ extends StatelessTestsetInfoConsoleReportEventListener
{
public static final String BRIEF = "brief";
-
public static final String PLAIN = "plain";
-
private static final String TEST_SET_STARTING_PREFIX = "Running ";
- private final ConsoleLogger logger;
-
- public ConsoleReporter( ConsoleLogger logger )
- {
- this.logger = logger;
- }
+ private final boolean usePhrasedClassNameInRunning;
+ private final boolean usePhrasedClassNameInTestCaseSummary;
- public ConsoleLogger getConsoleLogger()
+ public ConsoleReporter( ConsoleLogger logger,
+ boolean usePhrasedClassNameInRunning, boolean usePhrasedClassNameInTestCaseSummary )
{
- return logger;
+ super( logger );
+ this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+ this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
}
- public void testSetStarting( ReportEntry report )
+ @Override
+ public void testSetStarting( TestSetReportEntry report )
{
- MessageBuilder builder = buffer();
- logger.info( concatenateWithTestGroup( builder.a( TEST_SET_STARTING_PREFIX ), report ) );
+ MessageBuilder builder = buffer().a( TEST_SET_STARTING_PREFIX );
+ String runningTestCase = concatenateWithTestGroup( builder, report, usePhrasedClassNameInRunning );
+ getConsoleLogger()
+ .info( runningTestCase );
}
+ @Override
public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List testResults )
{
boolean success = testSetStats.getCompletedCount() > 0;
@@ -71,13 +74,14 @@ public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetSta
boolean flakes = testSetStats.getSkipped() > 0;
Level level = resolveLevel( success, failures, errors, skipped, flakes );
- println( testSetStats.getColoredTestSetSummary( report ), level );
+ println( testSetStats.getColoredTestSetSummary( report, usePhrasedClassNameInTestCaseSummary ), level );
for ( String testResult : testResults )
{
println( testResult, level );
}
}
+ @Override
public void reset()
{
}
@@ -87,13 +91,16 @@ private void println( String message, Level level )
switch ( level )
{
case FAILURE:
- logger.error( message );
+ getConsoleLogger()
+ .error( message );
break;
case UNSTABLE:
- logger.warning( message );
+ getConsoleLogger()
+ .warning( message );
break;
default:
- logger.info( message );
+ getConsoleLogger()
+ .info( message );
}
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
index e622c6d186..ed93a659c5 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
@@ -24,6 +24,10 @@
import org.apache.maven.plugin.surefire.log.api.Level;
import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
import org.apache.maven.shared.utils.logging.MessageBuilder;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
import org.apache.maven.surefire.report.ReporterFactory;
import org.apache.maven.surefire.report.RunListener;
import org.apache.maven.surefire.report.RunStatistics;
@@ -113,26 +117,32 @@ public File getReportsDirectory()
return reportConfiguration.getReportsDirectory();
}
- private ConsoleReporter createConsoleReporter()
+ private StatelessTestsetInfoConsoleReportEventListener createConsoleReporter()
{
- return shouldReportToConsole() ? new ConsoleReporter( consoleLogger ) : NullConsoleReporter.INSTANCE;
+ StatelessTestsetInfoConsoleReportEventListener consoleReporter =
+ reportConfiguration.instantiateConsoleReporter( consoleLogger );
+ return useNonNull( consoleReporter, NullConsoleReporter.INSTANCE );
}
- private FileReporter createFileReporter()
+ private StatelessTestsetInfoFileReportEventListener createFileReporter()
{
- FileReporter fileReporter = reportConfiguration.instantiateFileReporter( forkNumber );
+ StatelessTestsetInfoFileReportEventListener fileReporter =
+ reportConfiguration.instantiateFileReporter( forkNumber );
return useNonNull( fileReporter, NullFileReporter.INSTANCE );
}
- private StatelessXmlReporter createSimpleXMLReporter()
+ private StatelessReportEventListener createSimpleXMLReporter()
{
- StatelessXmlReporter xmlReporter = reportConfiguration.instantiateStatelessXmlReporter( forkNumber );
+ StatelessReportEventListener xmlReporter =
+ reportConfiguration.instantiateStatelessXmlReporter( forkNumber );
return useNonNull( xmlReporter, NullStatelessXmlReporter.INSTANCE );
}
- private TestcycleConsoleOutputReceiver createConsoleOutputReceiver()
+ private ConsoleOutputReportEventListener createConsoleOutputReceiver()
{
- return reportConfiguration.instantiateConsoleOutputFileReporter( forkNumber );
+ ConsoleOutputReportEventListener outputReporter =
+ reportConfiguration.instantiateConsoleOutputFileReporter( forkNumber );
+ return useNonNull( outputReporter, NullConsoleOutputReceiver.INSTANCE );
}
private StatisticsReporter createStatisticsReporter()
@@ -141,13 +151,6 @@ private StatisticsReporter createStatisticsReporter()
return useNonNull( statisticsReporter, NullStatisticsReporter.INSTANCE );
}
- private boolean shouldReportToConsole()
- {
- return reportConfiguration.isUseFile()
- ? reportConfiguration.isPrintSummary()
- : reportConfiguration.isRedirectTestOutputToFile() || reportConfiguration.isBriefOrPlainFormat();
- }
-
public void mergeFromOtherFactories( Collection factories )
{
for ( DefaultReporterFactory factory : factories )
@@ -175,10 +178,13 @@ public RunResult close()
public void runStarting()
{
- log( "" );
- log( "-------------------------------------------------------" );
- log( " T E S T S" );
- log( "-------------------------------------------------------" );
+ if ( reportConfiguration.isPrintSummary() )
+ {
+ log( "" );
+ log( "-------------------------------------------------------" );
+ log( " T E S T S" );
+ log( "-------------------------------------------------------" );
+ }
}
private void runCompleted()
@@ -192,14 +198,17 @@ private void runCompleted()
boolean printedFailures = printTestFailures( failure );
boolean printedErrors = printTestFailures( error );
boolean printedFlakes = printTestFailures( flake );
- if ( printedFailures | printedErrors | printedFlakes )
+ if ( reportConfiguration.isPrintSummary() )
{
+ if ( printedFailures | printedErrors | printedFlakes )
+ {
+ log( "" );
+ }
+ boolean hasSuccessful = globalStats.getCompletedCount() > 0;
+ boolean hasSkipped = globalStats.getSkipped() > 0;
+ log( globalStats.getSummary(), hasSuccessful, printedFailures, printedErrors, hasSkipped, printedFlakes );
log( "" );
}
- boolean hasSuccessful = globalStats.getCompletedCount() > 0;
- boolean hasSkipped = globalStats.getSkipped() > 0;
- log( globalStats.getSummary(), hasSuccessful, printedFailures, printedErrors, hasSkipped, printedFlakes );
- log( "" );
}
public RunStatistics getGlobalRunStatistics()
@@ -266,7 +275,7 @@ else if ( seenSuccess )
* Merge all the TestMethodStats in each TestRunListeners and put results into flakyTests, failedTests and
* errorTests, indexed by test class and method name. Update globalStatistics based on the result of the merge.
*/
- void mergeTestHistoryResult()
+ private void mergeTestHistoryResult()
{
globalStats = new RunStatistics();
flakyTests = new TreeMap<>();
@@ -382,7 +391,6 @@ boolean printTestFailures( TestResultType type )
for ( Map.Entry> entry : testStats.entrySet() )
{
- printed = true;
List testMethodStats = entry.getValue();
if ( testMethodStats.size() == 1 )
{
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
index 056e903532..7ef2d82be4 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DirectConsoleOutput.java
@@ -19,15 +19,10 @@
* under the License.
*/
-import java.io.PrintStream;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
+import org.apache.maven.surefire.report.TestSetReportEntry;
-import org.apache.maven.surefire.report.ReportEntry;
+import java.io.PrintStream;
-import static java.nio.ByteBuffer.wrap;
-import static java.nio.charset.Charset.defaultCharset;
import static java.util.Objects.requireNonNull;
/**
@@ -41,44 +36,37 @@
public class DirectConsoleOutput
implements TestcycleConsoleOutputReceiver
{
- private static final Charset STANDARD_CHARSET = defaultCharset();
-
- private final PrintStream sout;
+ private final PrintStream out;
- private final PrintStream serr;
+ private final PrintStream err;
- public DirectConsoleOutput( PrintStream sout, PrintStream serr )
+ public DirectConsoleOutput( PrintStream out, PrintStream err )
{
- this.sout = requireNonNull( sout );
- this.serr = requireNonNull( serr );
+ this.out = requireNonNull( out );
+ this.err = requireNonNull( err );
}
@Override
- public void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+ public void writeTestOutput( String output, boolean newLine, boolean stdout )
{
- PrintStream stream = stdout ? sout : serr;
- //noinspection SynchronizationOnLocalVariableOrMethodParameter
- synchronized ( stream )
+ PrintStream stream = stdout ? out : err;
+ if ( newLine )
+ {
+ stream.println( output );
+ }
+ else
{
- try
- {
- CharBuffer decode = STANDARD_CHARSET.newDecoder().decode( wrap( buf, off, len ) );
- stream.append( decode );
- }
- catch ( CharacterCodingException e )
- {
- stream.write( buf, off, len );
- }
+ stream.print( output );
}
}
@Override
- public void testSetStarting( ReportEntry reportEntry )
+ public void testSetStarting( TestSetReportEntry reportEntry )
{
}
@Override
- public void testSetCompleted( ReportEntry report )
+ public void testSetCompleted( TestSetReportEntry report )
{
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
index 290c252014..2301e34818 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporter.java
@@ -19,16 +19,15 @@
* under the License.
*/
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
import org.apache.maven.surefire.report.ReporterException;
import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
import java.nio.charset.Charset;
import java.util.List;
@@ -42,64 +41,71 @@
* @author Kristian Rosenvold
*/
public class FileReporter
+ extends StatelessTestsetInfoFileReportEventListener
{
- private final File reportsDirectory;
- private final String reportNameSuffix;
- private final Charset encoding;
+ private final boolean usePhrasedFileName;
+ private final boolean usePhrasedClassNameInRunning;
+ private final boolean usePhrasedClassNameInTestCaseSummary;
- public FileReporter( File reportsDirectory, String reportNameSuffix, Charset encoding )
+ public FileReporter( File reportsDirectory, String reportNameSuffix, Charset encoding, boolean usePhrasedFileName,
+ boolean usePhrasedClassNameInRunning, boolean usePhrasedClassNameInTestCaseSummary )
{
- this.reportsDirectory = reportsDirectory;
- this.reportNameSuffix = reportNameSuffix;
- this.encoding = encoding;
+ super( reportsDirectory, reportNameSuffix, encoding );
+ this.usePhrasedFileName = usePhrasedFileName;
+ this.usePhrasedClassNameInRunning = usePhrasedClassNameInRunning;
+ this.usePhrasedClassNameInTestCaseSummary = usePhrasedClassNameInTestCaseSummary;
}
- private PrintWriter testSetStarting( ReportEntry report )
+ static File getReportFile( File reportsDirectory, String reportEntryName, String reportNameSuffix,
+ String fileExtension )
{
- File reportFile = getReportFile( reportsDirectory, report.getName(), reportNameSuffix, ".txt" );
+ String fileName =
+ reportEntryName + ( isNotBlank( reportNameSuffix ) ? "-" + reportNameSuffix : "" ) + fileExtension;
+ return new File( reportsDirectory, stripIllegalFilenameChars( fileName ) );
+ }
+
+ @Override
+ public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List testResults )
+ {
+ File reportFile = getReportFile( getReportsDirectory(),
+ usePhrasedFileName ? report.getReportSourceName() : report.getSourceName(),
+ getReportNameSuffix(),
+ ".txt" );
File reportDir = reportFile.getParentFile();
// noinspection ResultOfMethodCallIgnored
reportDir.mkdirs();
- try
+ try ( BufferedWriter writer = createFileReporterWriter( reportFile, getEncoding() ) )
{
- Writer encodedStream = new OutputStreamWriter( new FileOutputStream( reportFile ), encoding );
-
- PrintWriter writer = new PrintWriter( new BufferedWriter( encodedStream, 16 * 1024 ) );
-
- writer.println( "-------------------------------------------------------------------------------" );
+ writer.write( "-------------------------------------------------------------------------------" );
+ writer.newLine();
- writer.println( "Test set: " + report.getName() );
+ String tesSet = usePhrasedClassNameInRunning ? report.getReportSourceName() : report.getSourceName();
+ writer.write( "Test set: " + tesSet );
+ writer.newLine();
- writer.println( "-------------------------------------------------------------------------------" );
+ writer.write( "-------------------------------------------------------------------------------" );
+ writer.newLine();
- return writer;
+ writer.write( testSetStats.getTestSetSummary( report, usePhrasedClassNameInTestCaseSummary ) );
+ writer.newLine();
+ for ( String testResult : testResults )
+ {
+ writer.write( testResult );
+ writer.newLine();
+ }
}
catch ( IOException e )
{
- throw new ReporterException( "Unable to create file for report: " + e.getMessage(), e );
+ throw new ReporterException( "Unable to create file for report: " + e.getLocalizedMessage(), e );
}
}
- static File getReportFile( File reportsDirectory, String reportEntryName, String reportNameSuffix,
- String fileExtension )
+ private static BufferedWriter createFileReporterWriter( File reportFile, Charset encoding )
+ throws FileNotFoundException
{
- String fileName =
- reportEntryName + ( isNotBlank( reportNameSuffix ) ? "-" + reportNameSuffix : "" ) + fileExtension;
- return new File( reportsDirectory, stripIllegalFilenameChars( fileName ) );
- }
-
- public void testSetCompleted( WrappedReportEntry report, TestSetStats testSetStats, List testResults )
- {
- try ( PrintWriter writer = testSetStarting( report ) )
- {
- writer.println( testSetStats.getTestSetSummary( report ) );
- for ( String testResult : testResults )
- {
- writer.println( testResult );
- }
- }
+ return new BufferedWriter( new OutputStreamWriter( new FileOutputStream( reportFile ), encoding ), 64 * 1024 );
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java
new file mode 100644
index 0000000000..a826b6760f
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleOutputReceiver.java
@@ -0,0 +1,63 @@
+package org.apache.maven.plugin.surefire.report;
+
+/*
+ * 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.report.TestSetReportEntry;
+
+/**
+ * TestcycleConsoleOutputReceiver doing nothing rather than using null.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 3.0.0-M4
+ */
+public class NullConsoleOutputReceiver
+ implements TestcycleConsoleOutputReceiver
+{
+
+ static final NullConsoleOutputReceiver INSTANCE = new NullConsoleOutputReceiver();
+
+ private NullConsoleOutputReceiver()
+ {
+ }
+
+ @Override
+ public void testSetStarting( TestSetReportEntry reportEntry )
+ {
+
+ }
+
+ @Override
+ public void testSetCompleted( TestSetReportEntry report )
+ {
+
+ }
+
+ @Override
+ public void close()
+ {
+
+ }
+
+ @Override
+ public void writeTestOutput( String output, boolean newLine, boolean stdout )
+ {
+
+ }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
index af68d1e01d..f33fca1456 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullConsoleReporter.java
@@ -22,7 +22,7 @@
import java.util.List;
import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
/**
* ConsoleReporter doing nothing rather than using null.
@@ -38,11 +38,11 @@ class NullConsoleReporter
private NullConsoleReporter()
{
- super( new NullConsoleLogger() );
+ super( new NullConsoleLogger(), false, false );
}
@Override
- public void testSetStarting( ReportEntry report )
+ public void testSetStarting( TestSetReportEntry report )
{
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
index df1bf9df9e..be217ebfe8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullFileReporter.java
@@ -35,7 +35,7 @@ class NullFileReporter
private NullFileReporter()
{
- super( null, null, null );
+ super( null, null, null, false, false, false );
}
@Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
index 7f5d202146..d43369dd8f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/NullStatelessXmlReporter.java
@@ -33,7 +33,7 @@ class NullStatelessXmlReporter
private NullStatelessXmlReporter()
{
- super( null, null, false, 0, null, null );
+ super( null, null, false, 0, null, null, null, false, false, false, false );
}
@Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
index b89c4e9b02..fcb066ebbe 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
@@ -22,7 +22,7 @@
import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
import org.apache.maven.shared.utils.xml.PrettyPrintXMLWriter;
import org.apache.maven.shared.utils.xml.XMLWriter;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
import org.apache.maven.surefire.report.ReporterException;
import org.apache.maven.surefire.report.SafeThrowable;
@@ -34,12 +34,13 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentLinkedDeque;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType;
@@ -81,8 +82,9 @@
* @see Ant's format enhancement proposal
* (not yet implemented by Ant 1.8.2)
*/
-@Deprecated // this is no more stateless due to existence of testClassMethodRunHistoryMap since of 2.19. Rename to StatefulXmlReporter in 3.0.
+//todo this is no more stateless due to existence of testClassMethodRunHistoryMap since of 2.19.
public class StatelessXmlReporter
+ implements StatelessReportEventListener
{
private final File reportsDirectory;
@@ -94,14 +96,25 @@ public class StatelessXmlReporter
private final String xsdSchemaLocation;
+ private final String xsdVersion;
+
// Map between test class name and a map between test method name
// and the list of runs for each test method
- private final Map>> testClassMethodRunHistoryMap;
+ private final Map> testClassMethodRunHistoryMap;
+
+ private final boolean phrasedFileName;
+
+ private final boolean phrasedSuiteName;
+
+ private final boolean phrasedClassName;
+
+ private final boolean phrasedMethodName;
public StatelessXmlReporter( File reportsDirectory, String reportNameSuffix, boolean trimStackTrace,
int rerunFailingTestsCount,
- Map>> testClassMethodRunHistoryMap,
- String xsdSchemaLocation )
+ Map> testClassMethodRunHistoryMap,
+ String xsdSchemaLocation, String xsdVersion, boolean phrasedFileName,
+ boolean phrasedSuiteName, boolean phrasedClassName, boolean phrasedMethodName )
{
this.reportsDirectory = reportsDirectory;
this.reportNameSuffix = reportNameSuffix;
@@ -109,19 +122,18 @@ public StatelessXmlReporter( File reportsDirectory, String reportNameSuffix, boo
this.rerunFailingTestsCount = rerunFailingTestsCount;
this.testClassMethodRunHistoryMap = testClassMethodRunHistoryMap;
this.xsdSchemaLocation = xsdSchemaLocation;
+ this.xsdVersion = xsdVersion;
+ this.phrasedFileName = phrasedFileName;
+ this.phrasedSuiteName = phrasedSuiteName;
+ this.phrasedClassName = phrasedClassName;
+ this.phrasedMethodName = phrasedMethodName;
}
+ @Override
public void testSetCompleted( WrappedReportEntry testSetReportEntry, TestSetStats testSetStats )
{
- String testClassName = testSetReportEntry.getName();
-
- Map> methodRunHistoryMap = getAddMethodRunHistoryMap( testClassName );
-
- // Update testClassMethodRunHistoryMap
- for ( WrappedReportEntry methodEntry : testSetStats.getReportEntries() )
- {
- getAddMethodEntryList( methodRunHistoryMap, methodEntry );
- }
+ Map>> classMethodStatistics =
+ arrangeMethodStatistics( testSetReportEntry, testSetStats );
OutputStream outputStream = getOutputStream( testSetReportEntry );
try ( OutputStreamWriter fw = getWriter( outputStream ) )
@@ -129,111 +141,18 @@ public void testSetCompleted( WrappedReportEntry testSetReportEntry, TestSetStat
XMLWriter ppw = new PrettyPrintXMLWriter( fw );
ppw.setEncoding( UTF_8.name() );
- createTestSuiteElement( ppw, testSetReportEntry, testSetStats, testSetReportEntry.elapsedTimeAsString() );
+ createTestSuiteElement( ppw, testSetReportEntry, testSetStats ); // TestSuite
showProperties( ppw, testSetReportEntry.getSystemProperties() );
- // Iterate through all the test methods in the test class
- for ( Entry> entry : methodRunHistoryMap.entrySet() )
+ for ( Entry>> statistics : classMethodStatistics.entrySet() )
{
- List methodEntryList = entry.getValue();
- if ( methodEntryList == null )
+ for ( Entry> thisMethodRuns : statistics.getValue().entrySet() )
{
- throw new IllegalStateException( "Get null test method run history" );
- }
-
- if ( !methodEntryList.isEmpty() )
- {
- if ( rerunFailingTestsCount > 0 )
- {
- switch ( getTestResultType( methodEntryList ) )
- {
- case success:
- for ( WrappedReportEntry methodEntry : methodEntryList )
- {
- if ( methodEntry.getReportEntryType() == SUCCESS )
- {
- startTestElement( ppw, methodEntry, reportNameSuffix,
- methodEntryList.get( 0 ).elapsedTimeAsString() );
- ppw.endElement();
- }
- }
- break;
- case error:
- case failure:
- // When rerunFailingTestsCount is set to larger than 0
- startTestElement( ppw, methodEntryList.get( 0 ), reportNameSuffix,
- methodEntryList.get( 0 ).elapsedTimeAsString() );
- boolean firstRun = true;
- for ( WrappedReportEntry singleRunEntry : methodEntryList )
- {
- if ( firstRun )
- {
- firstRun = false;
- getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
- singleRunEntry.getReportEntryType().getXmlTag(), false );
- createOutErrElements( fw, ppw, singleRunEntry, outputStream );
- }
- else
- {
- getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
- singleRunEntry.getReportEntryType().getRerunXmlTag(), true );
- }
- }
- ppw.endElement();
- break;
- case flake:
- String runtime = "";
- // Get the run time of the first successful run
- for ( WrappedReportEntry singleRunEntry : methodEntryList )
- {
- if ( singleRunEntry.getReportEntryType() == SUCCESS )
- {
- runtime = singleRunEntry.elapsedTimeAsString();
- break;
- }
- }
- startTestElement( ppw, methodEntryList.get( 0 ), reportNameSuffix, runtime );
- for ( WrappedReportEntry singleRunEntry : methodEntryList )
- {
- if ( singleRunEntry.getReportEntryType() != SUCCESS )
- {
- getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
- singleRunEntry.getReportEntryType().getFlakyXmlTag(), true );
- }
- }
- ppw.endElement();
-
- break;
- case skipped:
- startTestElement( ppw, methodEntryList.get( 0 ), reportNameSuffix,
- methodEntryList.get( 0 ).elapsedTimeAsString() );
- getTestProblems( fw, ppw, methodEntryList.get( 0 ), trimStackTrace, outputStream,
- methodEntryList.get( 0 ).getReportEntryType().getXmlTag(), false );
- ppw.endElement();
- break;
- default:
- throw new IllegalStateException( "Get unknown test result type" );
- }
- }
- else
- {
- // rerunFailingTestsCount is smaller than 1, but for some reasons a test could be run
- // for more than once
- for ( WrappedReportEntry methodEntry : methodEntryList )
- {
- startTestElement( ppw, methodEntry, reportNameSuffix, methodEntry.elapsedTimeAsString() );
- if ( methodEntry.getReportEntryType() != SUCCESS )
- {
- getTestProblems( fw, ppw, methodEntry, trimStackTrace, outputStream,
- methodEntry.getReportEntryType().getXmlTag(), false );
- createOutErrElements( fw, ppw, methodEntry, outputStream );
- }
- ppw.endElement();
- }
- }
+ serializeTestClass( outputStream, fw, ppw, thisMethodRuns.getValue() );
}
}
+
ppw.endElement(); // TestSuite
}
catch ( Exception e )
@@ -246,6 +165,143 @@ public void testSetCompleted( WrappedReportEntry testSetReportEntry, TestSetStat
}
}
+ private Map>> arrangeMethodStatistics(
+ WrappedReportEntry testSetReportEntry, TestSetStats testSetStats )
+ {
+ Map>> classMethodStatistics = new LinkedHashMap<>();
+ for ( WrappedReportEntry methodEntry : aggregateCacheFromMultipleReruns( testSetReportEntry, testSetStats ) )
+ {
+ String testClassName = methodEntry.getSourceName();
+ Map> stats = classMethodStatistics.get( testClassName );
+ if ( stats == null )
+ {
+ stats = new LinkedHashMap<>();
+ classMethodStatistics.put( testClassName, stats );
+ }
+ String methodName = methodEntry.getName();
+ List methodRuns = stats.get( methodName );
+ if ( methodRuns == null )
+ {
+ methodRuns = new ArrayList<>();
+ stats.put( methodName, methodRuns );
+ }
+ methodRuns.add( methodEntry );
+ }
+ return classMethodStatistics;
+ }
+
+ private Deque aggregateCacheFromMultipleReruns( WrappedReportEntry testSetReportEntry,
+ TestSetStats testSetStats )
+ {
+ String suiteClassName = testSetReportEntry.getSourceName();
+ Deque methodRunHistory = getAddMethodRunHistoryMap( suiteClassName );
+ methodRunHistory.addAll( testSetStats.getReportEntries() );
+ return methodRunHistory;
+ }
+
+ private void serializeTestClass( OutputStream outputStream, OutputStreamWriter fw, XMLWriter ppw,
+ List methodEntries )
+ {
+ if ( rerunFailingTestsCount > 0 )
+ {
+ serializeTestClassWithRerun( outputStream, fw, ppw, methodEntries );
+ }
+ else
+ {
+ // rerunFailingTestsCount is smaller than 1, but for some reasons a test could be run
+ // for more than once
+ serializeTestClassWithoutRerun( outputStream, fw, ppw, methodEntries );
+ }
+ }
+
+ private void serializeTestClassWithoutRerun( OutputStream outputStream, OutputStreamWriter fw, XMLWriter ppw,
+ List methodEntries )
+ {
+ for ( WrappedReportEntry methodEntry : methodEntries )
+ {
+ startTestElement( ppw, methodEntry );
+ if ( methodEntry.getReportEntryType() != SUCCESS )
+ {
+ getTestProblems( fw, ppw, methodEntry, trimStackTrace, outputStream,
+ methodEntry.getReportEntryType().getXmlTag(), false );
+ createOutErrElements( fw, ppw, methodEntry, outputStream );
+ }
+ ppw.endElement();
+ }
+ }
+
+ private void serializeTestClassWithRerun( OutputStream outputStream, OutputStreamWriter fw, XMLWriter ppw,
+ List methodEntries )
+ {
+ WrappedReportEntry firstMethodEntry = methodEntries.get( 0 );
+ switch ( getTestResultType( methodEntries ) )
+ {
+ case success:
+ for ( WrappedReportEntry methodEntry : methodEntries )
+ {
+ if ( methodEntry.getReportEntryType() == SUCCESS )
+ {
+ startTestElement( ppw, methodEntry );
+ ppw.endElement();
+ }
+ }
+ break;
+ case error:
+ case failure:
+ // When rerunFailingTestsCount is set to larger than 0
+ startTestElement( ppw, firstMethodEntry );
+ boolean firstRun = true;
+ for ( WrappedReportEntry singleRunEntry : methodEntries )
+ {
+ if ( firstRun )
+ {
+ firstRun = false;
+ getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
+ singleRunEntry.getReportEntryType().getXmlTag(), false );
+ createOutErrElements( fw, ppw, singleRunEntry, outputStream );
+ }
+ else
+ {
+ getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
+ singleRunEntry.getReportEntryType().getRerunXmlTag(), true );
+ }
+ }
+ ppw.endElement();
+ break;
+ case flake:
+ WrappedReportEntry successful = null;
+ // Get the run time of the first successful run
+ for ( WrappedReportEntry singleRunEntry : methodEntries )
+ {
+ if ( singleRunEntry.getReportEntryType() == SUCCESS )
+ {
+ successful = singleRunEntry;
+ break;
+ }
+ }
+ WrappedReportEntry firstOrSuccessful = successful == null ? methodEntries.get( 0 ) : successful;
+ startTestElement( ppw, firstOrSuccessful );
+ for ( WrappedReportEntry singleRunEntry : methodEntries )
+ {
+ if ( singleRunEntry.getReportEntryType() != SUCCESS )
+ {
+ getTestProblems( fw, ppw, singleRunEntry, trimStackTrace, outputStream,
+ singleRunEntry.getReportEntryType().getFlakyXmlTag(), true );
+ }
+ }
+ ppw.endElement();
+ break;
+ case skipped:
+ startTestElement( ppw, firstMethodEntry );
+ getTestProblems( fw, ppw, firstMethodEntry, trimStackTrace, outputStream,
+ firstMethodEntry.getReportEntryType().getXmlTag(), false );
+ ppw.endElement();
+ break;
+ default:
+ throw new IllegalStateException( "Get unknown test result type" );
+ }
+ }
+
/**
* Clean testClassMethodRunHistoryMap
*/
@@ -271,20 +327,20 @@ private TestResultType getTestResultType( List methodEntryLi
return DefaultReporterFactory.getTestResultType( testResultTypeList, rerunFailingTestsCount );
}
- private Map> getAddMethodRunHistoryMap( String testClassName )
+ private Deque getAddMethodRunHistoryMap( String testClassName )
{
- Map> methodRunHistoryMap = testClassMethodRunHistoryMap.get( testClassName );
- if ( methodRunHistoryMap == null )
+ Deque methodRunHistory = testClassMethodRunHistoryMap.get( testClassName );
+ if ( methodRunHistory == null )
{
- methodRunHistoryMap = Collections.synchronizedMap( new LinkedHashMap>() );
- testClassMethodRunHistoryMap.put( testClassName, methodRunHistoryMap );
+ methodRunHistory = new ConcurrentLinkedDeque<>();
+ testClassMethodRunHistoryMap.put( testClassName == null ? "null" : testClassName, methodRunHistory );
}
- return methodRunHistoryMap;
+ return methodRunHistory;
}
private OutputStream getOutputStream( WrappedReportEntry testSetReportEntry )
{
- File reportFile = getReportFile( testSetReportEntry, reportsDirectory, reportNameSuffix );
+ File reportFile = getReportFile( testSetReportEntry );
File reportDir = reportFile.getParentFile();
@@ -293,7 +349,7 @@ private OutputStream getOutputStream( WrappedReportEntry testSetReportEntry )
try
{
- return new BufferedOutputStream( new FileOutputStream( reportFile ), 16 * 1024 );
+ return new BufferedOutputStream( new FileOutputStream( reportFile ), 64 * 1024 );
}
catch ( Exception e )
{
@@ -306,65 +362,52 @@ private static OutputStreamWriter getWriter( OutputStream fos )
return new OutputStreamWriter( fos, UTF_8 );
}
- private static void getAddMethodEntryList( Map> methodRunHistoryMap,
- WrappedReportEntry methodEntry )
+ private File getReportFile( WrappedReportEntry report )
{
- List methodEntryList = methodRunHistoryMap.get( methodEntry.getName() );
- if ( methodEntryList == null )
- {
- methodEntryList = new ArrayList<>();
- methodRunHistoryMap.put( methodEntry.getName(), methodEntryList );
- }
- methodEntryList.add( methodEntry );
- }
-
- private static File getReportFile( ReportEntry report, File reportsDirectory, String reportNameSuffix )
- {
- String reportName = "TEST-" + report.getName();
+ String reportName = "TEST-" + ( phrasedFileName ? report.getReportSourceName() : report.getSourceName() );
String customizedReportName = isBlank( reportNameSuffix ) ? reportName : reportName + "-" + reportNameSuffix;
return new File( reportsDirectory, stripIllegalFilenameChars( customizedReportName + ".xml" ) );
}
- private static void startTestElement( XMLWriter ppw, WrappedReportEntry report, String reportNameSuffix,
- String timeAsString )
+ private void startTestElement( XMLWriter ppw, WrappedReportEntry report )
{
ppw.startElement( "testcase" );
- ppw.addAttribute( "name", report.getReportName() );
+ String name = phrasedMethodName ? report.getReportName() : report.getName();
+ ppw.addAttribute( "name", name == null ? "" : extraEscapeAttribute( name ) );
+
if ( report.getGroup() != null )
{
ppw.addAttribute( "group", report.getGroup() );
}
- if ( report.getSourceName() != null )
+
+ String className = phrasedClassName ? report.getReportSourceName( reportNameSuffix )
+ : report.getSourceName( reportNameSuffix );
+ if ( className != null )
{
- if ( reportNameSuffix != null && !reportNameSuffix.isEmpty() )
- {
- ppw.addAttribute( "classname", report.getSourceName() + "(" + reportNameSuffix + ")" );
- }
- else
- {
- ppw.addAttribute( "classname", report.getSourceName() );
- }
+ ppw.addAttribute( "classname", extraEscapeAttribute( className ) );
}
- ppw.addAttribute( "time", timeAsString );
+
+ ppw.addAttribute( "time", report.elapsedTimeAsString() );
}
- private void createTestSuiteElement( XMLWriter ppw, WrappedReportEntry report, TestSetStats testSetStats,
- String timeAsString )
+ private void createTestSuiteElement( XMLWriter ppw, WrappedReportEntry report, TestSetStats testSetStats )
{
ppw.startElement( "testsuite" );
ppw.addAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
ppw.addAttribute( "xsi:noNamespaceSchemaLocation", xsdSchemaLocation );
- ppw.addAttribute( "version", "3.0" );
+ ppw.addAttribute( "version", xsdVersion );
- ppw.addAttribute( "name", report.getReportName( reportNameSuffix ) );
+ String reportName = phrasedSuiteName ? report.getReportSourceName( reportNameSuffix )
+ : report.getSourceName( reportNameSuffix );
+ ppw.addAttribute( "name", reportName == null ? "" : extraEscapeAttribute( reportName ) );
if ( report.getGroup() != null )
{
ppw.addAttribute( "group", report.getGroup() );
}
- ppw.addAttribute( "time", timeAsString );
+ ppw.addAttribute( "time", report.elapsedTimeAsString() );
ppw.addAttribute( "tests", String.valueOf( testSetStats.getCompletedCount() ) );
@@ -385,7 +428,7 @@ private static void getTestProblems( OutputStreamWriter outputStreamWriter, XMLW
if ( report.getMessage() != null && !report.getMessage().isEmpty() )
{
- ppw.addAttribute( "message", extraEscape( report.getMessage(), true ) );
+ ppw.addAttribute( "message", extraEscapeAttribute( report.getMessage() ) );
}
if ( report.getStackTraceWriter() != null )
@@ -416,7 +459,7 @@ private static void getTestProblems( OutputStreamWriter outputStreamWriter, XMLW
ppw.startElement( "stackTrace" );
}
- ppw.writeText( extraEscape( stackTrace, false ) );
+ extraEscapeElementValue( stackTrace, outputStreamWriter, ppw, fw );
if ( hasNestedElements )
{
@@ -491,7 +534,7 @@ private static void showProperties( XMLWriter xmlWriter, Map sys
xmlWriter.addAttribute( "name", key );
- xmlWriter.addAttribute( "value", extraEscape( value, true ) );
+ xmlWriter.addAttribute( "value", extraEscapeAttribute( value ) );
xmlWriter.endElement();
}
@@ -499,16 +542,47 @@ private static void showProperties( XMLWriter xmlWriter, Map sys
}
/**
- * Handle stuff that may pop up in java that is not legal in xml
+ * Handle stuff that may pop up in java that is not legal in xml.
+ *
+ * @param message The string
+ * @return The escaped string or returns itself if all characters are legal
+ */
+ private static String extraEscapeAttribute( String message )
+ {
+ // Someday convert to xml 1.1 which handles everything but 0 inside string
+ return containsEscapesIllegalXml10( message ) ? escapeXml( message, true ) : message;
+ }
+
+ /**
+ * Writes escaped string or the message within CDATA if all characters are legal.
*
* @param message The string
- * @param attribute true if the escaped value is inside an attribute
- * @return The escaped string
*/
- private static String extraEscape( String message, boolean attribute )
+ private static void extraEscapeElementValue( String message, OutputStreamWriter outputStreamWriter,
+ XMLWriter xmlWriter, OutputStream fw )
{
// Someday convert to xml 1.1 which handles everything but 0 inside string
- return containsEscapesIllegalXml10( message ) ? escapeXml( message, attribute ) : message;
+ if ( containsEscapesIllegalXml10( message ) )
+ {
+ xmlWriter.writeText( escapeXml( message, false ) );
+ }
+ else
+ {
+ try
+ {
+ EncodingOutputStream eos = new EncodingOutputStream( fw );
+ xmlWriter.writeText( "" ); // Cheat sax to emit element
+ outputStreamWriter.flush();
+ eos.getUnderlying().write( ByteConstantsHolder.CDATA_START_BYTES );
+ eos.write( message.getBytes( UTF_8 ) );
+ eos.getUnderlying().write( ByteConstantsHolder.CDATA_END_BYTES );
+ eos.flush();
+ }
+ catch ( IOException e )
+ {
+ throw new ReporterException( "When writing xml element", e );
+ }
+ }
}
private static final class EncodingOutputStream
@@ -585,6 +659,13 @@ private static boolean isIllegalEscape( int c )
return c >= 0 && c < 32 && c != '\n' && c != '\r' && c != '\t';
}
+ /**
+ * escape for XML 1.0
+ *
+ * @param text The string
+ * @param attribute true if the escaped value is inside an attribute
+ * @return The escaped string
+ */
private static String escapeXml( String text, boolean attribute )
{
StringBuilder sb = new StringBuilder( text.length() * 2 );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index 995d687348..2adbac4b5b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -27,15 +27,22 @@
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
+import org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
import org.apache.maven.surefire.report.ConsoleOutputReceiver;
import org.apache.maven.surefire.report.ReportEntry;
import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.RunMode;
import org.apache.maven.surefire.report.TestSetReportEntry;
import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
import static org.apache.maven.plugin.surefire.report.ReportEntryType.SKIPPED;
import static org.apache.maven.plugin.surefire.report.ReportEntryType.SUCCESS;
+import static org.apache.maven.surefire.report.RunMode.NORMAL_RUN;
+import static java.util.Objects.requireNonNull;
/**
* Reports data for a single test set.
@@ -50,15 +57,15 @@ public class TestSetRunListener
private final TestSetStats detailsForThis;
- private final TestcycleConsoleOutputReceiver consoleOutputReceiver;
+ private final ConsoleOutputReportEventListener consoleOutputReceiver;
private final boolean briefOrPlainFormat;
- private final StatelessXmlReporter simpleXMLReporter;
+ private final StatelessReportEventListener simpleXMLReporter;
- private final ConsoleReporter consoleReporter;
+ private final StatelessTestsetInfoConsoleReportEventListener consoleReporter;
- private final FileReporter fileReporter;
+ private final StatelessTestsetInfoFileReportEventListener fileReporter;
private final StatisticsReporter statisticsReporter;
@@ -66,10 +73,15 @@ public class TestSetRunListener
private Utf8RecodingDeferredFileOutputStream testStdErr = initDeferred( "stderr" );
+ private volatile RunMode runMode = NORMAL_RUN;
+
@SuppressWarnings( "checkstyle:parameternumber" )
- public TestSetRunListener( ConsoleReporter consoleReporter, FileReporter fileReporter,
- StatelessXmlReporter simpleXMLReporter,
- TestcycleConsoleOutputReceiver consoleOutputReceiver,
+ public TestSetRunListener( StatelessTestsetInfoConsoleReportEventListener
+ consoleReporter,
+ StatelessTestsetInfoFileReportEventListener
+ fileReporter,
+ StatelessReportEventListener simpleXMLReporter,
+ ConsoleOutputReportEventListener consoleOutputReceiver,
StatisticsReporter statisticsReporter, boolean trimStackTrace,
boolean isPlainFormat, boolean briefOrPlainFormat )
{
@@ -133,7 +145,7 @@ public void error( String message )
@Override
public void error( String message, Throwable t )
{
- consoleReporter.getConsoleLogger().error( message, t );
+ consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ), t );
}
@Override
@@ -143,13 +155,13 @@ public void error( Throwable t )
}
@Override
- public synchronized void writeTestOutput( byte[] buf, int off, int len, boolean stdout )
+ public void writeTestOutput( String output, boolean newLine, boolean stdout )
{
try
{
- Utf8RecodingDeferredFileOutputStream os = stdout ? testStdOut : testStdErr;
- os.write( buf, off, len );
- consoleOutputReceiver.writeTestOutput( buf, off, len, stdout );
+ Utf8RecodingDeferredFileOutputStream stream = stdout ? testStdOut : testStdErr;
+ stream.write( output, newLine );
+ consoleOutputReceiver.writeTestOutput( output, newLine, stdout );
}
catch ( IOException e )
{
@@ -247,6 +259,13 @@ public void testExecutionSkippedByUser()
{
}
+ public RunMode markAs( RunMode currentRunMode )
+ {
+ RunMode runMode = this.runMode;
+ this.runMode = requireNonNull( currentRunMode );
+ return runMode;
+ }
+
@Override
public void testAssumptionFailure( ReportEntry report )
{
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
index 8698159ba1..3e360c1e96 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetStats.java
@@ -29,6 +29,7 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.surefire.report.CategorizedReportEntry.GROUP_PREFIX;
/**
* Maintains per-thread test result state. Not thread safe.
@@ -169,7 +170,7 @@ private void incrementCompletedCount()
completedCount += 1;
}
- public String getTestSetSummary( WrappedReportEntry reportEntry )
+ public String getTestSetSummary( WrappedReportEntry reportEntry, boolean phrasedClassName )
{
String summary = TESTS_RUN + completedCount
+ COMMA
@@ -186,12 +187,13 @@ public String getTestSetSummary( WrappedReportEntry reportEntry )
summary += FAILURE_MARKER;
}
- summary += IN_MARKER + reportEntry.getNameWithGroup();
+ summary += IN_MARKER;
+ summary += phrasedClassName ? reportEntry.getReportNameWithGroup() : reportEntry.getNameWithGroup();
return summary;
}
- public String getColoredTestSetSummary( WrappedReportEntry reportEntry )
+ public String getColoredTestSetSummary( WrappedReportEntry reportEntry, boolean phrasedClassName )
{
final boolean isSuccessful = failures == 0 && errors == 0 && skipped == 0;
final boolean isFailure = failures > 0;
@@ -255,7 +257,7 @@ public String getColoredTestSetSummary( WrappedReportEntry reportEntry )
builder.failure( FAILURE_MARKER );
}
builder.a( IN_MARKER );
- return concatenateWithTestGroup( builder, reportEntry );
+ return concatenateWithTestGroup( builder, reportEntry, phrasedClassName );
}
public List getTestResults()
@@ -269,7 +271,7 @@ public List getTestResults()
}
else if ( plainFormat && testResult.isSkipped() )
{
- result.add( testResult.getName() + " skipped" );
+ result.add( testResult.getSourceName() + " skipped" );
}
else if ( plainFormat && testResult.isSucceeded() )
{
@@ -287,20 +289,29 @@ public Collection getReportEntries()
/**
* Append the test set message for a report.
- * e.g. "org.foo.BarTest ( of group )"
+ * e.g. "org.foo.BarTest ( of group )" or phrased text "test class description ( of group )".
*
* @param builder MessageBuilder with preceded text inside
* @param report report whose test set is starting
* @return the message
*/
- static String concatenateWithTestGroup( MessageBuilder builder, ReportEntry report )
+ static String concatenateWithTestGroup( MessageBuilder builder, ReportEntry report, boolean phrasedClassName )
{
- final String testClass = report.getNameWithGroup();
- int delimiter = testClass.lastIndexOf( '.' );
- String pkg = testClass.substring( 0, 1 + delimiter );
- String cls = testClass.substring( 1 + delimiter );
- return builder.a( pkg )
- .strong( cls )
- .toString();
+ if ( phrasedClassName )
+ {
+ return builder.strong( report.getReportNameWithGroup() )
+ .toString();
+ }
+ else
+ {
+ String testClass = report.getNameWithGroup();
+ int indexOfGroup = testClass.indexOf( GROUP_PREFIX );
+ int delimiter = testClass.lastIndexOf( '.', indexOfGroup == -1 ? testClass.length() : indexOfGroup );
+ String pkg = testClass.substring( 0, 1 + delimiter );
+ String cls = testClass.substring( 1 + delimiter );
+ return builder.a( pkg )
+ .strong( cls )
+ .toString();
+ }
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
index f1b5b5a397..cdd4b3a4a0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestcycleConsoleOutputReceiver.java
@@ -19,18 +19,19 @@
* under the License.
*/
+import org.apache.maven.surefire.extensions.ConsoleOutputReportEventListener;
import org.apache.maven.surefire.report.ConsoleOutputReceiver;
-import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.TestSetReportEntry;
/**
* @author Kristian Rosenvold
*/
public interface TestcycleConsoleOutputReceiver
- extends ConsoleOutputReceiver
+ extends ConsoleOutputReceiver, ConsoleOutputReportEventListener
{
- void testSetStarting( ReportEntry reportEntry );
+ void testSetStarting( TestSetReportEntry reportEntry );
- void testSetCompleted( ReportEntry report );
+ void testSetCompleted( TestSetReportEntry report );
void close();
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/Utf8RecodingDeferredFileOutputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/Utf8RecodingDeferredFileOutputStream.java
index 8b496cdf2a..c3eda011d9 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/Utf8RecodingDeferredFileOutputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/Utf8RecodingDeferredFileOutputStream.java
@@ -23,11 +23,9 @@
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.maven.surefire.util.internal.StringUtils.NL;
/**
* A deferred file output stream decorator that recodes the bytes written into the stream from the VM default encoding
@@ -44,10 +42,10 @@ class Utf8RecodingDeferredFileOutputStream
@SuppressWarnings( "checkstyle:magicnumber" )
Utf8RecodingDeferredFileOutputStream( String channel )
{
- this.deferredFileOutputStream = new DeferredFileOutputStream( 1000000, channel, "deferred", null );
+ deferredFileOutputStream = new DeferredFileOutputStream( 1000000, channel, "deferred", null );
}
- public synchronized void write( byte[] buf, int off, int len )
+ public synchronized void write( String output, boolean newLine )
throws IOException
{
if ( closed )
@@ -55,28 +53,15 @@ public synchronized void write( byte[] buf, int off, int len )
return;
}
- if ( !Charset.defaultCharset().equals( UTF_8 ) )
+ if ( output == null )
{
- CharBuffer decodedFromDefaultCharset = Charset.defaultCharset().decode( ByteBuffer.wrap( buf, off, len ) );
- ByteBuffer utf8Encoded = UTF_8.encode( decodedFromDefaultCharset );
-
- if ( utf8Encoded.hasArray() )
- {
- byte[] convertedBytes = utf8Encoded.array();
-
- deferredFileOutputStream.write( convertedBytes, utf8Encoded.position(), utf8Encoded.remaining() );
- }
- else
- {
- byte[] convertedBytes = new byte[utf8Encoded.remaining()];
- utf8Encoded.get( convertedBytes, 0, utf8Encoded.remaining() );
-
- deferredFileOutputStream.write( convertedBytes, 0, convertedBytes.length );
- }
+ output = "null";
}
- else
+
+ deferredFileOutputStream.write( output.getBytes( UTF_8 ) );
+ if ( newLine )
{
- deferredFileOutputStream.write( buf, off, len );
+ deferredFileOutputStream.write( NL.getBytes( UTF_8 ) );
}
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
index 3426e3a252..102eea44a3 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
@@ -29,6 +29,7 @@
import static java.util.Collections.unmodifiableMap;
import static org.apache.maven.plugin.surefire.report.ReporterUtils.formatElapsedTime;
import static org.apache.maven.surefire.util.internal.StringUtils.NL;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
/**
* @author Kristian Rosenvold
@@ -74,6 +75,12 @@ public Integer getElapsed()
return elapsed;
}
+ @Override
+ public int getElapsed( int fallback )
+ {
+ return elapsed == null ? fallback : elapsed;
+ }
+
public ReportEntryType getReportEntryType()
{
return reportEntryType;
@@ -95,15 +102,27 @@ public String getSourceName()
return original.getSourceName();
}
+ @Override
+ public String getSourceText()
+ {
+ return original.getSourceText();
+ }
+
@Override
public String getName()
{
return original.getName();
}
+ @Override
+ public String getNameText()
+ {
+ return original.getNameText();
+ }
+
public String getClassMethodName()
{
- return getSourceName() + "." + getName();
+ return original.getSourceName() + "." + original.getName();
}
@Override
@@ -135,15 +154,28 @@ public String elapsedTimeAsString()
return formatElapsedTime( getElapsed() );
}
- public String getReportName()
+ String getReportSourceName()
+ {
+ String sourceName = getSourceName();
+ String sourceText = getSourceText();
+ return isBlank( sourceText ) ? sourceName : sourceText;
+ }
+
+ String getReportSourceName( String suffix )
{
- final int i = getName().lastIndexOf( "(" );
- return i > 0 ? getName().substring( 0, i ) : getName();
+ return isBlank( suffix ) ? getReportSourceName() : getReportSourceName() + "(" + suffix + ")";
}
- public String getReportName( String suffix )
+ String getSourceName( String suffix )
{
- return suffix != null && !suffix.isEmpty() ? getReportName() + "(" + suffix + ")" : getReportName();
+ return isBlank( suffix ) ? getSourceName() : getSourceName() + "(" + suffix + ")";
+ }
+
+ String getReportName()
+ {
+ String name = getName();
+ String nameText = getNameText();
+ return isBlank( nameText ) ? name : nameText;
}
public String getOutput( boolean trimStackTrace )
@@ -160,7 +192,8 @@ public String getElapsedTimeVerbose()
public String getElapsedTimeSummary()
{
- return getName() + " " + getElapsedTimeVerbose();
+ String description = getName() == null ? getSourceName() : getClassMethodName();
+ return description + " " + getElapsedTimeVerbose();
}
public boolean isErrorOrFailure()
@@ -185,6 +218,12 @@ public String getNameWithGroup()
return original.getNameWithGroup();
}
+ @Override
+ public String getReportNameWithGroup()
+ {
+ return original.getReportNameWithGroup();
+ }
+
@Override
public Map getSystemProperties()
{
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
index a53db0241d..fb5450b31f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
@@ -20,7 +20,8 @@
*/
import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.IOException;
+
import org.apache.maven.surefire.report.ReportEntry;
import static org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMap.fromFile;
@@ -41,11 +42,11 @@ public StatisticsReporter( File dataFile )
this( dataFile, fromFile( dataFile ), new RunEntryStatisticsMap() );
}
- protected StatisticsReporter( File dataFile, RunEntryStatisticsMap existing, RunEntryStatisticsMap newRestuls )
+ protected StatisticsReporter( File dataFile, RunEntryStatisticsMap existing, RunEntryStatisticsMap newResults )
{
this.dataFile = dataFile;
this.existing = existing;
- this.newResults = newRestuls;
+ this.newResults = newResults;
}
public synchronized void testSetCompleted()
@@ -54,7 +55,7 @@ public synchronized void testSetCompleted()
{
newResults.serialize( dataFile );
}
- catch ( FileNotFoundException e )
+ catch ( IOException e )
{
throw new RuntimeException( e );
}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
index 34e8e7fcc8..7ddcfbe00f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
@@ -19,12 +19,17 @@
* under the License.
*/
-import static org.apache.maven.plugin.surefire.util.ScannerUtil.convertJarFileResourceToJavaClassName;
-import static org.apache.maven.plugin.surefire.util.ScannerUtil.isJavaClassFile;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
+import org.apache.maven.surefire.testset.TestFilter;
+import org.apache.maven.surefire.testset.TestListResolver;
+import org.apache.maven.surefire.util.DefaultScanResult;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
@@ -32,11 +37,8 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.surefire.testset.TestFilter;
-import org.apache.maven.surefire.testset.TestListResolver;
-import org.apache.maven.surefire.util.DefaultScanResult;
+import static org.apache.maven.plugin.surefire.util.ScannerUtil.convertJarFileResourceToJavaClassName;
+import static org.apache.maven.plugin.surefire.util.ScannerUtil.isJavaClassFile;
/**
* Scans dependencies looking for tests.
@@ -93,28 +95,29 @@ private static void scanArtifact( File artifact, TestFilter filt
}
}
- public static List filter( List artifacts, List groupArtifactIds )
+ /**
+ *
+ * @param artifacts a list to filter
+ * @param artifactPatterns a list of strings in the form
+ *