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 2020/03/07 21:11:31 UTC

[maven-surefire] branch maven2surefire-jvm-communication updated: StreamFeeder (impl class of extension) moved to the right module and added a new test, and moved the logic from MasterProcessCommand to the StreamFeeder

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

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


The following commit(s) were added to refs/heads/maven2surefire-jvm-communication by this push:
     new 5bbe758  StreamFeeder (impl class of extension) moved to the right module and added a new test, and moved the logic from MasterProcessCommand to the StreamFeeder
5bbe758 is described below

commit 5bbe758baf303b1f898c2f37bcd75024598b4268
Author: tibordigana <ti...@apache.org>
AuthorDate: Sat Mar 7 22:11:22 2020 +0100

    StreamFeeder (impl class of extension) moved to the right module and added a new test, and moved the logic from MasterProcessCommand to the StreamFeeder
---
 .../surefire/extensions/LegacyForkChannel.java     |   3 +-
 .../plugin/surefire/extensions/StreamFeeder.java   | 203 +++++++++++++++++++++
 .../surefire/extensions/SurefireForkChannel.java   |   3 +-
 .../TestLessInputStreamBuilderTest.java            |   6 +-
 .../TestProvidingInputStreamTest.java              |   7 +-
 .../surefire/extensions/StreamFeederTest.java      | 162 ++++++++++++++++
 .../org/apache/maven/surefire/JUnit4SuiteTest.java |   6 +-
 .../surefire/booter/MasterProcessCommand.java      |  93 +---------
 .../spi/LegacyMasterProcessChannelDecoder.java     |  27 ++-
 .../maven/surefire/booter/CommandReaderTest.java   |   4 +-
 .../spi/LegacyMasterProcessChannelDecoderTest.java |  54 ++----
 .../surefire/extensions/util/StreamFeeder.java     |  94 ----------
 .../extensions}/CommandlineExecutorTest.java       |  37 +---
 .../surefire/extensions}/JUnit4SuiteTest.java      |   2 +-
 14 files changed, 430 insertions(+), 271 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
index 89cda94..a554edf 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/LegacyForkChannel.java
@@ -26,7 +26,6 @@ import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.extensions.EventHandler;
 import org.apache.maven.surefire.extensions.ForkChannel;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
-import org.apache.maven.surefire.extensions.util.StreamFeeder;
 
 import javax.annotation.Nonnull;
 import java.nio.channels.ReadableByteChannel;
@@ -71,7 +70,7 @@ final class LegacyForkChannel extends ForkChannel
     public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands,
                                                     WritableByteChannel stdIn )
     {
-        return new StreamFeeder( "std-in-fork-" + getForkChannelId(), stdIn, commands );
+        return new StreamFeeder( "std-in-fork-" + getForkChannelId(), stdIn, commands, logger );
     }
 
     @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java
