You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2019/12/27 01:59:00 UTC

[maven-surefire] branch cli updated: added unit tests

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch cli
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git


The following commit(s) were added to refs/heads/cli by this push:
     new 6f4ee88  added unit tests
6f4ee88 is described below

commit 6f4ee88cf31605841fa9672227538f3c874c780d
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Dec 27 02:58:51 2019 +0100

    added unit tests
---
 .../plugin/surefire/booterclient/ForkStarter.java  |   3 +-
 .../surefire/booterclient/ForkStarterTest.java     | 272 +++++++++++++++++++++
 .../plugin/surefire/booterclient/MainClass.java    |  52 ++++
 .../org/apache/maven/surefire/JUnit4SuiteTest.java |   2 +
 surefire-extensions-api/pom.xml                    |  13 +
 .../extensions/util/CommandlineExecutorTest.java   | 109 +++++++++
 .../surefire/extensions/util/JUnit4SuiteTest.java  |  38 +++
 7 files changed, 487 insertions(+), 2 deletions(-)

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 a17737b..f2734ee 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,6 @@ import org.apache.maven.surefire.extensions.util.LineConsumerThread;
 import org.apache.maven.surefire.extensions.util.StreamFeeder;
 import org.apache.maven.surefire.shared.utils.cli.CommandLineException;
 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
-import org.apache.maven.surefire.booter.KeyValueSource;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 import org.apache.maven.surefire.booter.ProviderFactory;
@@ -548,7 +547,7 @@ public class ForkStarter
         }
     }
 
