From efff9ff7ea46a57682a54802fa127237c1842ba8 Mon Sep 17 00:00:00 2001 From: agudian Date: Tue, 6 Nov 2012 21:54:00 +0100 Subject: [PATCH 1/2] forkMode=onceperthread, working for JUnit providers - TestNG support sill needs some work. Requires maven-shared-utils version 0.2 (or latest snapshot of it). Don't use the extra thread for reading the next test class support of forkMode=onceperthread in TestNG provider Adapt and add integration tests for forkMode=onceperthread fix gathering test results with forkMode=onceperthread (fixes faild ITs) --- .../plugin/surefire/AbstractSurefireMojo.java | 45 ++++- .../booterclient/BooterSerializer.java | 8 +- .../booterclient/ForkConfiguration.java | 25 ++- .../surefire/booterclient/ForkStarter.java | 172 +++++++++++++++--- .../lazytestprovider/FlushReceiver.java | 7 + .../FlushReceiverProvider.java | 5 + .../ProcessAwareCommandline.java | 41 +++++ .../TestProvidingInputStream.java | 52 ++++++ .../booterclient/output/ForkClient.java | 17 ++ ...DeserializerProviderConfigurationTest.java | 59 ++++-- ...rDeserializerStartupConfigurationTest.java | 10 +- .../booterclient/ForkConfigurationTest.java | 4 +- pom.xml | 2 +- .../surefire/booter/ForkingRunListener.java | 3 + .../maven/surefire/util/LazyTestsToRun.java | 135 ++++++++++++++ .../surefire/booter/BooterConstants.java | 1 + .../surefire/booter/BooterDeserializer.java | 5 +- .../maven/surefire/booter/ForkedBooter.java | 15 +- .../booter/ProviderConfiguration.java | 12 +- .../apache/maven/surefire/its/ForkModeIT.java | 24 ++- .../its/fixture/SurefireLauncher.java | 10 + ...efire907PerThreadWithoutThreadCountIT.java | 3 +- .../test/resources/fork-mode-testng/pom.xml | 1 + .../src/test/java/forkMode/Test1.java | 5 + .../src/test/resources/fork-mode/pom.xml | 1 + .../src/test/java/forkMode/Test1.java | 7 +- .../maven/surefire/junit/JUnit3Provider.java | 12 +- .../maven/surefire/junit4/JUnit4Provider.java | 18 +- .../surefire/junitcore/JUnitCoreProvider.java | 17 +- .../surefire/junitcore/JUnitCoreWrapper.java | 23 ++- .../testng/TestNGDirectoryTestSuite.java | 110 ++++++----- .../maven/surefire/testng/TestNGProvider.java | 12 +- 32 files changed, 722 insertions(+), 139 deletions(-) create mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java create mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java create mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java create mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java create mode 100644 surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index 7d14557e5f..3214e2f928 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -29,6 +29,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.maven.artifact.Artifact; @@ -327,8 +328,11 @@ public abstract class AbstractSurefireMojo protected Boolean failIfNoTests; /** - * Option to specify the forking mode. Can be "never", "once", "always" or "perthread". "none" and "pertest" are also accepted - * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks. + * Option to specify the forking mode. Can be "never", "once", "always", "perthread" or "onceperthread". "none" and "pertest" are also accepted + * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks, each executing one test-class. + * "onceperthread" will fork "threadCount" processes that each execute a 1/"threadCount" of all test-classes.
+ * The system properties and the "argLine" of the forked processes may contain the place holder string ${surefire.threadNumber}, + * which is replaced with a fixed number for each thread, ranging from 1 to "threadCount". * * @since 2.1 */ @@ -429,9 +433,9 @@ public abstract class AbstractSurefireMojo protected String testNGArtifactName; /** - * (forkMode=perthread or TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be - * allocated for this execution. Only makes sense to use in conjunction with the parallel parameter. (forkMode=perthread - * does not support/require the parallel parameter) + * (forkMode=perthread, forkmode=onceperthread or TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be + * allocated for this execution. Only makes sense to use in conjunction with the parallel parameter or with forkMode=perthread + * or forkmode=onceperthread. * * @since 2.2 */ @@ -572,6 +576,12 @@ public abstract class AbstractSurefireMojo private Artifact surefireBooterArtifact; private Toolchain toolchain; + + /** + * The placeholder that is replaced by the executing thread's running number. The thread number + * range starts with 1 + */ + public static final String THREAD_NUMBER_PLACEHOLDER = "${surefire.threadNumber}"; protected abstract String getPluginName(); @@ -741,7 +751,8 @@ private RunResult executeProvider( ProviderInfo provider, DefaultScanResult scan final RunResult result; if ( isForkModeNever() ) { - effectiveProperties.copyToSystemProperties(); + createCopyAndReplaceThreadNumPlaceholder(effectiveProperties, 1).copyToSystemProperties(); + InPluginVMSurefireStarter surefireStarter = createInprocessStarter( provider, classLoaderConfiguration, runOrderParameters ); result = surefireStarter.runSuitesInProcess( scanResult ); @@ -771,6 +782,18 @@ private RunResult executeProvider( ProviderInfo provider, DefaultScanResult scan return result; } + + public static SurefireProperties createCopyAndReplaceThreadNumPlaceholder(SurefireProperties effectiveSystemProperties, int threadNumber) { + SurefireProperties filteredProperties = new SurefireProperties(effectiveSystemProperties); + String threadNumberString = String.valueOf(threadNumber); + for (Entry entry : effectiveSystemProperties.entrySet()) { + if (entry.getValue() instanceof String) { + filteredProperties.put(entry.getKey(), ((String)entry.getValue()).replace(THREAD_NUMBER_PLACEHOLDER, threadNumberString)); + } + } + return filteredProperties; + } + protected void cleanupForkConfiguration( ForkConfiguration forkConfiguration ) { if ( !getLog().isDebugEnabled() && forkConfiguration != null ) @@ -1002,7 +1025,7 @@ private ProviderConfiguration createProviderConfiguration( RunOrderParameters ru return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, failIfNoTests, reporterConfiguration, testNg, testSuiteDefinition, providerProperties, - null ); + null, false ); } public String getStatisticsFileName( String configurationHash ) @@ -1344,7 +1367,8 @@ protected ForkConfiguration getForkConfiguration() private int getEffectiveForkCount() { - return ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) ) ? getThreadCount() : 1; + return ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) || + ForkConfiguration.FORK_ONCE_PERTHREAD.equals( getEffectiveForkMode() ) ) ? getThreadCount() : 1; } private String getEffectiveDebugForkedProcess() @@ -1728,9 +1752,10 @@ void ensureParallelRunningCompatibility() void ensureThreadCountWithPerThread() throws MojoFailureException { - if ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) && getThreadCount() < 1 ) + if ( ( ForkConfiguration.FORK_PERTHREAD.equals( getEffectiveForkMode() ) || + ForkConfiguration.FORK_ONCE_PERTHREAD.equals( getEffectiveForkMode() )) && getThreadCount() < 1 ) { - throw new MojoFailureException( "Fork mode perthread requires a thread count" ); + throw new MojoFailureException( "Fork modes perthread and onceperthread require a thread count" ); } } 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 c4f2c1658d..5b1dcbb273 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 @@ -21,7 +21,9 @@ import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Properties; + import org.apache.maven.surefire.booter.BooterConstants; import org.apache.maven.surefire.booter.ClassLoaderConfiguration; import org.apache.maven.surefire.booter.KeyValueSource; @@ -66,7 +68,7 @@ public BooterSerializer( ForkConfiguration forkConfiguration ) DOes not modify sourceProperties */ public File serialize(KeyValueSource sourceProperties, ProviderConfiguration booterConfiguration, StartupConfiguration providerConfiguration, - Object testSet) + Object testSet, boolean readTestsFromInStream) throws IOException { @@ -82,7 +84,9 @@ public File serialize(KeyValueSource sourceProperties, ProviderConfiguration boo properties.setProperty( BooterConstants.TESTARTIFACT_CLASSIFIER, testNg.getClassifier() ); } - properties.setProperty( BooterConstants.FORKTESTSET, getTypeEncoded( testSet ) ); + properties.setProperty( BooterConstants.FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM, Boolean.valueOf( readTestsFromInStream ) ); + properties.setProperty( BooterConstants.FORKTESTSET, getTypeEncoded( testSet ) ); + TestRequest testSuiteDefinition = booterConfiguration.getTestSuiteDefinition(); if ( testSuiteDefinition != null ) { 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 451af0268e..fef053a248 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 @@ -27,6 +27,9 @@ import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; + +import org.apache.maven.plugin.surefire.AbstractSurefireMojo; +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline; import org.apache.maven.plugin.surefire.util.Relocator; import org.apache.maven.shared.utils.StringUtils; import org.apache.maven.shared.utils.cli.Commandline; @@ -52,6 +55,8 @@ public class ForkConfiguration public static final String FORK_NEVER = "never"; public static final String FORK_PERTHREAD = "perthread"; + + public static final String FORK_ONCE_PERTHREAD = "onceperthread"; private final int forkCount; @@ -102,7 +107,8 @@ else if ( "none".equalsIgnoreCase( forkMode ) ) return FORK_NEVER; } else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) || - forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) ) + forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) || + forkMode.equals( FORK_ONCE_PERTHREAD )) { return forkMode; } @@ -117,27 +123,28 @@ else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) || * @param classPath cla the classpath arguments * @param classpathConfiguration the classpath configuration * @param shadefire true if running shadefire + * @param threadNumber the thread number, to be the replacement in the argLine * @return A commandline * @throws org.apache.maven.surefire.booter.SurefireBooterForkException * when unable to perform the fork */ - public Commandline createCommandLine( List classPath, ClassLoaderConfiguration classpathConfiguration, - boolean shadefire ) + public ProcessAwareCommandline createCommandLine( List classPath, ClassLoaderConfiguration classpathConfiguration, + boolean shadefire, int threadNumber ) throws SurefireBooterForkException { - return createCommandLine( classPath, classpathConfiguration.isManifestOnlyJarRequestedAndUsable(), shadefire ); + return createCommandLine( classPath, classpathConfiguration.isManifestOnlyJarRequestedAndUsable(), shadefire, threadNumber ); } - public Commandline createCommandLine( List classPath, boolean useJar, boolean shadefire ) + public ProcessAwareCommandline createCommandLine( List classPath, boolean useJar, boolean shadefire, int threadNumber ) throws SurefireBooterForkException { - Commandline cli = new Commandline(); + ProcessAwareCommandline cli = new ProcessAwareCommandline(); cli.setExecutable( jvmExecutable ); if ( argLine != null ) { - cli.createArg().setLine( stripNewLines( argLine ) ); + cli.createArg().setLine( replaceThreadNumberPlaceholder(stripNewLines( argLine ), threadNumber) ); } if ( environmentVariables != null ) @@ -186,6 +193,10 @@ public Commandline createCommandLine( List classPath, boolean useJar, bo return cli; } + private String replaceThreadNumberPlaceholder(String argLine, int threadNumber) { + return argLine.replace(AbstractSurefireMojo.THREAD_NUMBER_PLACEHOLDER, String.valueOf(threadNumber)); + } + /** * Create a jar with just a manifest containing a Main-Class entry for BooterConfiguration and a Class-Path entry * for all classpath elements. 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 7a7b4bc5d5..6c135b3861 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 @@ -23,24 +23,31 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Properties; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.maven.plugin.surefire.AbstractSurefireMojo; import org.apache.maven.plugin.surefire.CommonReflector; import org.apache.maven.plugin.surefire.StartupReportConfiguration; import org.apache.maven.plugin.surefire.SurefireProperties; +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline; +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream; import org.apache.maven.plugin.surefire.booterclient.output.ForkClient; import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer; import org.apache.maven.plugin.surefire.report.DefaultReporterFactory; import org.apache.maven.shared.utils.cli.CommandLineException; import org.apache.maven.shared.utils.cli.CommandLineTimeOutException; import org.apache.maven.shared.utils.cli.CommandLineUtils; -import org.apache.maven.shared.utils.cli.Commandline; import org.apache.maven.surefire.booter.Classpath; import org.apache.maven.surefire.booter.ClasspathConfiguration; import org.apache.maven.surefire.booter.KeyValueSource; @@ -52,9 +59,9 @@ import org.apache.maven.surefire.booter.SurefireExecutionException; import org.apache.maven.surefire.booter.SystemPropertyManager; import org.apache.maven.surefire.providerapi.SurefireProvider; -import org.apache.maven.surefire.report.RunStatistics; import org.apache.maven.surefire.suite.RunResult; import org.apache.maven.surefire.util.DefaultScanResult; +import org.codehaus.plexus.util.CollectionUtils; /** @@ -87,6 +94,17 @@ public class ForkStarter private static volatile int systemPropertiesFileCounter = 0; + private final ThreadLocal threadNumber = new ThreadLocal() + { + private final AtomicInteger nextThreadNumber = new AtomicInteger( 1 ); + + @Override + protected Integer initialValue() + { + return nextThreadNumber.getAndIncrement(); + } + }; + public ForkStarter( ProviderConfiguration providerConfiguration, StartupConfiguration startupConfiguration, ForkConfiguration forkConfiguration, int forkedProcessTimeoutInSeconds, @@ -113,17 +131,21 @@ public RunResult run( SurefireProperties effectiveSystemProperties, DefaultScanR { final ForkClient forkClient = new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() ); - result = fork( null, new PropertiesWrapper( providerProperties ), forkClient, - fileReporterFactory.getGlobalRunStatistics(), effectiveSystemProperties ); + result = + fork( null, new PropertiesWrapper( providerProperties ), forkClient, effectiveSystemProperties, 1, + null ); } else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) ) { - result = runSuitesForkPerTestSet( providerProperties, effectiveSystemProperties, 1 ); + result = runSuitesForkPerTestSet( effectiveSystemProperties, 1 ); } else if ( ForkConfiguration.FORK_PERTHREAD.equals( requestedForkMode ) ) { - result = runSuitesForkPerTestSet( providerProperties, effectiveSystemProperties, - forkConfiguration.getForkCount() ); + result = runSuitesForkPerTestSet( effectiveSystemProperties, forkConfiguration.getForkCount() ); + } + else if ( ForkConfiguration.FORK_ONCE_PERTHREAD.equals( requestedForkMode ) ) + { + result = runSuitesForkOncePerThread( effectiveSystemProperties, forkConfiguration.getForkCount() ); } else { @@ -137,8 +159,85 @@ else if ( ForkConfiguration.FORK_PERTHREAD.equals( requestedForkMode ) ) return result; } - private RunResult runSuitesForkPerTestSet( final Properties properties, - final SurefireProperties effectiveSystemProperties, int forkCount ) + private RunResult runSuitesForkOncePerThread( final SurefireProperties effectiveSystemProperties, int forkCount ) + throws SurefireBooterForkException + { + + ArrayList> results = new ArrayList>( forkCount ); + ExecutorService executorService = new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue( forkCount ) ); + + try + { + // Ask to the executorService to run all tasks + RunResult globalResult = new RunResult( 0, 0, 0, 0 ); + + List> suites = CollectionUtils.iteratorToList( getSuitesIterator() ); + final Queue messageQueue = new ConcurrentLinkedQueue(); + for ( Class clazz : suites ) + { + messageQueue.add( clazz.getName() ); + } + + for ( int threadNum = 0; threadNum < forkCount && threadNum < suites.size(); threadNum++ ) + { + final int finalThreadNumber = threadNum + 1; + + Callable pf = new Callable() + { + public RunResult call() + throws Exception + { + TestProvidingInputStream testProvidingInputStream = + new TestProvidingInputStream( messageQueue ); + + ForkClient forkClient = + new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties(), + testProvidingInputStream ); + + return fork( null, new PropertiesWrapper( providerConfiguration.getProviderProperties() ), + forkClient, effectiveSystemProperties, finalThreadNumber, + testProvidingInputStream ); + } + }; + + results.add( executorService.submit( pf ) ); + } + + for ( Future result : results ) + { + try + { + RunResult cur = result.get(); + if ( cur != null ) + { + globalResult = globalResult.aggregate( cur ); + } + else + { + throw new SurefireBooterForkException( "No results for " + result.toString() ); + } + } + catch ( InterruptedException e ) + { + throw new SurefireBooterForkException( "Interrupted", e ); + } + catch ( ExecutionException e ) + { + throw new SurefireBooterForkException( "ExecutionException", e ); + } + } + return globalResult; + + } + finally + { + closeExecutor( executorService ); + } + + } + + private RunResult runSuitesForkPerTestSet( final SurefireProperties effectiveSystemProperties, final int forkCount ) throws SurefireBooterForkException { @@ -150,19 +249,27 @@ private RunResult runSuitesForkPerTestSet( final Properties properties, { // Ask to the executorService to run all tasks RunResult globalResult = new RunResult( 0, 0, 0, 0 ); - final Iterator suites = getSuitesIterator(); + final Iterator> suites = getSuitesIterator(); while ( suites.hasNext() ) { final Object testSet = suites.next(); - final ForkClient forkClient = - new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() ); Callable pf = new Callable() { public RunResult call() throws Exception { - return fork( testSet, new PropertiesWrapper( properties ), forkClient, - fileReporterFactory.getGlobalRunStatistics(), effectiveSystemProperties ); + int thisThreadsThreadNumber = threadNumber.get(); + if ( thisThreadsThreadNumber > forkCount ) + { + // this would be a bug in the ThreadPoolExecutor + throw new IllegalStateException( + "More threads than " + forkCount + " have been created by the ThreadPoolExecutor." ); + } + + ForkClient forkClient = new ForkClient( fileReporterFactory, + startupReportConfiguration.getTestVmSystemProperties() ); + return fork( testSet, new PropertiesWrapper( providerConfiguration.getProviderProperties() ), + forkClient, effectiveSystemProperties, thisThreadsThreadNumber, null ); } }; results.add( executorService.submit( pf ) ); @@ -217,9 +324,9 @@ private void closeExecutor( ExecutorService executorService ) } } - private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkClient forkClient, - RunStatistics globalRunStatistics, SurefireProperties effectiveSystemProperties ) + SurefireProperties effectiveSystemProperties, int threadNumber, + TestProvidingInputStream testProvidingInputStream ) throws SurefireBooterForkException { File surefireProperties; @@ -229,14 +336,18 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration ); surefireProperties = - booterSerializer.serialize( providerProperties, providerConfiguration, startupConfiguration, testSet ); + booterSerializer.serialize( providerProperties, providerConfiguration, startupConfiguration, testSet, + null != testProvidingInputStream ); if ( effectiveSystemProperties != null ) { - systPropsFile = SystemPropertyManager.writePropertiesFile( effectiveSystemProperties, - forkConfiguration.getTempDirectory(), - "surefire_" + systemPropertiesFileCounter++, - forkConfiguration.isDebug() ); + SurefireProperties filteredProperties = + AbstractSurefireMojo.createCopyAndReplaceThreadNumPlaceholder( effectiveSystemProperties, + threadNumber ); + systPropsFile = + SystemPropertyManager.writePropertiesFile( filteredProperties, forkConfiguration.getTempDirectory(), + "surefire_" + systemPropertiesFileCounter++, + forkConfiguration.isDebug() ); } } catch ( IOException e ) @@ -254,10 +365,15 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC // Surefire-booter if !useSystemClassLoader Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls ); - @SuppressWarnings( "unchecked" ) Commandline cli = + @SuppressWarnings( "unchecked" ) ProcessAwareCommandline cli = forkConfiguration.createCommandLine( bootClasspath.getClassPath(), startupConfiguration.getClassLoaderConfiguration(), - startupConfiguration.isShadefire() ); + startupConfiguration.isShadefire(), threadNumber ); + + if ( testProvidingInputStream != null ) + { + testProvidingInputStream.setFlushReceiverProvider( cli ); + } cli.createArg().setFile( surefireProperties ); @@ -279,7 +395,8 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC { final int timeout = forkedProcessTimeoutInSeconds > 0 ? forkedProcessTimeoutInSeconds : 0; final int result = - CommandLineUtils.executeCommandLine( cli, threadedStreamConsumer, threadedStreamConsumer, timeout ); + CommandLineUtils.executeCommandLine( cli, testProvidingInputStream, threadedStreamConsumer, + threadedStreamConsumer, timeout ); if ( result != RunResult.SUCCESS ) { throw new SurefireBooterForkException( "Error occurred in starting fork, check output in log" ); @@ -299,17 +416,18 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC finally { threadedStreamConsumer.close(); - forkClient.close(runResult == RunResult.Timeout); + forkClient.close( runResult == RunResult.Timeout ); if ( runResult == null ) { - runResult = globalRunStatistics.getRunResult(); + runResult = fileReporterFactory.getGlobalRunStatistics().getRunResult(); } } return runResult; } - private Iterator getSuitesIterator() + @SuppressWarnings( "unchecked" ) + private Iterator> getSuitesIterator() throws SurefireBooterForkException { try diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java new file mode 100644 index 0000000000..5269d7aa5e --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java @@ -0,0 +1,7 @@ +package org.apache.maven.plugin.surefire.booterclient.lazytestprovider; + +import java.io.IOException; + +public interface FlushReceiver { + void flush() throws IOException; +} diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java new file mode 100644 index 0000000000..f285a666c2 --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java @@ -0,0 +1,5 @@ +package org.apache.maven.plugin.surefire.booterclient.lazytestprovider; + +public interface FlushReceiverProvider { + FlushReceiver getFlushReceiver(); +} \ No newline at end of file diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java new file mode 100644 index 0000000000..5182107ebb --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java @@ -0,0 +1,41 @@ +package org.apache.maven.plugin.surefire.booterclient.lazytestprovider; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.Commandline; + + + +public class ProcessAwareCommandline extends Commandline implements FlushReceiverProvider { + private final class OutputStreamFlushReceiver implements FlushReceiver { + private final OutputStream outputStream; + + private OutputStreamFlushReceiver(OutputStream outputStream) { + this.outputStream = outputStream; + } + + public void flush() throws IOException { + outputStream.flush(); + } + } + + private FlushReceiver flushReceiver; + + @Override + public Process execute() throws CommandLineException { + Process process = super.execute(); + + if (process.getOutputStream() != null) { + flushReceiver = new OutputStreamFlushReceiver(process.getOutputStream()); + } + + return process; + } + + 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/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java new file mode 100644 index 0000000000..a4e899d686 --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java @@ -0,0 +1,52 @@ +package org.apache.maven.plugin.surefire.booterclient.lazytestprovider; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Queue; +import java.util.concurrent.Semaphore; + +public class TestProvidingInputStream extends InputStream { + private final Queue testItemQueue; + private byte[] currentBuffer; + private int currentPos; + private Semaphore semaphore = new Semaphore(0); + private FlushReceiverProvider flushReceiverProvider; + + public TestProvidingInputStream(Queue testItemQueue) { + this.testItemQueue = testItemQueue; + } + + public void setFlushReceiverProvider(FlushReceiverProvider flushReceiverProvider) { + this.flushReceiverProvider = flushReceiverProvider; + } + + @Override + public synchronized int read() throws IOException { + if (null == currentBuffer) { + if (null != flushReceiverProvider && null != flushReceiverProvider.getFlushReceiver()) { + flushReceiverProvider.getFlushReceiver().flush(); + } + + semaphore.acquireUninterruptibly(); + + String currentElement = testItemQueue.poll(); + if (null != currentElement) { + currentBuffer = currentElement.getBytes(); + currentPos = 0; + } else { + return -1; + } + } + + if (currentPos < currentBuffer.length) { + return (currentBuffer[currentPos++] & 0xff); + } else { + currentBuffer = null; + return ('\n' & 0xff); + } + } + + public void provideNewTest() { + semaphore.release(); + } +} \ 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 75599544c7..fa7d1412fc 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 @@ -28,6 +28,8 @@ import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; + +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream; import org.apache.maven.plugin.surefire.report.DefaultReporterFactory; import org.apache.maven.shared.utils.cli.StreamConsumer; import org.apache.maven.surefire.booter.ForkingRunListener; @@ -36,6 +38,7 @@ import org.apache.maven.surefire.report.ConsoleOutputReceiver; import org.apache.maven.surefire.report.ReportEntry; import org.apache.maven.surefire.report.ReporterException; +import org.apache.maven.surefire.report.ReporterFactory; import org.apache.maven.surefire.report.RunListener; import org.apache.maven.surefire.report.StackTraceWriter; import org.apache.maven.surefire.util.NestedRuntimeException; @@ -49,7 +52,9 @@ public class ForkClient implements StreamConsumer { + private final DefaultReporterFactory providerReporterFactory; + private final TestProvidingInputStream testProvidingInputStream; private final Map testSetReporters = Collections.synchronizedMap( new HashMap() ); @@ -59,9 +64,15 @@ public class ForkClient private volatile boolean saidGoodBye = false; public ForkClient( DefaultReporterFactory providerReporterFactory, Properties testVmSystemProperties ) + { + this(providerReporterFactory, testVmSystemProperties, null); + } + + public ForkClient( DefaultReporterFactory providerReporterFactory, Properties testVmSystemProperties, TestProvidingInputStream testProvidingInputStream ) { this.providerReporterFactory = providerReporterFactory; this.testVmSystemProperties = testVmSystemProperties; + this.testProvidingInputStream = testProvidingInputStream; } public void consumeLine( String s ) @@ -134,6 +145,12 @@ public void consumeLine( String s ) case ForkingRunListener.BOOTERCODE_CONSOLE: getOrCreateConsoleLogger( channelNumber ).info( createConsoleMessage( remaining ) ); break; + case ForkingRunListener.BOOTERCODE_NEXT_TEST: + if (null != testProvidingInputStream) + { + testProvidingInputStream.provideNewTest(); + } + break; case ForkingRunListener.BOOTERCODE_BYE: saidGoodBye = true; break; diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java index b7e1ebaf28..509929d10a 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java @@ -27,13 +27,16 @@ import java.util.Collections; import java.util.List; import java.util.Properties; + +import junit.framework.Assert; +import junit.framework.TestCase; + import org.apache.maven.surefire.booter.BooterDeserializer; import org.apache.maven.surefire.booter.ClassLoaderConfiguration; import org.apache.maven.surefire.booter.ClasspathConfiguration; import org.apache.maven.surefire.booter.PropertiesWrapper; import org.apache.maven.surefire.booter.ProviderConfiguration; import org.apache.maven.surefire.booter.StartupConfiguration; -import org.apache.maven.surefire.booter.SystemPropertyManager; import org.apache.maven.surefire.booter.TypeEncodedValue; import org.apache.maven.surefire.report.ReporterConfiguration; import org.apache.maven.surefire.testset.DirectoryScannerParameters; @@ -42,9 +45,6 @@ import org.apache.maven.surefire.testset.TestRequest; import org.apache.maven.surefire.util.RunOrder; -import junit.framework.Assert; -import junit.framework.TestCase; - /** * Performs roundtrip testing of serialization/deserialization of the ProviderConfiguration * @@ -55,7 +55,7 @@ public class BooterDeserializerProviderConfigurationTest { public static final TypeEncodedValue aTestTyped = new TypeEncodedValue( String.class.getName(), "aTest" ); - + private final String aUserRequestedTest = "aUserRequestedTest"; private static ClassLoaderConfiguration getForkConfiguration() @@ -79,7 +79,7 @@ public void testDirectoryScannerParams() ClassLoaderConfiguration forkConfiguration = getForkConfiguration(); final StartupConfiguration testStartupConfiguration = getTestStartupConfiguration( forkConfiguration ); ProviderConfiguration providerConfiguration = getReloadedProviderConfiguration(); - ProviderConfiguration read = saveAndReload( providerConfiguration, testStartupConfiguration ); + ProviderConfiguration read = saveAndReload( providerConfiguration, testStartupConfiguration, false ); Assert.assertEquals( aDir, read.getBaseDir() ); Assert.assertEquals( includes.get( 0 ), read.getIncludes().get( 0 ) ); @@ -95,10 +95,10 @@ public void testReporterConfiguration() DirectoryScannerParameters directoryScannerParameters = getDirectoryScannerParametersWithoutSpecificTests(); ClassLoaderConfiguration forkConfiguration = getForkConfiguration(); - ProviderConfiguration providerConfiguration = getTestProviderConfiguration( directoryScannerParameters ); + ProviderConfiguration providerConfiguration = getTestProviderConfiguration( directoryScannerParameters, false ); final StartupConfiguration testProviderConfiguration = getTestStartupConfiguration( forkConfiguration ); - ProviderConfiguration reloaded = saveAndReload( providerConfiguration, testProviderConfiguration ); + ProviderConfiguration reloaded = saveAndReload( providerConfiguration, testProviderConfiguration, false ); assertTrue( reloaded.getReporterConfiguration().isTrimStackTrace().booleanValue() ); assertNotNull( reloaded.getReporterConfiguration().getReportsDirectory() ); @@ -134,6 +134,15 @@ public void testTestForFork() Assert.assertEquals( aTestTyped, reloaded.getTestForFork() ); } + + public void testTestForForkWithMultipleFiles() + throws IOException + { + final ProviderConfiguration reloaded = getReloadedProviderConfigurationForReadFromInStream(); + Assert.assertNull( reloaded.getTestForFork() ); + Assert.assertTrue( reloaded.isReadTestsFromInStream() ); + + } public void testFailIfNoTests() throws IOException @@ -143,14 +152,26 @@ public void testFailIfNoTests() } + private ProviderConfiguration getReloadedProviderConfigurationForReadFromInStream() + throws IOException + { + return getReloadedProviderConfiguration(true); + } + private ProviderConfiguration getReloadedProviderConfiguration() + throws IOException + { + return getReloadedProviderConfiguration(false); + } + + private ProviderConfiguration getReloadedProviderConfiguration(boolean readTestsFromInStream) throws IOException { DirectoryScannerParameters directoryScannerParameters = getDirectoryScannerParametersWithoutSpecificTests(); ClassLoaderConfiguration forkConfiguration = getForkConfiguration(); - ProviderConfiguration booterConfiguration = getTestProviderConfiguration( directoryScannerParameters ); + ProviderConfiguration booterConfiguration = getTestProviderConfiguration( directoryScannerParameters, readTestsFromInStream ); final StartupConfiguration testProviderConfiguration = getTestStartupConfiguration( forkConfiguration ); - return saveAndReload( booterConfiguration, testProviderConfiguration ); + return saveAndReload( booterConfiguration, testProviderConfiguration, readTestsFromInStream ); } private DirectoryScannerParameters getDirectoryScannerParametersWithoutSpecificTests() @@ -168,19 +189,27 @@ private DirectoryScannerParameters getDirectoryScannerParametersWithoutSpecificT } private ProviderConfiguration saveAndReload( ProviderConfiguration booterConfiguration, - StartupConfiguration testProviderConfiguration ) + StartupConfiguration testProviderConfiguration, boolean readTestsFromInStream ) throws IOException { final ForkConfiguration forkConfiguration = ForkConfigurationTest.getForkConfiguration( null, null ); PropertiesWrapper props = new PropertiesWrapper( new Properties()); BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration ); - String aTest = "aTest"; - final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, aTest); + Object test; + if (readTestsFromInStream) + { + test = null; + } + else + { + test = "aTest"; + } + final File propsTest = booterSerializer.serialize( props, booterConfiguration, testProviderConfiguration, test, readTestsFromInStream); BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) ); return booterDeserializer.deserialize(); } - private ProviderConfiguration getTestProviderConfiguration( DirectoryScannerParameters directoryScannerParameters ) + private ProviderConfiguration getTestProviderConfiguration( DirectoryScannerParameters directoryScannerParameters, boolean readTestsFromInStream ) { File cwd = new File( "." ); @@ -192,7 +221,7 @@ private ProviderConfiguration getTestProviderConfiguration( DirectoryScannerPara RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null ); return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration, new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(), - aTestTyped ); + aTestTyped, readTestsFromInStream ); } private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration ) diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java index bc9085a5f5..d72621d413 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java @@ -25,6 +25,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Properties; + +import junit.framework.TestCase; + import org.apache.maven.surefire.booter.BooterDeserializer; import org.apache.maven.surefire.booter.ClassLoaderConfiguration; import org.apache.maven.surefire.booter.Classpath; @@ -32,7 +35,6 @@ import org.apache.maven.surefire.booter.PropertiesWrapper; import org.apache.maven.surefire.booter.ProviderConfiguration; import org.apache.maven.surefire.booter.StartupConfiguration; -import org.apache.maven.surefire.booter.SystemPropertyManager; import org.apache.maven.surefire.report.ReporterConfiguration; import org.apache.maven.surefire.testset.DirectoryScannerParameters; import org.apache.maven.surefire.testset.RunOrderParameters; @@ -40,8 +42,6 @@ import org.apache.maven.surefire.testset.TestRequest; import org.apache.maven.surefire.util.RunOrder; -import junit.framework.TestCase; - /** * Performs roundtrip testing of serialization/deserialization of The StartupConfiguration * @@ -130,7 +130,7 @@ private StartupConfiguration saveAndReload( StartupConfiguration startupConfigur PropertiesWrapper props = new PropertiesWrapper( new Properties()); BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration ); String aTest = "aTest"; - final File propsTest = booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest); + final File propsTest = booterSerializer.serialize( props, getProviderConfiguration(), startupConfiguration, aTest, false ); BooterDeserializer booterDeserializer = new BooterDeserializer( new FileInputStream( propsTest ) ); return booterDeserializer.getProviderConfiguration(); } @@ -152,7 +152,7 @@ private ProviderConfiguration getProviderConfiguration() RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null ); return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration, new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(), - BooterDeserializerProviderConfigurationTest.aTestTyped ); + BooterDeserializerProviderConfigurationTest.aTestTyped, true ); } private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration ) diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java index ce44f23451..6ec931ec2e 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java @@ -41,7 +41,7 @@ public void testCreateCommandLine_UseSystemClassLoaderForkOnce_ShouldConstructMa File cpElement = getTempClasspathFile(); Commandline cli = - config.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), true, false ); + config.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), true, false, 1 ); String line = StringUtils.join( cli.getCommandline(), " " ); assertTrue( line.contains( "-jar" ) ); @@ -56,7 +56,7 @@ public void testArglineWithNewline() final Commandline commandLine = forkConfiguration.createCommandLine( Collections.singletonList( cpElement.getAbsolutePath() ), false, - false ); + false, 1 ); assertTrue( commandLine.toString().contains( "abc def" ) ); } diff --git a/pom.xml b/pom.xml index 11776bae58..2eb2c3c5e9 100644 --- a/pom.xml +++ b/pom.xml @@ -222,7 +222,7 @@ org.apache.maven.shared maven-shared-utils - 0.1 + 0.2-SNAPSHOT jmock diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java index b3283e3fa2..e5c8ac3c92 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java @@ -22,6 +22,7 @@ import java.io.PrintStream; import java.util.Enumeration; import java.util.Properties; + import org.apache.maven.surefire.report.ConsoleLogger; import org.apache.maven.surefire.report.ConsoleOutputReceiver; import org.apache.maven.surefire.report.ReportEntry; @@ -75,6 +76,8 @@ public class ForkingRunListener public static final byte BOOTERCODE_SYSPROPS = (byte) 'I'; + public static final byte BOOTERCODE_NEXT_TEST = (byte) 'N'; + public static final byte BOOTERCODE_BYE = (byte) 'Z'; private final PrintStream target; diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java new file mode 100644 index 0000000000..dabe1ae744 --- /dev/null +++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java @@ -0,0 +1,135 @@ +/** + * + */ +package org.apache.maven.surefire.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.maven.surefire.booter.ForkingRunListener; + +/** + * A variant of TestsToRun that is provided with test class names asynchronously + * from an {@link InputStream} (e.g. {@code System.in}). The method + * {@link #iterator()} returns an Iterator that blocks on calls to + * {@link Iterator#hasNext()} until new classes are available, or no more + * classes will be available. + *

+ * The methods {@link #getLocatedClasses()} and {@link #size()} will throw an + * {@link UnsupportedOperationException}. + * + * @author Andreas Gudian + * + */ +public class LazyTestsToRun extends TestsToRun { + private List workQueue = new ArrayList(); + private BufferedReader inputReader; + private boolean streamClosed = false; + private ClassLoader testClassLoader; + private PrintStream originalOutStream; + + public LazyTestsToRun(InputStream testSource, ClassLoader testClassLoader, PrintStream originalOutStream) { + super(Collections.emptyList()); + + this.testClassLoader = testClassLoader; + this.originalOutStream = originalOutStream; + + inputReader = new BufferedReader(new InputStreamReader(testSource)); + } + + protected void addWorkItem(String className) { + synchronized (workQueue) { + workQueue.add(ReflectionUtils.loadClass(testClassLoader, className)); + } + } + + protected void requestNextTest() { + StringBuffer sb = new StringBuffer(); + sb.append((char) ForkingRunListener.BOOTERCODE_NEXT_TEST).append(",0,want more!\n"); + originalOutStream.print(sb.toString()); + } + + private class BlockingIterator implements Iterator { + private int lastPos = -1; + + public boolean hasNext() { + int nextPos = lastPos + 1; + synchronized (workQueue) { + if (workQueue.size() > nextPos) { + return true; + } else { + if (needsToWaitForInput(nextPos)) { + requestNextTest(); + + String nextClassName; + try { + nextClassName = inputReader.readLine(); + } catch (IOException e) { + streamClosed = true; + return false; + } + + if (null == nextClassName) { + streamClosed = true; + } else { + addWorkItem(nextClassName); + } + } + + return (workQueue.size() > nextPos); + } + } + } + + private boolean needsToWaitForInput(int nextPos) { + return workQueue.size() == nextPos && !streamClosed; + } + + public Object next() { + synchronized (workQueue) { + return workQueue.get(++lastPos); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + public Iterator iterator() { + return new BlockingIterator(); + } + + /** + * Unsupported. Use {@link #iterator()} instead. + */ + public int size() { + throw new UnsupportedOperationException("use method iterator()"); + } + + /** + * Unsupported. Use {@link #iterator()} instead. + */ + public Class[] getLocatedClasses() { + throw new UnsupportedOperationException("use method iterator()"); + } + + public String toString() { + StringBuffer sb = new StringBuffer("LazyTestsToRun "); + synchronized (workQueue) { + sb.append("(more items expected: ").append(!streamClosed).append("): "); + sb.append(workQueue); + } + + return sb.toString(); + } + +} diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java index 375bf68e0d..430ea9c384 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java @@ -46,4 +46,5 @@ public interface BooterConstants String TEST_SUITE_XML_FILES = "testSuiteXmlFiles"; String PROVIDER_CONFIGURATION = "providerConfiguration"; String FORKTESTSET = "forkTestSet"; + String FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM = "preferTestsFromInStream"; } diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java index af03c1c151..85e4c93f32 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java @@ -61,7 +61,10 @@ public ProviderConfiguration deserialize() final File reportsDirectory = new File( properties.getProperty( REPORTSDIRECTORY ) ); final String testNgVersion = properties.getProperty( TESTARTIFACT_VERSION ); final String testArtifactClassifier = properties.getProperty( TESTARTIFACT_CLASSIFIER ); + final TypeEncodedValue typeEncodedTestForFork = properties.getTypeEncodedValue( FORKTESTSET ); + final boolean preferTestsFromInStream = properties.getBooleanProperty( FORKTESTSET_PREFER_TESTS_FROM_IN_STREAM ); + final String requestedTest = properties.getProperty( REQUESTEDTEST ); final String requestedTestMethod = properties.getProperty( REQUESTEDTESTMETHOD ); final File sourceDirectory = properties.getFileProperty( SOURCE_DIRECTORY ); @@ -90,7 +93,7 @@ public ProviderConfiguration deserialize() return new ProviderConfiguration( dirScannerParams, runOrderParameters, properties.getBooleanProperty( FAILIFNOTESTS ), reporterConfiguration, testNg, - testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork ); + testSuiteDefinition, properties.getProperties(), typeEncodedTestForFork, preferTestsFromInStream ); } public StartupConfiguration getProviderConfiguration() diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java index 938755cb60..2e8f8124f0 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java @@ -23,7 +23,9 @@ import java.io.FileInputStream; import java.io.InputStream; import java.io.PrintStream; + import org.apache.maven.surefire.suite.RunResult; +import org.apache.maven.surefire.util.LazyTestsToRun; /** * The part of the booter that is unique to a forked vm. @@ -64,6 +66,7 @@ public static void main( String[] args ) final StartupConfiguration startupConfiguration = booterDeserializer.getProviderConfiguration(); TypeEncodedValue forkedTestSet = providerConfiguration.getTestForFork(); + boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream(); final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration(); final ClassLoader testClassLoader = classpathConfiguration.createForkingTestClassLoader( @@ -71,7 +74,17 @@ public static void main( String[] args ) startupConfiguration.writeSurefireTestClasspathProperty(); - Object testSet = forkedTestSet != null ? forkedTestSet.getDecodedValue( testClassLoader ) : null; + Object testSet; + if (forkedTestSet != null) { + testSet = forkedTestSet.getDecodedValue( testClassLoader ); + } + else if (readTestsFromInputStream) { + testSet = new LazyTestsToRun(System.in, testClassLoader, originalOut); + } + else { + testSet = null; + } + runSuitesInProcess( testSet, testClassLoader, startupConfiguration, providerConfiguration, originalOut ); // Say bye. originalOut.println( "Z,0,BYE!" ); diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java index bc1dc7f088..23535ad8ac 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java @@ -64,12 +64,14 @@ public class ProviderConfiguration private final boolean failIfNoTests; private final TypeEncodedValue forkTestSet; + + private final boolean readTestsFromInStream; public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters, RunOrderParameters runOrderParameters, boolean failIfNoTests, ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact, TestRequest testSuiteDefinition, Properties providerProperties, - TypeEncodedValue typeEncodedTestSet ) + TypeEncodedValue typeEncodedTestSet, boolean readTestsFromInStream) { this.runOrderParameters = runOrderParameters; this.providerProperties = providerProperties; @@ -79,6 +81,7 @@ public ProviderConfiguration( DirectoryScannerParameters directoryScannerParamet this.dirScannerParams = directoryScannerParameters; this.failIfNoTests = failIfNoTests; this.forkTestSet = typeEncodedTestSet; + this.readTestsFromInStream = readTestsFromInStream; } @@ -133,9 +136,14 @@ public TypeEncodedValue getTestForFork() { return forkTestSet; } - + public RunOrderParameters getRunOrderParameters() { return runOrderParameters; } + + + public boolean isReadTestsFromInStream() { + return readTestsFromInStream; + } } diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java index 158ad85a35..8653106ddd 100644 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/ForkModeIT.java @@ -19,6 +19,10 @@ * under the License. */ +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import org.apache.maven.surefire.its.fixture.OutputValidator; import org.apache.maven.surefire.its.fixture.SurefireIntegrationTestCase; import org.apache.maven.surefire.its.fixture.SurefireLauncher; @@ -55,8 +59,25 @@ public void testForkModeNone() String[] pids = doTest( unpack( getProject() ).forkMode( "none" ) ); assertSamePids( pids ); } + + public void testForkModeOncePerThreadSingleThread() + { + String[] pids = doTest( unpack( getProject() ).forkOncePerThread().threadCount(1) ); + assertSamePids( pids ); + } + + public void testForkModeOncePerThreadTwoThreads() + { + String[] pids = doTest( unpack( getProject() ).forkOncePerThread().threadCount(2) ); + assertDifferentPids( pids, 2 ); + } + + private void assertDifferentPids(String[] pids, int numOfDifferentPids) { + Set pidSet = new HashSet(Arrays.asList(pids)); + assertEquals( "number of different pids is not as expected", numOfDifferentPids, pidSet.size() ); + } - public void testForkModeOnce() + public void testForkModeOnce() { String[] pids = doTest( unpack( getProject() ).forkOnce() ); // DGF It would be nice to assert that "once" was different @@ -92,6 +113,7 @@ private void assertDifferentPids( String[] pids ) private String[] doTest( SurefireLauncher forkMode ) { + forkMode.addD( "testProperty", "testValue_${surefire.threadNumber}" ); final OutputValidator outputValidator = forkMode.executeTest(); outputValidator.verifyErrorFreeLog().assertTestSuiteResults( 3, 0, 0, 0 ); String[] pids = new String[3]; diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java index d6fc2e8b93..552e8b4ec0 100755 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java @@ -384,6 +384,16 @@ public SurefireLauncher forkPerThread() return forkMode( "perthread" ); } + public SurefireLauncher forkOncePerThread() + { + return forkMode( "onceperthread" ); + } + + public SurefireLauncher threadCount(int threadCount) + { + return addGoal( "-DthreadCount=" + threadCount ); + } + public SurefireLauncher forkMode( String forkMode ) { return addGoal( "-DforkMode=" + forkMode ); diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java index 43ecef46b2..577b8a5bb5 100755 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire907PerThreadWithoutThreadCountIT.java @@ -19,7 +19,6 @@ */ import org.apache.maven.surefire.its.fixture.OutputValidator; import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase; -import org.apache.maven.surefire.its.fixture.SurefireLauncher; import org.junit.Test; @@ -30,7 +29,7 @@ public class Surefire907PerThreadWithoutThreadCountIT public void categoryAB() { OutputValidator validator = unpack("fork-mode").forkPerThread().executeTestWithFailure(); - validator.verifyTextInLog( "Fork mode perthread requires a thread count" ); + validator.verifyTextInLog( "Fork modes perthread and onceperthread require a thread count" ); } } diff --git a/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml b/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml index 62593c943c..25b66f031d 100644 --- a/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml +++ b/surefire-integration-tests/src/test/resources/fork-mode-testng/pom.xml @@ -26,6 +26,7 @@ ${surefire.version} ${forkMode} + ${threadCount} diff --git a/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java b/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java index ef70d3e46e..408550b51f 100644 --- a/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java +++ b/surefire-integration-tests/src/test/resources/fork-mode-testng/src/test/java/forkMode/Test1.java @@ -4,12 +4,15 @@ import java.io.FileWriter; import java.io.IOException; import java.lang.management.ManagementFactory; +import java.util.Random; import org.testng.annotations.Test; public class Test1 { + private static final Random RANDOM = new Random(); + @Test public void test1() throws IOException @@ -32,6 +35,8 @@ public static void dumpPidFile( String name ) // In fact, it usually contains the pid and the local host name! String pid = ManagementFactory.getRuntimeMXBean().getName(); fw.write( pid ); + fw.write( " " ); + fw.write( System.getProperty( "testProperty", String.valueOf( RANDOM.nextLong() ) ) ); fw.flush(); fw.close(); } diff --git a/surefire-integration-tests/src/test/resources/fork-mode/pom.xml b/surefire-integration-tests/src/test/resources/fork-mode/pom.xml index 416fda9ac0..aa37e2cd6f 100644 --- a/surefire-integration-tests/src/test/resources/fork-mode/pom.xml +++ b/surefire-integration-tests/src/test/resources/fork-mode/pom.xml @@ -42,6 +42,7 @@ ${surefire.version} ${forkMode} + ${threadCount} diff --git a/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java b/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java index 05bac142d8..35f746e0a2 100644 --- a/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java +++ b/surefire-integration-tests/src/test/resources/fork-mode/src/test/java/forkMode/Test1.java @@ -4,6 +4,7 @@ import java.io.FileWriter; import java.io.IOException; import java.lang.management.ManagementFactory; +import java.util.Random; import junit.framework.TestCase; @@ -11,7 +12,9 @@ public class Test1 extends TestCase { - public void test1() + private static final Random RANDOM = new Random(); + + public void test1() throws IOException { dumpPidFile( this ); @@ -32,6 +35,8 @@ public static void dumpPidFile( TestCase test ) // In fact, it usually contains the pid and the local host name! String pid = ManagementFactory.getRuntimeMXBean().getName(); fw.write( pid ); + fw.write( " " ); + fw.write( System.getProperty( "testProperty", String.valueOf( RANDOM.nextLong() ) ) ); fw.flush(); fw.close(); } diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java index ff39099812..bbff4da5b0 100644 --- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java +++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java @@ -76,7 +76,17 @@ public RunResult invoke( Object forkTestSet ) { if ( testsToRun == null ) { - testsToRun = forkTestSet == null ? scanClassPath() : TestsToRun.fromClass( (Class) forkTestSet ); + if (forkTestSet instanceof TestsToRun) + { + testsToRun = (TestsToRun) forkTestSet; + } + else if (forkTestSet instanceof Class) + { + testsToRun = TestsToRun.fromClass( (Class) forkTestSet ); + } else + { + testsToRun = scanClassPath(); + } } ReporterFactory reporterFactory = providerParameters.getReporterFactory(); diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java index 28f127fa7b..5354685794 100644 --- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java +++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java @@ -89,9 +89,19 @@ public RunResult invoke( Object forkTestSet ) { if ( testsToRun == null ) { - testsToRun = forkTestSet == null ? scanClassPath() : TestsToRun.fromClass( (Class) forkTestSet ); + if (forkTestSet instanceof TestsToRun) + { + testsToRun = (TestsToRun) forkTestSet; + } + else if (forkTestSet instanceof Class) + { + testsToRun = TestsToRun.fromClass( (Class) forkTestSet ); + } else + { + testsToRun = scanClassPath(); + } } - + upgradeCheck(); final ReporterFactory reporterFactory = providerParameters.getReporterFactory(); @@ -107,9 +117,9 @@ public RunResult invoke( Object forkTestSet ) runNotifer.fireTestRunStarted( null ); - for ( Class clazz : testsToRun.getLocatedClasses() ) + for ( Iterator> iter = testsToRun.iterator(); iter.hasNext(); ) { - executeTestSet( clazz, reporter, runNotifer ); + executeTestSet( iter.next(), reporter, runNotifer ); } runNotifer.fireTestRunFinished( result ); diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java index 01b7c5d00b..afe68889c0 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java @@ -116,12 +116,17 @@ public RunResult invoke( Object forkTestSet ) if ( testsToRun == null ) { - testsToRun = forkTestSet == null ? getSuitesAsList( filter ) : TestsToRun.fromClass( (Class) forkTestSet ); - } - - if ( testsToRun.size() == 0 ) - { - filter = null; + if (forkTestSet instanceof TestsToRun) + { + testsToRun = (TestsToRun) forkTestSet; + } + else if (forkTestSet instanceof Class) + { + testsToRun = TestsToRun.fromClass( (Class) forkTestSet ); + } else + { + testsToRun = getSuitesAsList( filter ); + } } final Map testSetMap = new ConcurrentHashMap(); diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java index 57350b9377..bfd4dffc98 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java @@ -19,12 +19,13 @@ * under the License. */ +import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; + import org.apache.maven.surefire.common.junit4.JUnit4RunListener; import org.apache.maven.surefire.testset.TestSetFailedException; import org.apache.maven.surefire.util.TestsToRun; - import org.junit.runner.Computer; import org.junit.runner.JUnitCore; import org.junit.runner.Request; @@ -51,16 +52,20 @@ public static void execute( TestsToRun testsToRun, JUnitCoreParameters jUnitCore junitCore.addListener( runListener ); } - Request req = Request.classes( computer, testsToRun.getLocatedClasses() ); - if ( filter != null ) - { - req = req.filterWith( filter ); - } - try { - final Result run = junitCore.run( req ); - JUnit4RunListener.rethrowAnyTestMechanismFailures( run ); + Iterator classIter = testsToRun.iterator(); + while (classIter.hasNext()) + { + Request req = Request.classes( computer, new Class[]{ (Class) classIter.next() }); + if ( filter != null ) + { + req = req.filterWith( filter ); + } + + final Result run = junitCore.run( req ); + JUnit4RunListener.rethrowAnyTestMechanismFailures( run ); + } } finally { diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java index 807db0efc8..3e1ba0de06 100644 --- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java +++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java @@ -40,6 +40,7 @@ import org.apache.maven.surefire.report.RunListener; import org.apache.maven.surefire.report.SimpleReportEntry; import org.apache.maven.surefire.testset.TestSetFailedException; +import org.apache.maven.surefire.util.LazyTestsToRun; import org.apache.maven.surefire.util.RunOrderCalculator; import org.apache.maven.surefire.util.ScanResult; import org.apache.maven.surefire.util.TestsToRun; @@ -56,7 +57,9 @@ public class TestNGDirectoryTestSuite private final ArtifactVersion version; private final Map options; - + + private final Map junitOptions; + private final String testSourceDirectory; private final File reportsDirectory; @@ -68,6 +71,8 @@ public class TestNGDirectoryTestSuite private final String testMethodPattern; private final RunOrderCalculator runOrderCalculator; + + private final Class junitTestClass; public TestNGDirectoryTestSuite( String testSourceDirectory, String artifactVersion, Properties confOptions, File reportsDirectory, String testMethodPattern, @@ -83,55 +88,77 @@ public TestNGDirectoryTestSuite( String testSourceDirectory, String artifactVers this.scanResult = scanResult; this.version = new DefaultArtifactVersion( artifactVersion ); this.testMethodPattern = testMethodPattern; + this.junitTestClass = findJUnitTestClass(); + this.junitOptions = createJUnitOptions(); } public void execute( TestsToRun testsToRun, ReporterFactory reporterManagerFactory ) throws ReporterException, TestSetFailedException { - if ( testsToRun.size() == 0 ) - { - return; - } + if ( testsToRun instanceof LazyTestsToRun ) + { + executeLazy( testsToRun, reporterManagerFactory ); + } else if ( testsToRun.size() > 1 ) + { + executeMulti( testsToRun, reporterManagerFactory ); + } else if ( testsToRun.size() == 1 ) + { + Class testClass = (Class) testsToRun.iterator().next(); + executeSingleClass( reporterManagerFactory, testClass ); + } + } - if ( testsToRun.size() > 1 ) - { - executeMulti( testsToRun, reporterManagerFactory ); - return; - } + private void executeSingleClass( ReporterFactory reporterManagerFactory, Class testClass ) throws TestSetFailedException { + this.options.put( "suitename", testClass.getName() ); - this.options.put( "suitename", testsToRun.getLocatedClasses()[0].getName() ); + RunListener reporter = reporterManagerFactory.createReporter(); + ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter ); - RunListener reporter = reporterManagerFactory.createReporter(); - ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter ); + startTestSuite( reporter, this ); - startTestSuite( reporter, this ); + final Map optionsToUse = isJUnitTest(testClass) ? junitOptions : options; + + TestNGExecutor.run( new Class[]{ testClass }, testSourceDirectory, optionsToUse, + version, reporter, this, reportsDirectory, testMethodPattern ); - TestNGExecutor.run( new Class[]{ (Class) testsToRun.iterator().next() }, this.testSourceDirectory, this.options, - this.version, reporter, this, reportsDirectory, testMethodPattern ); + finishTestSuite( reporter, this ); + } - finishTestSuite( reporter, this ); + public void executeLazy( TestsToRun testsToRun, ReporterFactory reporterFactory ) + throws ReporterException, TestSetFailedException + { + + for ( Iterator testClassIt = testsToRun.iterator(); testClassIt.hasNext(); ) + { + Class c = (Class) testClassIt.next(); + executeSingleClass(reporterFactory, c); + } } + private Class findJUnitTestClass() { + Class junitTest; + try + { + junitTest = Class.forName( "junit.framework.Test" ); + } + catch ( ClassNotFoundException e ) + { + junitTest = null; + } + return junitTest; + } + public void executeMulti( TestsToRun testsToRun, ReporterFactory reporterFactory ) throws ReporterException, TestSetFailedException { - Class junitTest; - try - { - junitTest = Class.forName( "junit.framework.Test" ); - } - catch ( ClassNotFoundException e ) - { - junitTest = null; - } - List testNgTestClasses = new ArrayList(); List junitTestClasses = new ArrayList(); - for ( Iterator it = testsToRun.iterator(); it.hasNext(); ) + Class[] allClasses = testsToRun.getLocatedClasses(); + for ( int i = 0; i < allClasses.length; i++) { - Class c = (Class) it.next(); - if ( junitTest != null && junitTest.isAssignableFrom( c ) ) + Class c = allClasses[i]; + if ( isJUnitTest(c) ) { junitTestClasses.add( c ); } @@ -156,29 +183,30 @@ public void executeMulti( TestsToRun testsToRun, ReporterFactory reporterFactory Class[] testClasses = (Class[]) testNgTestClasses.toArray( new Class[testNgTestClasses.size()] ); - TestNGExecutor.run( testClasses, this.testSourceDirectory, this.options, this.version, reporterManager, this, + TestNGExecutor.run( testClasses, this.testSourceDirectory, options, version, reporterManager, this, testNgReportsDirectory, testMethodPattern ); if ( junitTestClasses.size() > 0 ) { testClasses = (Class[]) junitTestClasses.toArray( new Class[junitTestClasses.size()] ); - Map junitOptions = new HashMap(); - for ( Iterator it = this.options.keySet().iterator(); it.hasNext(); ) - { - Object key = it.next(); - junitOptions.put( key, options.get( key ) ); - } - - junitOptions.put( "junit", Boolean.TRUE ); - - TestNGExecutor.run( testClasses, this.testSourceDirectory, junitOptions, this.version, reporterManager, + TestNGExecutor.run( testClasses, testSourceDirectory, junitOptions, version, reporterManager, this, junitReportsDirectory, testMethodPattern ); } finishTestSuite( reporterManager, this ); } + private boolean isJUnitTest(Class c) { + return junitTestClass != null && junitTestClass.isAssignableFrom( c ); + } + + private Map createJUnitOptions() { + Map junitOptions = new HashMap(this.options); + junitOptions.put( "junit", Boolean.TRUE ); + return junitOptions; + } + // single class test public void execute( String testSetName, ReporterFactory reporterManagerFactory ) throws ReporterException, TestSetFailedException diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java index 56c5888325..f281b9e116 100644 --- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java +++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java @@ -100,7 +100,17 @@ public RunResult invoke( Object forkTestSet ) { if ( testsToRun == null ) { - testsToRun = forkTestSet == null ? scanClassPath() : TestsToRun.fromClass( (Class) forkTestSet ); + if (forkTestSet instanceof TestsToRun) + { + testsToRun = (TestsToRun) forkTestSet; + } + else if (forkTestSet instanceof Class) + { + testsToRun = TestsToRun.fromClass( (Class) forkTestSet ); + } else + { + testsToRun = scanClassPath(); + } } TestNGDirectoryTestSuite suite = getDirectorySuite(); suite.execute( testsToRun, reporterFactory ); From f341dff9da23d5a2ae4ac6ffa1eb8dd8e27a855c Mon Sep 17 00:00:00 2001 From: agudian Date: Fri, 16 Nov 2012 23:09:52 +0100 Subject: [PATCH 2/2] Added licence headers, javadoc, did some minor renaming --- .../booterclient/ForkConfiguration.java | 8 +- .../surefire/booterclient/ForkStarter.java | 4 +- .../lazytestprovider/FlushReceiver.java | 30 ++++++++ .../FlushReceiverProvider.java | 29 ++++++++ .../OutputStreamFlushableCommandline.java | 73 +++++++++++++++++++ .../ProcessAwareCommandline.java | 41 ----------- .../TestProvidingInputStream.java | 44 +++++++++++ .../maven/surefire/util/LazyTestsToRun.java | 37 +++++++++- 8 files changed, 215 insertions(+), 51 deletions(-) create mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java delete mode 100644 maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java 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 fef053a248..66a11a1252 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 @@ -29,7 +29,7 @@ import java.util.jar.Manifest; import org.apache.maven.plugin.surefire.AbstractSurefireMojo; -import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline; +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline; import org.apache.maven.plugin.surefire.util.Relocator; import org.apache.maven.shared.utils.StringUtils; import org.apache.maven.shared.utils.cli.Commandline; @@ -128,17 +128,17 @@ else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) || * @throws org.apache.maven.surefire.booter.SurefireBooterForkException * when unable to perform the fork */ - public ProcessAwareCommandline createCommandLine( List classPath, ClassLoaderConfiguration classpathConfiguration, + public OutputStreamFlushableCommandline createCommandLine( List classPath, ClassLoaderConfiguration classpathConfiguration, boolean shadefire, int threadNumber ) throws SurefireBooterForkException { return createCommandLine( classPath, classpathConfiguration.isManifestOnlyJarRequestedAndUsable(), shadefire, threadNumber ); } - public ProcessAwareCommandline createCommandLine( List classPath, boolean useJar, boolean shadefire, int threadNumber ) + public OutputStreamFlushableCommandline createCommandLine( List classPath, boolean useJar, boolean shadefire, int threadNumber ) throws SurefireBooterForkException { - ProcessAwareCommandline cli = new ProcessAwareCommandline(); + OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline(); cli.setExecutable( jvmExecutable ); 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 6c135b3861..19bfff3380 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 @@ -40,7 +40,7 @@ import org.apache.maven.plugin.surefire.CommonReflector; import org.apache.maven.plugin.surefire.StartupReportConfiguration; import org.apache.maven.plugin.surefire.SurefireProperties; -import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.ProcessAwareCommandline; +import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline; import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream; import org.apache.maven.plugin.surefire.booterclient.output.ForkClient; import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer; @@ -365,7 +365,7 @@ private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkC // Surefire-booter if !useSystemClassLoader Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls ); - @SuppressWarnings( "unchecked" ) ProcessAwareCommandline cli = + @SuppressWarnings( "unchecked" ) OutputStreamFlushableCommandline cli = forkConfiguration.createCommandLine( bootClasspath.getClassPath(), startupConfiguration.getClassLoaderConfiguration(), startupConfiguration.isShadefire(), threadNumber ); diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java index 5269d7aa5e..0c0bcdd331 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiver.java @@ -1,7 +1,37 @@ 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; +/** + * Something that can be flushed. + * + * @author Andreas Gudian + * + */ public interface FlushReceiver { + /** + * Performs a flush, releasing any buffered resources. + * + * @throws IOException in case the flush operation failed + */ void flush() throws IOException; } diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java index f285a666c2..052bb44b5a 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/FlushReceiverProvider.java @@ -1,5 +1,34 @@ 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. + */ + +/** + * Provides a {@link FlushReceiver}. + * + * @author Andreas Gudian + * + */ public interface FlushReceiverProvider { + + /** + * @return a {@link FlushReceiver} + */ FlushReceiver getFlushReceiver(); } \ No newline at end of file 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 new file mode 100644 index 0000000000..3149566d83 --- /dev/null +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/OutputStreamFlushableCommandline.java @@ -0,0 +1,73 @@ +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; + +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.Commandline; + +/** + * A {@link Commandline} implementation that provides the output stream of + * the executed process in form of a {@link FlushReceiver}, for it to be + * flushed on demand. + * + * @author Andreas Gudian + * + */ +public class OutputStreamFlushableCommandline extends Commandline implements FlushReceiverProvider { + /** + * Wraps an output stream in order to delegate a flush. + * + */ + private final class OutputStreamFlushReceiver implements FlushReceiver { + private final OutputStream outputStream; + + private OutputStreamFlushReceiver(OutputStream outputStream) { + this.outputStream = outputStream; + } + + public void flush() throws IOException { + outputStream.flush(); + } + } + + private FlushReceiver flushReceiver; + + @Override + public Process execute() throws CommandLineException { + Process process = super.execute(); + + if (process.getOutputStream() != null) { + flushReceiver = new OutputStreamFlushReceiver(process.getOutputStream()); + } + + return process; + } + + /* (non-Javadoc) + * @see org.apache.maven.plugin.surefire.booterclient.lazytestprovider.FlushReceiverProvider#getFlushReceiver() + */ + 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/lazytestprovider/ProcessAwareCommandline.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java deleted file mode 100644 index 5182107ebb..0000000000 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/ProcessAwareCommandline.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.apache.maven.plugin.surefire.booterclient.lazytestprovider; - -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.maven.shared.utils.cli.CommandLineException; -import org.apache.maven.shared.utils.cli.Commandline; - - - -public class ProcessAwareCommandline extends Commandline implements FlushReceiverProvider { - private final class OutputStreamFlushReceiver implements FlushReceiver { - private final OutputStream outputStream; - - private OutputStreamFlushReceiver(OutputStream outputStream) { - this.outputStream = outputStream; - } - - public void flush() throws IOException { - outputStream.flush(); - } - } - - private FlushReceiver flushReceiver; - - @Override - public Process execute() throws CommandLineException { - Process process = super.execute(); - - if (process.getOutputStream() != null) { - flushReceiver = new OutputStreamFlushReceiver(process.getOutputStream()); - } - - return process; - } - - 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/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java index a4e899d686..8b6f39664c 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java @@ -1,10 +1,43 @@ 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.InputStream; import java.util.Queue; import java.util.concurrent.Semaphore; +/** + * An {@link InputStream} that, when read, provides test class names out of + * a queue. + *

+ * The Stream provides only one test at a time, but only after {@link #provideNewTest()} + * has been invoked. + *

+ * After providing each test class name, followed by a newline character, a flush is + * performed on the {@link FlushReceiver} provided by the {@link FlushReceiverProvider} + * that can be set using {@link #setFlushReceiverProvider(FlushReceiverProvider)}. + * + * @author Andreas Gudian + * + */ public class TestProvidingInputStream extends InputStream { private final Queue testItemQueue; private byte[] currentBuffer; @@ -12,10 +45,18 @@ public class TestProvidingInputStream extends InputStream { private Semaphore semaphore = new Semaphore(0); private FlushReceiverProvider flushReceiverProvider; + /** + * C'tor + * + * @param testItemQueue source of the tests to be read from this stream + */ public TestProvidingInputStream(Queue testItemQueue) { this.testItemQueue = testItemQueue; } + /** + * @param flushReceiverProvider the provider for a flush receiver. + */ public void setFlushReceiverProvider(FlushReceiverProvider flushReceiverProvider) { this.flushReceiverProvider = flushReceiverProvider; } @@ -46,6 +87,9 @@ public synchronized int read() throws IOException { } } + /** + * Signal that a new test is to be provided. + */ public void provideNewTest() { semaphore.release(); } diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java index dabe1ae744..24fe675fac 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/LazyTestsToRun.java @@ -1,8 +1,24 @@ -/** - * - */ package org.apache.maven.surefire.util; +/* + * 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.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -16,7 +32,7 @@ import org.apache.maven.surefire.booter.ForkingRunListener; /** - * A variant of TestsToRun that is provided with test class names asynchronously + * A variant of TestsToRun that is provided with test class names * from an {@link InputStream} (e.g. {@code System.in}). The method * {@link #iterator()} returns an Iterator that blocks on calls to * {@link Iterator#hasNext()} until new classes are available, or no more @@ -35,6 +51,13 @@ public class LazyTestsToRun extends TestsToRun { private ClassLoader testClassLoader; private PrintStream originalOutStream; + /** + * C'tor + * + * @param testSource source to read the tests from + * @param testClassLoader class loader to load the test classes + * @param originalOutStream the output stream to use when requesting new new tests + */ public LazyTestsToRun(InputStream testSource, ClassLoader testClassLoader, PrintStream originalOutStream) { super(Collections.emptyList()); @@ -104,6 +127,9 @@ public void remove() { } + /* (non-Javadoc) + * @see org.apache.maven.surefire.util.TestsToRun#iterator() + */ public Iterator iterator() { return new BlockingIterator(); } @@ -122,6 +148,9 @@ public Class[] getLocatedClasses() { throw new UnsupportedOperationException("use method iterator()"); } + /* (non-Javadoc) + * @see org.apache.maven.surefire.util.TestsToRun#toString() + */ public String toString() { StringBuffer sb = new StringBuffer("LazyTestsToRun "); synchronized (workQueue) {