new file mode 100644
index 0000000..604ca33
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/StreamFeeder.java
@@ -0,0 +1,203 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.booter.MasterProcessCommand;
+import org.apache.maven.surefire.extensions.CloseableDaemonThread;
+import org.apache.maven.surefire.extensions.CommandReader;
+import org.apache.maven.surefire.util.internal.ImmutableMap;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.channels.WritableByteChannel;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.MAGIC_NUMBER;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
+
+/**
+ * Commands which are sent from plugin to the forked jvm.
+ * <br>
+ *     <br>
+ * magic number : opcode [: opcode specific data]*
+ * <br>
+ *     or data encoded with Base64
+ * <br>
+ * magic number : opcode [: Base64(opcode specific data)]*
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 3.0.0-M5
+ */
+public class StreamFeeder extends CloseableDaemonThread
+{
+    private static final Map<MasterProcessCommand, String> COMMAND_OPCODES = opcodesToStrings();
+
+    private final WritableByteChannel channel;
+    private final CommandReader commandReader;
+    private final ConsoleLogger logger;
+
+    private volatile boolean disabled;
+    private volatile Throwable exception;
+
+    public StreamFeeder( @Nonnull String threadName, @Nonnull WritableByteChannel channel,
+                         @Nonnull CommandReader commandReader, @Nonnull ConsoleLogger logger )
+    {
+        super( threadName );
+        this.channel = channel;
+        this.commandReader = commandReader;
+        this.logger = logger;
+    }
+
+    @Override
+    @SuppressWarnings( "checkstyle:innerassignment" )
+    public void run()
+    {
+        try ( WritableByteChannel c = channel )
+        {
+            for ( Command cmd; ( cmd = commandReader.readNextCommand() ) != null; )
+            {
+                if ( !disabled )
+                {
+                    MasterProcessCommand cmdType = cmd.getCommandType();
+                    byte[] data = cmdType.hasDataType() ? encode( cmdType, cmd.getData() ) : encode( cmdType );
+                    c.write( ByteBuffer.wrap( data ) );
+                }
+            }
+        }
+        catch ( ClosedChannelException e )
+        {
+            // closed externally
+        }
+        catch ( IOException | NonWritableChannelException e )
+        {
+            exception = e.getCause() == null ? e : e.getCause();
+        }
+        catch ( IllegalArgumentException e )
+        {
+            logger.error( e.getLocalizedMessage() );
+        }
+    }
+
+    public void disable()
+    {
+        disabled = true;
+    }
+
+    public Throwable getException()
+    {
+        return exception;
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        channel.close();
+    }
+
+    /**
+     * Public method for testing purposes.
+     *
+     * @param cmdType command type
+     * @param data data to encode
+     * @return command with data encoded to bytes
+     */
+    public static byte[] encode( MasterProcessCommand cmdType, String data )
+    {
+        if ( !cmdType.hasDataType() )
+        {
+            throw new IllegalArgumentException( "cannot use data without data type" );
+        }
+
+        if ( cmdType.getDataType() != String.class )
+        {
+            throw new IllegalArgumentException( "Data type can be only " + String.class );
+        }
+
+        return encode( COMMAND_OPCODES.get( cmdType ), data )
+            .toString()
+            .getBytes( US_ASCII );
+    }
+
+    /**
+     * Public method for testing purposes.
+     *
+     * @param cmdType command type
+     * @return command without data encoded to bytes
+     */
+    public static byte[] encode( MasterProcessCommand cmdType )
+    {
+        if ( cmdType.getDataType() != Void.class )
+        {
+            throw new IllegalArgumentException( "Data type can be only " + cmdType.getDataType() );
+        }
+
+        return encode( COMMAND_OPCODES.get( cmdType ), null )
+            .toString()
+            .getBytes( US_ASCII );
+    }
+
+    /**
+     * Encodes opcode and data.
+     *
+     * @param operation opcode
+     * @param data   data
+     * @return encoded command
+     */
+    private static StringBuilder encode( String operation, String data )
+    {
+        StringBuilder s = new StringBuilder( 128 )
+            .append( ':' )
+            .append( MAGIC_NUMBER )
+            .append( ':' )
+            .append( operation );
+
+        if ( data != null )
+        {
+            s.append( ':' )
+                .append( data );
+        }
+
+        return s.append( ':' );
+    }
+
+    private static Map<MasterProcessCommand, String> opcodesToStrings()
+    {
+        Map<MasterProcessCommand, String> opcodes = new HashMap<>();
+        opcodes.put( RUN_CLASS, "run-testclass" );
+        opcodes.put( TEST_SET_FINISHED, "testset-finished" );
+        opcodes.put( SKIP_SINCE_NEXT_TEST, "skip-since-next-test" );
+        opcodes.put( SHUTDOWN, "shutdown" );
+        opcodes.put( NOOP, "noop" );
+        opcodes.put( BYE_ACK, "bye-ack" );
+        return new ImmutableMap<>( opcodes );
+    }
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
index 65a38f0..0aa790c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -26,7 +26,6 @@ import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.extensions.EventHandler;
 import org.apache.maven.surefire.extensions.ForkChannel;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
-import org.apache.maven.surefire.extensions.util.StreamFeeder;
 
 import javax.annotation.Nonnull;
 import java.io.IOException;
@@ -117,7 +116,7 @@ final class SurefireForkChannel extends ForkChannel
     public CloseableDaemonThread bindCommandReader( @Nonnull CommandReader commands,
                                                     WritableByteChannel stdIn )
     {
-        return new StreamFeeder( "commands-fork-" + getForkChannelId(), channel, commands );
+        return new StreamFeeder( "commands-fork-" + getForkChannelId(), channel, commands, logger );
     }
 
     @Override
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
index 5e4f766..2aab4c2 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStreamBuilderTest.java
@@ -33,12 +33,12 @@ import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 import static java.nio.channels.Channels.newChannel;
-import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider
-    .TestLessInputStream.TestLessInputStreamBuilder;
+import static org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
 import static org.apache.maven.surefire.booter.Shutdown.EXIT;
 import static org.apache.maven.surefire.booter.Shutdown.KILL;
+import static org.apache.maven.plugin.surefire.extensions.StreamFeeder.encode;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
@@ -155,7 +155,7 @@ public class TestLessInputStreamBuilderTest
                     if ( cmd != null )
                     {
                         MasterProcessCommand cmdType = cmd.getCommandType();
-                        buffer = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
+                        buffer = cmdType.hasDataType() ? encode( cmdType, cmd.getData() ) : encode( cmdType );
                     }
                 }
 
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index a7f3b8d..1d348ce 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -21,6 +21,7 @@ package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
 
 import org.apache.maven.surefire.booter.Command;
 import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