-    private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkClient forkClient,
+    private RunResult fork( Object testSet, PropertiesWrapper providerProperties, ForkClient forkClient,
                             SurefireProperties effectiveSystemProperties, int forkNumber,
                             AbstractForkInputStream commandInputStream, boolean readTestsFromInStream )
         throws SurefireBooterForkException
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
new file mode 100644
index 0000000..e0874f0
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkStarterTest.java
@@ -0,0 +1,272 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.StartupReportConfiguration;
+import org.apache.maven.plugin.surefire.SurefireProperties;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractForkInputStream;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
+import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
+import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
+import org.apache.maven.surefire.booter.AbstractPathConfiguration;
+import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
+import org.apache.maven.surefire.booter.Classpath;
+import org.apache.maven.surefire.booter.PropertiesWrapper;
+import org.apache.maven.surefire.booter.ProviderConfiguration;
+import org.apache.maven.surefire.booter.Shutdown;
+import org.apache.maven.surefire.booter.StartupConfiguration;
+import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.report.ReporterConfiguration;
+import org.apache.maven.surefire.shared.compress.archivers.zip.Zip64Mode;
+import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveOutputStream;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.Manifest;
+import java.util.zip.Deflater;
+
+import static org.fest.util.Files.delete;
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.powermock.reflect.Whitebox.invokeMethod;
+
+/**
+ *
+ */
+public class ForkStarterTest
+{
+    private static String baseDir = System.getProperty( "user.dir" );
+    private static File tmp;
+
+    @Rule
+    public final ExpectedException e = ExpectedException.none();
+
+    @BeforeClass
+    public static void prepareFiles() throws IOException
+    {
+        File target = new File( baseDir, "target" );
+        tmp = new File( target, "tmp" );
+        tmp.mkdirs();
+        File booter = new File( tmp, "surefirebooter.jar" );
+        booter.createNewFile();
+
+        try ( ZipArchiveOutputStream zos = new ZipArchiveOutputStream( new FileOutputStream( booter ) ) )
+        {
+            zos.setUseZip64( Zip64Mode.Never );
+            zos.setLevel( Deflater.NO_COMPRESSION );
+
+            ZipArchiveEntry ze = new ZipArchiveEntry( "META-INF/MANIFEST.MF" );
+            zos.putArchiveEntry( ze );
+
+            Manifest man = new Manifest();
+
+            man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
+            man.getMainAttributes().putValue( "Main-Class", MainClass.class.getName() );
+
+            man.write( zos );
+
+            zos.closeArchiveEntry();
+
+            ze = new ZipArchiveEntry( "org/apache/maven/plugin/surefire/booterclient/MainClass.class" );
+            zos.putArchiveEntry( ze );
+            String classesDir = Paths.get( target.getPath(), "test-classes" ).toString();
+            Path cls = Paths.get( classesDir, "org", "apache", "maven", "plugin", "surefire", "booterclient",
+                "MainClass.class" );
+            zos.write( Files.readAllBytes( cls ) );
+            zos.closeArchiveEntry();
+        }
+    }
+
+    @AfterClass
+    public static void deleteTmp()
+    {
+        delete( tmp );
+    }
+
+    @Test
+    public void processShouldExitWithoutSayingGoodBye() throws Exception
+    {
+        ReporterConfiguration reporterConfiguration = new ReporterConfiguration( tmp, true );
+
+        ProviderConfiguration providerConfiguration = mock( ProviderConfiguration.class );
+        when( providerConfiguration.getReporterConfiguration() )
+            .thenReturn( reporterConfiguration );
+        when( providerConfiguration.getShutdown() )
+            .thenReturn( Shutdown.EXIT );
+
+        StartupConfiguration startupConfiguration = mock( StartupConfiguration.class );
+        when( startupConfiguration.getClasspathConfiguration() )
+            .thenReturn( new ClasspathConfig() );
+        when( startupConfiguration.getClassLoaderConfiguration() )
+            .thenReturn( new ClassLoaderConfiguration( false, false ) );
+        when( startupConfiguration.getProviderClassName() )
+            .thenReturn( MainClass.class.getName() );
+
+        ForkConfiguration forkConfiguration = mock( ForkConfiguration.class );
+        when( forkConfiguration.getWorkingDirectory() )
+            .thenReturn( tmp );
+        when( forkConfiguration.getTempDirectory() )
+            .thenReturn( tmp );
+        when( forkConfiguration.getPluginPlatform() )
+            .thenReturn( new Platform() );
+        OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
+        cli.setWorkingDirectory( tmp );
+        cli.setExecutable( System.getProperty( "java.home" ) + "/bin/java" );
+        cli.createArg().setLine( "-jar" );
+        cli.createArg().setLine( "surefirebooter.jar" );
+        cli.createArg().setLine( "fail" );
+        when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
+            .thenReturn( cli );
+
+        StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
+            false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
+
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+
+        ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
+            0, startupReportConfiguration, logger );
+
+        DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
+
+        e.expect( SurefireBooterForkException.class );
+        e.expectMessage( containsString( "Process Exit Code: 1" ) );
+        e.expectMessage( containsString( "The forked VM terminated without properly saying goodbye." ) );
+        e.expectMessage( containsString( "VM crash or System.exit called?" ) );
+
+        Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
+            int.class, AbstractForkInputStream.class, boolean.class};
+        TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream( new ArrayDeque<String>() );
+        invokeMethod( forkStarter, "fork", types, null,
+            new PropertiesWrapper( Collections.<String, String>emptyMap() ),
+            new ForkClient( reporterFactory, null, logger, new AtomicBoolean(), 1 ),
+            new SurefireProperties(), 1, testProvidingInputStream, true );
+        testProvidingInputStream.close();
+    }
+
+    @Test
+    public void processShouldWaitForAck() throws Exception
+    {
+        ReporterConfiguration reporterConfiguration = new ReporterConfiguration( tmp, true );
+
+        ProviderConfiguration providerConfiguration = mock( ProviderConfiguration.class );
+        when( providerConfiguration.getReporterConfiguration() )
+            .thenReturn( reporterConfiguration );
+        when( providerConfiguration.getShutdown() )
+            .thenReturn( Shutdown.EXIT );
+
+        StartupConfiguration startupConfiguration = mock( StartupConfiguration.class );
+        when( startupConfiguration.getClasspathConfiguration() )
+            .thenReturn( new ClasspathConfig() );
+        when( startupConfiguration.getClassLoaderConfiguration() )
+            .thenReturn( new ClassLoaderConfiguration( false, false ) );
+        when( startupConfiguration.getProviderClassName() )
+            .thenReturn( MainClass.class.getName() );
+
+        ForkConfiguration forkConfiguration = mock( ForkConfiguration.class );
+        when( forkConfiguration.getWorkingDirectory() )
+            .thenReturn( tmp );
+        when( forkConfiguration.getTempDirectory() )
+            .thenReturn( tmp );
+        when( forkConfiguration.getPluginPlatform() )
+            .thenReturn( new Platform() );
+        OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
+        cli.setWorkingDirectory( tmp );
+        cli.setExecutable( System.getProperty( "java.home" ) + "/bin/java" );
+        cli.createArg().setLine( "-jar" );
+        cli.createArg().setLine( "surefirebooter.jar" );
+        when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
+            .thenReturn( cli );
+
+        StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
+            false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
+
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+
+        ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
+            0, startupReportConfiguration, logger );
+
+        DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
+
+        Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
+            int.class, AbstractForkInputStream.class, boolean.class};
+        TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
+        invokeMethod( forkStarter, "fork", types, null,
+            new PropertiesWrapper( Collections.<String, String>emptyMap() ),
+            new ForkClient( reporterFactory, testLessInputStream, logger, new AtomicBoolean(), 1 ),
+            new SurefireProperties(), 1, testLessInputStream, true );
+        testLessInputStream.close();
+    }
+
+    private static class ClasspathConfig extends AbstractPathConfiguration
+    {
+        ClasspathConfig()
+        {
+            this( Classpath.emptyClasspath(), false, false );
+        }
+
+        private ClasspathConfig( Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation )
+        {
+            super( surefireClasspathUrls, enableAssertions, childDelegation );
+        }
+
+        @Override
+        public Classpath getTestClasspath()
+        {
+            return Classpath.emptyClasspath();
+        }
+
+        @Override
+        public boolean isModularPathConfig()
+        {
+            return false;
+        }
+
+        @Override
+        public boolean isClassPathConfig()
+        {
+            return true;
+        }
+
+        @Override
+        protected Classpath getInprocClasspath()
+        {
+            return Classpath.emptyClasspath();
+        }
+    }
+}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
new file mode 100644
index 0000000..d90a128
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MainClass.java
@@ -0,0 +1,52 @@
+package org.apache.maven.plugin.surefire.booterclient;
+
+/*
+ * 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;
+
+/**
+ * For testing purposes.
+ */
+public class MainClass
+{
+    public static void main( String... args ) throws IOException
+    {
+        if ( "fail".equals( args[0] ) )
+        {
+            System.exit( 1 );
+        }
+        else
+        {
+            System.out.println( ":maven:surefire:std:out:bye" );
+            if ( System.in.read() == 0
+                && System.in.read() == 0
+                && System.in.read() == 0
+                && System.in.read() == 5
+                && System.in.read() == 0
+                && System.in.read() == 0
+                && System.in.read() == 0
+                && System.in.read() == 0 )
+            {
+                System.exit( 0 );
+            }
+            System.exit( 1 );
+        }
+    }
+}
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index d28a89c..b1e3b22 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -34,6 +34,7 @@ import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerProviderC
 import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerStartupConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.DefaultForkConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.ForkConfigurationTest;
+import org.apache.maven.plugin.surefire.booterclient.ForkStarterTest;
 import org.apache.maven.plugin.surefire.booterclient.ForkingRunListenerTest;
 import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfigurationTest;
 import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfigurationTest;
@@ -104,6 +105,7 @@ public class JUnit4SuiteTest extends TestCase
         suite.addTest( new JUnit4TestAdapter( TestSetStatsTest.class ) );
         suite.addTest( new JUnit4TestAdapter( StatelessTestsetInfoReporterTest.class ) );
         suite.addTest( new JUnit4TestAdapter( CommonReflectorTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( ForkStarterTest.class ) );
         return suite;
     }
 }
diff --git a/surefire-extensions-api/pom.xml b/surefire-extensions-api/pom.xml
index 5904157..1e88403 100644
--- a/surefire-extensions-api/pom.xml
+++ b/surefire-extensions-api/pom.xml
@@ -48,6 +48,16 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.maven.surefire</groupId>
+            <artifactId>surefire-shared-utils</artifactId>
+            <version>3.0.0-M4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jacoco</groupId>
             <artifactId>org.jacoco.agent</artifactId>
             <classifier>runtime</classifier>
@@ -89,6 +99,9 @@
                 </dependencies>
                 <configuration>
                     <argLine>${jvm.args.tests}</argLine>