+import org.apache.maven.plugin.surefire.extensions.StreamFeeder;
 import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
 import org.junit.Test;
 
@@ -106,11 +107,11 @@ public class TestProvidingInputStreamTest
 
         Command cmd = is.readNextCommand();
         assertThat( cmd.getData(), is( nullValue() ) );
-        String stream = new String( cmd.getCommandType().encode(), US_ASCII );
+        String stream = new String( StreamFeeder.encode( cmd.getCommandType() ), US_ASCII );
 
         cmd = is.readNextCommand();
         assertThat( cmd.getData(), is( nullValue() ) );
-        stream += new String( cmd.getCommandType().encode(), US_ASCII );
+        stream += new String( StreamFeeder.encode( cmd.getCommandType() ), US_ASCII );
 
         assertThat( stream,
             is( ":maven-surefire-command:testset-finished::maven-surefire-command:testset-finished:" ) );
@@ -161,7 +162,7 @@ public class TestProvidingInputStreamTest
                 {
                     idx = 0;
                     Command cmd = pluginIs.readNextCommand();
-                    buffer = cmd == null ? null : cmd.getCommandType().encode();
+                    buffer = cmd == null ? null : StreamFeeder.encode( cmd.getCommandType() );
                 }
 
                 if ( buffer != null )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java