+                    <includes>
+                        <include>**/JUnit4SuiteTest.java</include>
+                    </includes>
                     <systemPropertyVariables>
                         <jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
                     </systemPropertyVariables>
diff --git a/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
new file mode 100644
index 0000000..fd99059
--- /dev/null
+++ b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
@@ -0,0 +1,109 @@
+package org.apache.maven.surefire.extensions.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 org.apache.maven.surefire.shared.utils.cli.Commandline;
+import org.apache.maven.surefire.shared.utils.cli.StreamConsumer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.nio.file.Paths;
+
+import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.util.Files.delete;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ *
+ */
+public class CommandlineExecutorTest
+{
+    private CommandlineExecutor exec;
+    private CommandlineStreams streams;
+    private String baseDir;
+    LineConsumerThread out;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        baseDir = System.getProperty( "user.dir" );
+
+        delete( Paths.get( baseDir, "target", "CommandlineExecutorTest" ).toFile() );
+
+        boolean createdDir = Paths.get( baseDir, "target", "CommandlineExecutorTest" )
+            .toFile()
+            .mkdirs();
+
+        assertThat( createdDir )
+            .isTrue();
+
+        boolean createdFile = Paths.get( baseDir, "target", "CommandlineExecutorTest", "a.txt" )
+            .toFile()
+            .createNewFile();
+
+        assertThat( createdFile )
+            .isTrue();
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        if ( out != null )
+        {
+            out.close();
+        }
+        exec.close();
+        streams.close();
+        delete( Paths.get( baseDir, "target", "CommandlineExecutorTest" ).toFile() );
+    }
+
+    @Test
+    public void shouldExecuteNativeCommand() throws Exception
+    {
+        Closeable closer = mock( Closeable.class );
+        Commandline cli = new Commandline( IS_OS_WINDOWS ? "dir" : "ls -la" );
+        cli.setWorkingDirectory( Paths.get( baseDir, "target", "CommandlineExecutorTest" ).toFile() );
+        CountdownCloseable countdownCloseable = new CountdownCloseable( closer, 1 );
+        exec = new CommandlineExecutor( cli, countdownCloseable );
+        streams = exec.execute();
+        StreamConsumer consumer = mock( StreamConsumer.class );
+        InputStream is = new InputStream()
+        {
+            @Override
+            public int read()
+            {
+                return -1;
+            }
+        };
+        StreamFeeder in = new StreamFeeder( "std-in-fork-1", streams.getStdInChannel(), is );
+        in.start();
+        out = new LineConsumerThread( "std-out-fork-1", streams.getStdOutChannel(), consumer, countdownCloseable );
+        out.start();
+        exec.awaitExit();
+        verify( consumer )
+            .consumeLine( contains( "a.txt" ) );
+    }
+}
diff --git a/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/JUnit4SuiteTest.java b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/JUnit4SuiteTest.java
new file mode 100644
index 0000000..df9cca1
--- /dev/null
+++ b/surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/JUnit4SuiteTest.java
@@ -0,0 +1,38 @@
+package org.apache.maven.surefire.extensions.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 junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ *
+ */
+public class JUnit4SuiteTest extends TestCase
+{
+    public static Test suite()
+    {
+        TestSuite suite = new TestSuite();
+        suite.addTest( new JUnit4TestAdapter( CommandlineExecutorTest.class ) );
+        return suite;
+    }
+}