new file mode 100644
index 0000000..7da6745
--- /dev/null
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/StreamFeederTest.java
@@ -0,0 +1,162 @@
+package org.apache.maven.plugin.surefire.extensions;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.booter.Command;
+import org.apache.maven.surefire.extensions.CommandReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.Iterator;
+
+import static java.util.Arrays.asList;
+import static org.apache.maven.surefire.booter.Command.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link StreamFeeder}.
+ */
+public class StreamFeederTest
+{
+    private final ByteArrayOutputStream out = new ByteArrayOutputStream();
+    private final WritableByteChannel channel = mock( WritableByteChannel.class );
+    private final CommandReader commandReader = mock( CommandReader.class );
+    private StreamFeeder streamFeeder;
+
+    @Before
+    public void setup() throws IOException
+    {
+        final Iterator<Command> it = asList( new Command( RUN_CLASS, "pkg.ATest" ), TEST_SET_FINISHED ).iterator();
+        when( commandReader.readNextCommand() )
+            .thenAnswer( new Answer<Command>()
+            {
+                @Override
+                public Command answer( InvocationOnMock invocation )
+                {
+                    return it.hasNext() ? it.next() : null;
+                }
+            } );
+    }
+
+    @After
+    public void close() throws IOException
+    {
+        if ( streamFeeder != null )
+        {
+            streamFeeder.disable();
+            streamFeeder.close();
+        }
+    }
+
+    @Test
+    public void shouldEncodeCommandToStream() throws Exception
+    {
+        when( channel.write( any( ByteBuffer.class ) ) )
+            .thenAnswer( new Answer<Object>()
+            {
+                @Override
+                public Object answer( InvocationOnMock invocation ) throws IOException
+                {
+                    ByteBuffer bb = invocation.getArgument( 0 );
+                    bb.flip();
+                    out.write( bb.array() );
+                    return 0;
+                }
+            } );
+
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+        streamFeeder = new StreamFeeder( "t", channel, commandReader, logger );
+        streamFeeder.start();
+
+        streamFeeder.join();
+        String commands = out.toString();
+
+        assertThat( commands )
+            .isEqualTo( ":maven-surefire-command:run-testclass:pkg.ATest::maven-surefire-command:testset-finished:" );
+
+        verify( channel, times( 1 ) )
+            .close();
+
+        assertThat( streamFeeder.getException() )
+            .isNull();
+
+        verifyZeroInteractions( logger );
+    }
+
+    @Test
+    public void shouldFailThread() throws Exception
+    {
+        when( channel.write( any( ByteBuffer.class ) ) )
+            .thenAnswer( new Answer<Object>()
+            {
+                @Override
+                public Object answer( InvocationOnMock invocation ) throws IOException
+                {
+                    throw new IOException();
+                }
+            } );
+
+        ConsoleLogger logger = mock( ConsoleLogger.class );
+        streamFeeder = new StreamFeeder( "t", channel, commandReader, logger );
+        streamFeeder.start();
+
+        streamFeeder.join();
+
+        assertThat( out.size() )
+            .isZero();
+
+        verify( channel, times( 1 ) )
+            .close();
+
+        assertThat( streamFeeder.getException() )
+            .isNotNull()
+            .isInstanceOf( IOException.class );
+
+        verifyZeroInteractions( logger );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailWithoutData()
+    {
+        StreamFeeder.encode( RUN_CLASS );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void shouldFailWithData()
+    {
+        StreamFeeder.encode( NOOP, "" );
+    }
+}
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 92b7a42..a232544 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
@@ -40,7 +40,10 @@ import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfigu
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStreamBuilderTest;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStreamTest;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClientTest;
+import org.apache.maven.plugin.surefire.extensions.ConsoleOutputReporterTest;
 import org.apache.maven.plugin.surefire.extensions.ForkedProcessEventNotifierTest;
+import org.apache.maven.plugin.surefire.extensions.StatelessReporterTest;
+import org.apache.maven.plugin.surefire.extensions.StreamFeederTest;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactoryTest;
 import org.apache.maven.plugin.surefire.report.StatelessXmlReporterTest;
 import org.apache.maven.plugin.surefire.report.TestSetStatsTest;
@@ -50,9 +53,7 @@ import org.apache.maven.plugin.surefire.util.DependenciesScannerTest;
 import org.apache.maven.plugin.surefire.util.DirectoryScannerTest;
 import org.apache.maven.plugin.surefire.util.ScannerUtilTest;
 import org.apache.maven.plugin.surefire.util.SpecificFileFilterTest;
-import org.apache.maven.plugin.surefire.extensions.ConsoleOutputReporterTest;
 import org.apache.maven.surefire.extensions.ForkChannelTest;
-import org.apache.maven.plugin.surefire.extensions.StatelessReporterTest;
 import org.apache.maven.surefire.extensions.StatelessTestsetInfoReporterTest;
 import org.apache.maven.surefire.report.FileReporterTest;
 import org.apache.maven.surefire.report.RunStatisticsTest;
@@ -106,6 +107,7 @@ public class JUnit4SuiteTest extends TestCase
         suite.addTest( new JUnit4TestAdapter( CommonReflectorTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkStarterTest.class ) );
         suite.addTest( new JUnit4TestAdapter( ForkChannelTest.class ) );
+        suite.addTest( new JUnit4TestAdapter( StreamFeederTest.class ) );
         return suite;
     }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
index 55e604d..b6ae644 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
@@ -19,45 +19,31 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import javax.annotation.Nonnull;
-
-import static java.nio.charset.StandardCharsets.US_ASCII;
 import static java.util.Objects.requireNonNull;
 
 /**
  * Commands which are sent from plugin to the forked jvm.
- * Support and methods related to the commands.
- * <br>
- *     <br>
- * magic number : opcode [: opcode specific data]*
- * <br>
- *     or data encoded with Base64
- * <br>
- * magic number : opcode [: Base64(opcode specific data)]*
  *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.19
  */
 public enum MasterProcessCommand
 {
-    RUN_CLASS( "run-testclass", String.class ),
-    TEST_SET_FINISHED( "testset-finished", Void.class ),
-    SKIP_SINCE_NEXT_TEST( "skip-since-next-test", Void.class ),
-    SHUTDOWN( "shutdown", String.class ),
+    RUN_CLASS( String.class ),
+    TEST_SET_FINISHED( Void.class ),
+    SKIP_SINCE_NEXT_TEST( Void.class ),
+    SHUTDOWN( String.class ),
 
     /** To tell a forked process that the master process is still alive. Repeated after 10 seconds. */
-    NOOP( "noop", Void.class ),
-    BYE_ACK( "bye-ack", Void.class );
+    NOOP( Void.class ),
+    BYE_ACK( Void.class );
 
     public static final String MAGIC_NUMBER = "maven-surefire-command";
 
-    private final String opcodeName;
-
     private final Class<?> dataType;
 
-    MasterProcessCommand( String opcodeName, Class<?> dataType )
+    MasterProcessCommand( Class<?> dataType )
     {
-        this.opcodeName = opcodeName;
         this.dataType = requireNonNull( dataType, "dataType cannot be null" );
     }
 
@@ -70,69 +56,4 @@ public enum MasterProcessCommand
     {
         return dataType != Void.class;
     }
-
-    public static MasterProcessCommand byOpcode( @Nonnull String opcode )
-    {
-        for ( MasterProcessCommand cmd : values() )
-        {
-            if ( cmd.opcodeName.equals( requireNonNull( opcode ) ) )
-            {
-                return cmd;
-            }
-        }
-        return null;
-    }
-
-    public byte[] encode( String data )
-    {
-        if ( !hasDataType() )
-        {
-            throw new IllegalArgumentException( "cannot use data without data type" );
-        }
-
-        if ( getDataType() != String.class )
-        {
-            throw new IllegalArgumentException( "Data type can be only " + String.class );
-        }
-
-        return encode( opcodeName, data )
-                .toString()
-                .getBytes( US_ASCII );
-    }
-
-    public byte[] encode()
-    {
-        if ( getDataType() != Void.class )
-        {
-            throw new IllegalArgumentException( "Data type can be only " + getDataType() );
-        }
-
-        return encode( opcodeName, null )
-                .toString()
-                .getBytes( US_ASCII );
-    }
-
-    /**
-     * Encodes opcode and data.
-     *
-     * @param operation opcode
-     * @param data   data
-     * @return encoded command
-     */
-    private static StringBuilder encode( String operation, String data )
-    {
-        StringBuilder s = new StringBuilder( 128 )
-            .append( ':' )
-            .append( MAGIC_NUMBER )
-            .append( ':' )
-            .append( operation );
-
-        if ( data != null )
-        {
-            s.append( ':' )
-                    .append( data );
-        }
-
-        return s.append( ':' );
-    }
 }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java
index 9ac1ca5..e8d3efb 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoder.java
@@ -23,6 +23,7 @@ import org.apache.maven.surefire.booter.Command;
 import org.apache.maven.surefire.booter.DumpErrorSingleton;
 import org.apache.maven.surefire.booter.MasterProcessCommand;
 import org.apache.maven.surefire.providerapi.MasterProcessChannelDecoder;
+import org.apache.maven.surefire.util.internal.ImmutableMap;
 
 import javax.annotation.Nonnull;
 import java.io.EOFException;
@@ -30,9 +31,17 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
+import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.MAGIC_NUMBER;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
 
 /**
  * magic number : opcode [: opcode specific data]*
@@ -43,6 +52,8 @@ import static org.apache.maven.surefire.booter.MasterProcessCommand.MAGIC_NUMBER
  */
 public class LegacyMasterProcessChannelDecoder implements MasterProcessChannelDecoder
 {
+    private static final Map<String, MasterProcessCommand> COMMAND_OPCODES = stringsToOpcodes();
+
     private final ReadableByteChannel channel;
 
     public LegacyMasterProcessChannelDecoder( @Nonnull ReadableByteChannel channel )
@@ -108,7 +119,7 @@ public class LegacyMasterProcessChannelDecoder implements MasterProcessChannelDe
 
             if ( completion == FrameCompletion.COMPLETE )
             {
-                MasterProcessCommand cmd = MasterProcessCommand.byOpcode( tokens.get( 1 ) );
+                MasterProcessCommand cmd = COMMAND_OPCODES.get( tokens.get( 1 ) );
                 if ( tokens.size() == 2 )
                 {
                     return new Command( cmd );
@@ -137,7 +148,7 @@ public class LegacyMasterProcessChannelDecoder implements MasterProcessChannelDe
         if ( tokens.size() >= 2 )
         {
             String opcode = tokens.get( 1 );
-            MasterProcessCommand cmd = MasterProcessCommand.byOpcode( opcode );
+            MasterProcessCommand cmd = COMMAND_OPCODES.get( opcode );
             if ( cmd == null )
             {
                 return FrameCompletion.MALFORMED;
@@ -164,4 +175,16 @@ public class LegacyMasterProcessChannelDecoder implements MasterProcessChannelDe
         COMPLETE,
         MALFORMED
     }
+
+    private static Map<String, MasterProcessCommand> stringsToOpcodes()
+    {
+        Map<String, MasterProcessCommand> opcodes = new HashMap<>();
+        opcodes.put( "run-testclass", RUN_CLASS );
+        opcodes.put( "testset-finished", TEST_SET_FINISHED );
+        opcodes.put( "skip-since-next-test", SKIP_SINCE_NEXT_TEST );
+        opcodes.put( "shutdown", SHUTDOWN );
+        opcodes.put( "noop", NOOP );
+        opcodes.put( "bye-ack", BYE_ACK );
+        return new ImmutableMap<>( opcodes );
+    }
 }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
index 30a2c28..edaae08 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
@@ -248,7 +248,7 @@ public class CommandReaderTest
 
     private void addTestToPipeline( String cls )
     {
-        for ( byte cmdByte : MasterProcessCommand.RUN_CLASS.encode( cls ) )
+        for ( byte cmdByte : ( ":maven-surefire-command:run-testclass:" + cls + ":" ).getBytes() )
         {
             blockingStream.add( cmdByte );
         }
@@ -256,7 +256,7 @@ public class CommandReaderTest
 
     private void addEndOfPipeline()
     {
-        for ( byte cmdByte : MasterProcessCommand.TEST_SET_FINISHED.encode() )
+        for ( byte cmdByte : ":maven-surefire-command:testset-finished:".getBytes() )
         {
             blockingStream.add( cmdByte );
         }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoderTest.java
index b17e4c5..029b7ba 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/LegacyMasterProcessChannelDecoderTest.java
@@ -52,11 +52,8 @@ public class LegacyMasterProcessChannelDecoderTest
     public void testDecoderRunClass() throws IOException
     {
         assertEquals( String.class, RUN_CLASS.getDataType() );
-        byte[] encoded = RUN_CLASS.encode( "pkg.Test" );
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:run-testclass:pkg.Test:" );
-        byte[] line = addNL( encoded, '\n' );
-        InputStream is = new ByteArrayInputStream( line );
+        byte[] encoded = ":maven-surefire-command:run-testclass:pkg.Test:\n".getBytes();
+        InputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
         assertThat( command.getCommandType() ).isSameAs( RUN_CLASS );
@@ -69,9 +66,7 @@ public class LegacyMasterProcessChannelDecoderTest
         Command command = Command.TEST_SET_FINISHED;
         assertThat( command.getCommandType() ).isSameAs( TEST_SET_FINISHED );
         assertEquals( Void.class, TEST_SET_FINISHED.getDataType() );
-        byte[] encoded = TEST_SET_FINISHED.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:testset-finished:" );
+        byte[] encoded = ":maven-surefire-command:testset-finished:".getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         command = decoder.decode();
@@ -85,9 +80,7 @@ public class LegacyMasterProcessChannelDecoderTest
         Command command = Command.SKIP_SINCE_NEXT_TEST;
         assertThat( command.getCommandType() ).isSameAs( SKIP_SINCE_NEXT_TEST );
         assertEquals( Void.class, SKIP_SINCE_NEXT_TEST.getDataType() );
-        byte[] encoded = SKIP_SINCE_NEXT_TEST.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:skip-since-next-test:" );
+        byte[] encoded = ":maven-surefire-command:skip-since-next-test:".getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         command = decoder.decode();
@@ -100,9 +93,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         Shutdown shutdownType = EXIT;
         assertEquals( String.class, SHUTDOWN.getDataType() );
-        byte[] encoded = SHUTDOWN.encode( shutdownType.name() );
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:shutdown:" + shutdownType + ":" );
+        byte[] encoded = ( ":maven-surefire-command:shutdown:" + shutdownType + ":" ).getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
@@ -115,9 +106,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         Shutdown shutdownType = KILL;
         assertEquals( String.class, SHUTDOWN.getDataType() );
-        byte[] encoded = SHUTDOWN.encode( shutdownType.name() );
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:shutdown:" + shutdownType + ":" );
+        byte[] encoded = ( ":maven-surefire-command:shutdown:" + shutdownType + ":" ).getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
@@ -130,9 +119,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         Shutdown shutdownType = DEFAULT;
         assertEquals( String.class, SHUTDOWN.getDataType() );
-        byte[] encoded = SHUTDOWN.encode( shutdownType.name() );
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:shutdown:" + shutdownType + ":" );
+        byte[] encoded = ( ":maven-surefire-command:shutdown:" + shutdownType + ":" ).getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
@@ -145,9 +132,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         assertThat( NOOP ).isSameAs( Command.NOOP.getCommandType() );
         assertEquals( Void.class, NOOP.getDataType() );
-        byte[] encoded = NOOP.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:noop:" );
+        byte[] encoded = ":maven-surefire-command:noop:".getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
@@ -160,9 +145,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
         assertEquals( Void.class, BYE_ACK.getDataType() );
-        byte[] encoded = BYE_ACK.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:bye-ack:" );
+        byte[] encoded = ":maven-surefire-command:bye-ack:".getBytes();
         byte[] streamContent = ( "<something>" + new String( encoded ) + "<damaged>" ).getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( streamContent );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
@@ -176,9 +159,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
         assertEquals( Void.class, BYE_ACK.getDataType() );
-        byte[] encoded = BYE_ACK.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:bye-ack:" );
+        byte[] encoded = ":maven-surefire-command:bye-ack:".getBytes();
         byte[] streamContent = ( ":<damaged>:" + new String( encoded ) ).getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( streamContent );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
@@ -192,9 +173,7 @@ public class LegacyMasterProcessChannelDecoderTest
     {
         assertThat( BYE_ACK ).isSameAs( Command.BYE_ACK.getCommandType() );
         assertEquals( Void.class, BYE_ACK.getDataType() );
-        byte[] encoded = BYE_ACK.encode();
-        assertThat( new String( encoded ) )
-            .isEqualTo( ":maven-surefire-command:bye-ack:" );
+        byte[] encoded = ":maven-surefire-command:bye-ack:".getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream( encoded );
         LegacyMasterProcessChannelDecoder decoder = new LegacyMasterProcessChannelDecoder( newChannel( is ) );
         Command command = decoder.decode();
@@ -261,15 +240,4 @@ public class LegacyMasterProcessChannelDecoderTest
         assertThat( command.getCommandType() ).isSameAs( NOOP );
         assertNull( command.getData() );
     }
-
-    private static byte[] addNL( byte[] encoded, char... newLines )
-    {
-        byte[] line = new byte[encoded.length + newLines.length];
-        System.arraycopy( encoded, 0, line, 0, encoded.length );
-        for ( int i = encoded.length, j = 0; i < line.length; i++, j++ )
-        {
-            line[i] = (byte) newLines[j];
-        }
-        return line;
-    }
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
deleted file mode 100644
index 78e5ce0..0000000
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/util/StreamFeeder.java
+++ /dev/null
@@ -1,94 +0,0 @@
-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.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessCommand;
-import org.apache.maven.surefire.extensions.CloseableDaemonThread;
-import org.apache.maven.surefire.extensions.CommandReader;
-
-import javax.annotation.Nonnull;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.NonWritableChannelException;
-import java.nio.channels.WritableByteChannel;
-
-/**
- *
- */
-public class StreamFeeder extends CloseableDaemonThread
-{
-    private final WritableByteChannel channel;
-    private final CommandReader commandReader;
-
-    private volatile boolean disabled;
-    private volatile Throwable exception;
-
-    public StreamFeeder( @Nonnull String threadName, @Nonnull WritableByteChannel channel,
-                         @Nonnull CommandReader commandReader )
-    {
-        super( threadName );
-        this.channel = channel;
-        this.commandReader = commandReader;
-    }
-
-    @Override
-    @SuppressWarnings( "checkstyle:innerassignment" )
-    public void run()
-    {
-        try ( WritableByteChannel c = channel )
-        {
-            for ( Command cmd; ( cmd = commandReader.readNextCommand() ) != null; )
-            {
-                if ( !disabled )
-                {
-                    MasterProcessCommand cmdType = cmd.getCommandType();
-                    byte[] data = cmdType.hasDataType() ? cmdType.encode( cmd.getData() ) : cmdType.encode();
-                    c.write( ByteBuffer.wrap( data ) );
-                }
-            }
-        }
-        catch ( ClosedChannelException e )
-        {
-            // closed externally
-        }
-        catch ( IOException | NonWritableChannelException e )
-        {
-            exception = e.getCause() == null ? e : e.getCause();
-        }
-    }
-
-    public void disable()
-    {
-        disabled = true;
-    }
-
-    public Throwable getException()
-    {
-        return exception;
-    }
-
-    @Override
-    public void close() throws IOException
-    {
-        channel.close();
-    }
-}
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/plugin/surefire/extensions/CommandlineExecutorTest.java
similarity index 80%
rename from surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/CommandlineExecutorTest.java
rename to surefire-extensions-api/src/test/java/org/apache/maven/plugin/surefire/extensions/CommandlineExecutorTest.java
index 15f8ab8..92ee1e3 100644
--- 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/plugin/surefire/extensions/CommandlineExecutorTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.extensions.util;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,9 +19,11 @@ package org.apache.maven.surefire.extensions.util;
  * under the License.
  */
 
-import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.extensions.CommandReader;
 import org.apache.maven.surefire.extensions.EventHandler;
+import org.apache.maven.surefire.extensions.util.CommandlineExecutor;
+import org.apache.maven.surefire.extensions.util.CommandlineStreams;
+import org.apache.maven.surefire.extensions.util.CountdownCloseable;
+import org.apache.maven.surefire.extensions.util.LineConsumerThread;
 import org.apache.maven.surefire.shared.utils.cli.Commandline;
 import org.junit.After;
 import org.junit.Before;
@@ -45,7 +47,7 @@ public class CommandlineExecutorTest
     private CommandlineExecutor exec;
     private CommandlineStreams streams;
     private String baseDir;
-    LineConsumerThread out;
+    private LineConsumerThread out;
 
     @Before
     public void setUp() throws Exception
@@ -92,34 +94,7 @@ public class CommandlineExecutorTest
         streams = exec.execute();
         @SuppressWarnings( "unchecked" )
         EventHandler<String> consumer = mock( EventHandler.class );
-        CommandReader commandReader = new CommandReader()
-        {
-            @Override
-            public Command readNextCommand()
-            {
-                return null;
-            }
-
-            @Override
-            public void close()
-            {
-
-            }
-
-            @Override
-            public boolean isClosed()
-            {
-                return false;
-            }
-
-            @Override
-            public void tryFlush()
-            {
 
-            }
-        };
-        StreamFeeder in = new StreamFeeder( "std-in-fork-1", streams.getStdInChannel(), commandReader );
-        in.start();
         out = new LineConsumerThread( "std-out-fork-1", streams.getStdOutChannel(), consumer, countdownCloseable );
         out.start();
         exec.awaitExit();
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/plugin/surefire/extensions/JUnit4SuiteTest.java
similarity index 95%
rename from surefire-extensions-api/src/test/java/org/apache/maven/surefire/extensions/util/JUnit4SuiteTest.java
rename to surefire-extensions-api/src/test/java/org/apache/maven/plugin/surefire/extensions/JUnit4SuiteTest.java
index df9cca1..a8464a4 100644
--- 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/plugin/surefire/extensions/JUnit4SuiteTest.java
@@ -1,4 +1,4 @@
-package org.apache.maven.surefire.extensions.util;
+package org.apache.maven.plugin.surefire.extensions;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one