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 2021/12/28 21:20:11 UTC

[maven-surefire] branch sync-logs created (now fe34359)

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

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


      at fe34359  synchronized logs

This branch includes the following new commits:

     new fe34359  synchronized logs

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[maven-surefire] 01/01: synchronized logs

Posted by ti...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fe343595b37958903a736fb653533d1dca301a72
Author: Tibor Digana <ti...@gmail.com>
AuthorDate: Tue Dec 28 22:17:57 2021 +0100

    synchronized logs
---
 .../plugin/surefire/booterclient/ForkStarter.java  | 36 ++++++++++++------
 .../surefire/booterclient/output/ForkClient.java   | 19 +++++++---
 .../output/NativeStdErrStreamConsumer.java         |  8 ++--
 .../output/NativeStdOutStreamConsumer.java         |  8 ++--
 .../surefire/extensions/SurefireForkChannel.java   |  2 +-
 .../plugin/surefire/log/PluginConsoleLogger.java   | 20 ++++++----
 .../surefire/report/DefaultReporterFactory.java    |  8 +++-
 .../plugin/surefire/report/ReportsMerger.java      | 36 ++++++++++++++++++
 .../plugin/surefire/report/TestSetRunListener.java | 44 +++++++++++++++++-----
 .../surefire/booterclient/ForkStarterTest.java     | 23 +++++++++--
 .../booterclient/ForkingRunListenerTest.java       |  7 ++++
 .../booterclient/output/ForkClientTest.java        |  8 ++++
 .../maven/plugin/surefire/extensions/E2ETest.java  |  7 ++++
 .../extensions/EventConsumerThreadTest.java        |  8 ++++
 .../extensions/ForkedProcessEventNotifierTest.java |  8 ++++
 .../maven/surefire/extensions/ForkChannelTest.java |  8 ++++
 .../maven/surefire/stream/EventDecoderTest.java    |  8 ++++
 .../maven/surefire/api/fork/ForkNodeArguments.java |  4 ++
 .../api/stream/AbstractStreamDecoderTest.java      |  8 ++++
 .../maven/surefire/booter/ForkedNodeArg.java       | 10 ++++-
 .../booter/spi/CommandChannelDecoderTest.java      |  8 ++++
 21 files changed, 237 insertions(+), 51 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 a999069..adb7040 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
@@ -33,6 +33,8 @@ import org.apache.maven.plugin.surefire.booterclient.output.NativeStdErrStreamCo
 import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
+import org.apache.maven.plugin.surefire.report.ReportsMerger;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 import org.apache.maven.surefire.booter.ProviderConfiguration;
@@ -155,7 +157,7 @@ public class ForkStarter
 
     private final ConsoleLogger log;
 
-    private final DefaultReporterFactory defaultReporterFactory;
+    private final ReportsMerger reportMerger;
 
     private final Collection<DefaultReporterFactory> defaultReporterFactories;
 
@@ -206,7 +208,7 @@ public class ForkStarter
                     // if tests failed, but if this does not happen then printing warning to console is the only way to
                     // inform the users.
                     String msg = "ForkStarter IOException: " + e.getLocalizedMessage() + ".";
-                    File reportsDir = defaultReporterFactory.getReportsDirectory();
+                    File reportsDir = reportMerger.getReportsDirectory();
                     File dump = InPluginProcessDumpSingleton.getSingleton()
                                         .dumpStreamException( e, msg, reportsDir, jvmRun );
                     log.warning( msg + " See the dump file " + dump.getAbsolutePath() );
@@ -247,8 +249,8 @@ public class ForkStarter
         this.startupConfiguration = startupConfiguration;
         this.startupReportConfiguration = startupReportConfiguration;
         this.log = log;
-        defaultReporterFactory = new DefaultReporterFactory( startupReportConfiguration, log );
-        defaultReporterFactory.runStarting();
+        reportMerger = new DefaultReporterFactory( startupReportConfiguration, log );
+        reportMerger.runStarting();
         defaultReporterFactories = new ConcurrentLinkedQueue<>();
         currentForkClients = new ConcurrentLinkedQueue<>();
         timeoutCheckScheduler = createTimeoutCheckScheduler();
@@ -268,8 +270,8 @@ public class ForkStarter
         }
         finally
         {
-            defaultReporterFactory.mergeFromOtherFactories( defaultReporterFactories );
-            defaultReporterFactory.close();
+            reportMerger.mergeFromOtherFactories( defaultReporterFactories );
+            reportMerger.close();
             pingThreadScheduler.shutdownNow();
             timeoutCheckScheduler.shutdownNow();
             for ( String line : logsAtEnd )
@@ -574,8 +576,8 @@ public class ForkStarter
         File dumpLogDir = replaceForkThreadsInPath( startupReportConfiguration.getReportsDirectory(), forkNumber );
         try
         {
-            ForkNodeArguments forkNodeArguments =
-                new ForkedNodeArg( forkConfiguration.isDebug(), forkNumber, dumpLogDir, randomUUID().toString() );
+            ForkNodeArguments forkNodeArguments = new ForkedNodeArg( forkConfiguration.isDebug(), forkNumber,
+                dumpLogDir, randomUUID().toString(), forkClient.getConsoleOutputReceiver() );
             forkChannel = forkNodeFactory.createForkChannel( forkNodeArguments );
             closer.addCloseable( forkChannel );
             tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
@@ -639,7 +641,7 @@ public class ForkStarter
             CommandlineStreams streams = exec.execute();
             closer.addCloseable( streams );
 
-            err = bindErrorStream( forkNumber, countdownCloseable, streams );
+            err = bindErrorStream( forkNumber, countdownCloseable, streams, forkClient.getConsoleOutputReceiver() );
 
             forkChannel.bindCommandReader( commandReader, streams.getStdInChannel() );
 
@@ -754,9 +756,9 @@ public class ForkStarter
     }
 
     private Stoppable bindErrorStream( int forkNumber, CountdownCloseable countdownCloseable,
-                                       CommandlineStreams streams )
+                                       CommandlineStreams streams, ConsoleOutputReceiver consoleOutputReceiver )
     {
-        EventHandler<String> errConsumer = new NativeStdErrStreamConsumer( log );
+        EventHandler<String> errConsumer = new NativeStdErrStreamConsumer( consoleOutputReceiver );
         LineConsumerThread stdErr = new LineConsumerThread( "fork-" + forkNumber + "-err-thread",
             streams.getStdErrChannel(), errConsumer, countdownCloseable );
         stdErr.start();
@@ -888,13 +890,16 @@ public class ForkStarter
         private final File dumpLogDir;
         private final String sessionId;
         private final boolean debug;
+        private final ConsoleOutputReceiver consoleOutputReceiver;
 
-        ForkedNodeArg( boolean debug, int forkChannelId, File dumpLogDir, String sessionId )
+        ForkedNodeArg( boolean debug, int forkChannelId, File dumpLogDir, String sessionId,
+                       ConsoleOutputReceiver consoleOutputReceiver )
         {
             this.debug = debug;
             this.forkChannelId = forkChannelId;
             this.dumpLogDir = dumpLogDir;
             this.sessionId = sessionId;
+            this.consoleOutputReceiver = consoleOutputReceiver;
         }
 
         @Nonnull
@@ -938,6 +943,13 @@ public class ForkStarter
             return log;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return consoleOutputReceiver;
+        }
+
         @Override
         public File getEventStreamBinaryFile()
         {
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 a1e54b1..1011fbf 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
@@ -78,7 +78,7 @@ public class ForkClient
 
     private final int forkNumber;
 
-    private RunListener testSetReporter;
+    private volatile RunListener testSetReporter;
 
     /**
      * Written by one Thread and read by another: Main Thread and ForkStarter's Thread.
@@ -340,7 +340,7 @@ public class ForkClient
     {
         if ( forkedProcessTimeoutInSeconds > 0 )
         {
-            final long forkedProcessTimeoutInMillis = 1000 * forkedProcessTimeoutInSeconds;
+            final long forkedProcessTimeoutInMillis = 1000L * forkedProcessTimeoutInSeconds;
             final long startedAt = testSetStartedAt.get();
             if ( startedAt > START_TIME_ZERO && currentTimeMillis - startedAt >= forkedProcessTimeoutInMillis )
             {
@@ -376,11 +376,20 @@ public class ForkClient
         return testSetStartedAt.get() == START_TIME_NEGATIVE_TIMEOUT;
     }
 
+    /**
+     * Only {@link #getConsoleOutputReceiver()} may call this method in another Thread.
+     */
     private RunListener getTestSetReporter()
     {
         if ( testSetReporter == null )
         {
-            testSetReporter = defaultReporterFactory.createReporter();
+            synchronized ( this )
+            {
+                if ( testSetReporter == null )
+                {
+                    testSetReporter = defaultReporterFactory.createReporter();
+                }
+            }
         }
         return testSetReporter;
     }
@@ -394,7 +403,7 @@ public class ForkClient
 
     private void writeTestOutput( String output, boolean newLine, boolean isStdout )
     {
-        getOrCreateConsoleOutputReceiver()
+        getConsoleOutputReceiver()
                 .writeTestOutput( output, newLine, isStdout );
     }
 
@@ -414,7 +423,7 @@ public class ForkClient
         return getTestSetReporter();
     }
 
-    private ConsoleOutputReceiver getOrCreateConsoleOutputReceiver()
+    public ConsoleOutputReceiver getConsoleOutputReceiver()
     {
         return (ConsoleOutputReceiver) getTestSetReporter();
     }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
index d7136ff..427bd97 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdErrStreamConsumer.java
@@ -19,7 +19,7 @@ package org.apache.maven.plugin.surefire.booterclient.output;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.extensions.EventHandler;
 
 import javax.annotation.Nonnull;
@@ -35,9 +35,9 @@ import javax.annotation.Nonnull;
 public final class NativeStdErrStreamConsumer
     implements EventHandler<String>
 {
-    private final ConsoleLogger logger;
+    private final ConsoleOutputReceiver logger;
 
-    public NativeStdErrStreamConsumer( ConsoleLogger logger )
+    public NativeStdErrStreamConsumer( ConsoleOutputReceiver logger )
     {
         this.logger = logger;
     }
@@ -45,6 +45,6 @@ public final class NativeStdErrStreamConsumer
     @Override
     public void handleEvent( @Nonnull String line )
     {
-        logger.error( line );
+        logger.writeTestOutput( line, true, false );
     }
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
index 0d9cef2..89e165d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/NativeStdOutStreamConsumer.java
@@ -19,7 +19,7 @@ package org.apache.maven.plugin.surefire.booterclient.output;
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.extensions.EventHandler;
 
 import javax.annotation.Nonnull;
@@ -34,9 +34,9 @@ import javax.annotation.Nonnull;
 public final class NativeStdOutStreamConsumer
     implements EventHandler<String>
 {
-    private final ConsoleLogger logger;
+    private final ConsoleOutputReceiver logger;
 
-    public NativeStdOutStreamConsumer( ConsoleLogger logger )
+    public NativeStdOutStreamConsumer( ConsoleOutputReceiver logger )
     {
         this.logger = logger;
     }
@@ -44,6 +44,6 @@ public final class NativeStdOutStreamConsumer
     @Override
     public void handleEvent( @Nonnull String event )
     {
-        logger.info( event );
+        logger.writeTestOutput( event, true, true );
     }
 }
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 e070679..42208c8 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
@@ -143,7 +143,7 @@ final class SurefireForkChannel extends ForkChannel
     {
         ForkNodeArguments args = getArguments();
         out = new LineConsumerThread( "fork-" + args.getForkChannelId() + "-out-thread", stdOut,
-            new NativeStdOutStreamConsumer( args.getConsoleLogger() ), countdown );
+            new NativeStdOutStreamConsumer( args.getConsoleOutputReceiver() ), countdown );
         out.start();
 
         eventBindings = new EventBindings( eventHandler, countdown );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
index 88ac446..e5b4041 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/log/PluginConsoleLogger.java
@@ -25,6 +25,10 @@ import org.codehaus.plexus.logging.Logger;
 /**
  * Wrapper logger of miscellaneous implementations of {@link Logger}.
  *
+ * This instance is synchronized. The logger operations are mutually exclusive to standard out, standard err and console
+ * err/warn/info/debug logger operations, see {@link org.apache.maven.plugin.surefire.report.DefaultReporterFactory},
+ * {@link org.apache.maven.plugin.surefire.report.TestSetRunListener}.
+ *
  * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
  * @since 2.20
  * @see ConsoleLogger
@@ -46,12 +50,12 @@ public final class PluginConsoleLogger
     }
 
     @Override
-    public void debug( String message )
+    public synchronized void debug( String message )
     {
         plexusLogger.debug( message );
     }
 
-    public void debug( CharSequence content, Throwable error )
+    public synchronized void debug( CharSequence content, Throwable error )
     {
         plexusLogger.debug( content == null ? "" : content.toString(), error );
     }
@@ -63,7 +67,7 @@ public final class PluginConsoleLogger
     }
 
     @Override
-    public void info( String message )
+    public synchronized void info( String message )
     {
         plexusLogger.info( message );
     }
@@ -75,12 +79,12 @@ public final class PluginConsoleLogger
     }
 
     @Override
-    public void warning( String message )
+    public synchronized void warning( String message )
     {
         plexusLogger.warn( message );
     }
 
-    public void warning( CharSequence content, Throwable error )
+    public synchronized void warning( CharSequence content, Throwable error )
     {
         plexusLogger.warn( content == null ? "" : content.toString(), error );
     }
@@ -92,19 +96,19 @@ public final class PluginConsoleLogger
     }
 
     @Override
-    public void error( String message )
+    public synchronized void error( String message )
     {
         plexusLogger.error( message );
     }
 
     @Override
-    public void error( String message, Throwable t )
+    public synchronized void error( String message, Throwable t )
     {
         plexusLogger.error( message, t );
     }
 
     @Override
-    public void error( Throwable t )
+    public synchronized void error( Throwable t )
     {
         plexusLogger.error( "", t );
     }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
index 7b0c18d..693e63d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
@@ -65,7 +65,7 @@ import static org.apache.maven.surefire.api.util.internal.ObjectUtils.useNonNull
  * @author Kristian Rosenvold
  */
 public class DefaultReporterFactory
-    implements ReporterFactory
+    implements ReporterFactory, ReportsMerger
 {
     private final Collection<TestSetRunListener> listeners = new ConcurrentLinkedQueue<>();
     private final StartupReportConfiguration reportConfiguration;
@@ -107,11 +107,13 @@ public class DefaultReporterFactory
                                     createStatisticsReporter(),
                                     reportConfiguration.isTrimStackTrace(),
                                     PLAIN.equals( reportConfiguration.getReportFormat() ),
-                                    reportConfiguration.isBriefOrPlainFormat() );
+                                    reportConfiguration.isBriefOrPlainFormat(),
+                                    consoleLogger );
         addListener( testSetRunListener );
         return testSetRunListener;
     }
 
+    @Override
     public File getReportsDirectory()
     {
         return reportConfiguration.getReportsDirectory();
@@ -151,6 +153,7 @@ public class DefaultReporterFactory
         return useNonNull( statisticsReporter, NullStatisticsReporter.INSTANCE );
     }
 
+    @Override
     public void mergeFromOtherFactories( Collection<DefaultReporterFactory> factories )
     {
         for ( DefaultReporterFactory factory : factories )
@@ -176,6 +179,7 @@ public class DefaultReporterFactory
         return globalStats.getRunResult();
     }
 
+    @Override
     public void runStarting()
     {
         if ( reportConfiguration.isPrintSummary() )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ReportsMerger.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ReportsMerger.java
new file mode 100644
index 0000000..8722062
--- /dev/null
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ReportsMerger.java
@@ -0,0 +1,36 @@
+package org.apache.maven.plugin.surefire.report;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.api.suite.RunResult;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * This interface is used to merge reports in {@link org.apache.maven.plugin.surefire.booterclient.ForkStarter}.
+ */
+public interface ReportsMerger
+{
+    void runStarting();
+    void mergeFromOtherFactories( Collection<DefaultReporterFactory> factories );
+    File getReportsDirectory();
+    RunResult close();
+}
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index 15b7ed8..9ab198c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -69,6 +69,8 @@ public class TestSetRunListener
 
     private final StatisticsReporter statisticsReporter;
 
+    private final Object lock;
+
     private Utf8RecodingDeferredFileOutputStream testStdOut = initDeferred( "stdout" );
 
     private Utf8RecodingDeferredFileOutputStream testStdErr = initDeferred( "stderr" );
@@ -83,7 +85,7 @@ public class TestSetRunListener
                                StatelessReportEventListener<WrappedReportEntry, TestSetStats> simpleXMLReporter,
                                ConsoleOutputReportEventListener consoleOutputReceiver,
                                StatisticsReporter statisticsReporter, boolean trimStackTrace,
-                               boolean isPlainFormat, boolean briefOrPlainFormat )
+                               boolean isPlainFormat, boolean briefOrPlainFormat, Object lock )
     {
         this.consoleReporter = consoleReporter;
         this.fileReporter = fileReporter;
@@ -92,6 +94,7 @@ public class TestSetRunListener
         this.consoleOutputReceiver = consoleOutputReceiver;
         this.briefOrPlainFormat = briefOrPlainFormat;
         detailsForThis = new TestSetStats( trimStackTrace, isPlainFormat );
+        this.lock = lock;
     }
 
     @Override
@@ -103,7 +106,10 @@ public class TestSetRunListener
     @Override
     public void debug( String message )
     {
-        consoleReporter.getConsoleLogger().debug( trimTrailingNewLine( message ) );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().debug( trimTrailingNewLine( message ) );
+        }
     }
 
     @Override
@@ -115,7 +121,10 @@ public class TestSetRunListener
     @Override
     public void info( String message )
     {
-        consoleReporter.getConsoleLogger().info( trimTrailingNewLine( message ) );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().info( trimTrailingNewLine( message ) );
+        }
     }
 
     @Override
@@ -127,7 +136,10 @@ public class TestSetRunListener
     @Override
     public void warning( String message )
     {
-        consoleReporter.getConsoleLogger().warning( trimTrailingNewLine( message ) );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().warning( trimTrailingNewLine( message ) );
+        }
     }
 
     @Override
@@ -139,19 +151,28 @@ public class TestSetRunListener
     @Override
     public void error( String message )
     {
-        consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ) );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ) );
+        }
     }
 
     @Override
     public void error( String message, Throwable t )
     {
-        consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ), t );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().error( trimTrailingNewLine( message ), t );
+        }
     }
 
     @Override
     public void error( Throwable t )
     {
-        consoleReporter.getConsoleLogger().error( t );
+        synchronized ( lock )
+        {
+            consoleReporter.getConsoleLogger().error( t );
+        }
     }
 
     @Override
@@ -159,9 +180,12 @@ public class TestSetRunListener
     {
         try
         {
-            Utf8RecodingDeferredFileOutputStream stream = stdout ? testStdOut : testStdErr;
-            stream.write( output, newLine );
-            consoleOutputReceiver.writeTestOutput( output, newLine, stdout );
+            synchronized ( lock )
+            {
+                Utf8RecodingDeferredFileOutputStream stream = stdout ? testStdOut : testStdErr;
+                stream.write( output, newLine );
+                consoleOutputReceiver.writeTestOutput( output, newLine, stdout );
+            }
         }
         catch ( IOException e )
         {
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
index b1e1c91..b730336 100644
--- 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
@@ -28,6 +28,9 @@ import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessIn
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
 import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
+import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
+import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
@@ -155,15 +158,21 @@ public class ForkStarterTest
         when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
             .thenReturn( cli );
 
+        SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
+        SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
+        SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter( true, "3" );
+
         StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
-            false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
+            false, tmp, true, "", null, false, 0, null, null, true,
+            xmlReporter, outputReporter, statelessTestsetInfoReporter );
 
         ConsoleLogger logger = mock( ConsoleLogger.class );
 
         ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
             0, startupReportConfiguration, logger );
 
-        DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
+        DefaultReporterFactory reporterFactory =
+            new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
 
         e.expect( SurefireBooterForkException.class );
         e.expectMessage( containsString( "Process Exit Code: 1" ) );
@@ -214,15 +223,21 @@ public class ForkStarterTest
         when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
             .thenReturn( cli );
 
+        SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
+        SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
+        SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter( true, "3" );
+
         StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
-            false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
+            false, tmp, true, "", null, false, 0, null, null, true,
+            xmlReporter, outputReporter, statelessTestsetInfoReporter );
 
         ConsoleLogger logger = mock( ConsoleLogger.class );
 
         ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
             0, startupReportConfiguration, logger );
 
-        DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
+        DefaultReporterFactory reporterFactory =
+            new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
 
         Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
             int.class, AbstractCommandReader.class, ForkNodeFactory.class, boolean.class};
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
index 48eb35f..3d18afd 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
@@ -371,6 +371,13 @@ public class ForkingRunListenerTest
             return logger;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            throw new UnsupportedOperationException();
+        }
+
         boolean isCalled()
         {
             return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java
index a0b29cd..0b9c80d 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClientTest.java
@@ -46,6 +46,7 @@ import org.apache.maven.surefire.api.event.TestStartingEvent;
 import org.apache.maven.surefire.api.event.TestSucceededEvent;
 import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
 import org.apache.maven.surefire.api.event.TestsetStartingEvent;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.SafeThrowable;
 import org.apache.maven.surefire.api.report.SimpleReportEntry;
@@ -1880,6 +1881,13 @@ public class ForkClientTest
             return logger;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            throw new UnsupportedOperationException();
+        }
+
         boolean isCalled()
         {
             return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
index a9fde90..45a39df 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
@@ -431,6 +431,13 @@ public class E2ETest
             return logger;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return null;
+        }
+
         @Override
         public File getEventStreamBinaryFile()
         {
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java
index 0204532..4a43659 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/EventConsumerThreadTest.java
@@ -22,6 +22,7 @@ package org.apache.maven.plugin.surefire.extensions;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.extensions.EventHandler;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
 import org.junit.Test;
@@ -229,6 +230,13 @@ public class EventConsumerThreadTest
             return null;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return null;
+        }
+
         @Override
         public File getEventStreamBinaryFile()
         {
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java
index 9094392..b6fa15e 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/ForkedProcessEventNotifierTest.java
@@ -32,6 +32,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
 import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.SafeThrowable;
@@ -1304,6 +1305,13 @@ public class ForkedProcessEventNotifierTest
             return logger;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return null;
+        }
+
         boolean isCalled()
         {
             return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
index 9655c63..0391874 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
@@ -27,6 +27,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.event.ControlByeEvent;
 import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
 import org.junit.Test;
 
@@ -118,6 +119,13 @@ public class ForkChannelTest
             {
                 return reporter;
             }
+
+            @Nonnull
+            @Override
+            public ConsoleOutputReceiver getConsoleOutputReceiver()
+            {
+                return null;
+            }
         };
 
         ForkNodeFactory factory = new SurefireForkNodeFactory();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
index ccfd3f0..395600f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/stream/EventDecoderTest.java
@@ -44,6 +44,7 @@ import org.apache.maven.surefire.api.event.TestSucceededEvent;
 import org.apache.maven.surefire.api.event.TestsetCompletedEvent;
 import org.apache.maven.surefire.api.event.TestsetStartingEvent;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.api.report.ReportEntry;
 import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.report.SafeThrowable;
@@ -769,6 +770,13 @@ public class EventDecoderTest
             return null;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return null;
+        }
+
         @Override
         public File getEventStreamBinaryFile()
         {
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
index 7d20e0e..e78b283 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/fork/ForkNodeArguments.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.api.fork;
  */
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
@@ -55,6 +56,9 @@ public interface ForkNodeArguments
     @Nonnull
     ConsoleLogger getConsoleLogger();
 
+    @Nonnull
+    ConsoleOutputReceiver getConsoleOutputReceiver();
+
     File getEventStreamBinaryFile();
 
     File getCommandStreamBinaryFile();
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
index 748c467..3dc59ad 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoderTest.java
@@ -24,6 +24,7 @@ import org.apache.maven.surefire.api.booter.Constants;
 import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
 import org.apache.maven.surefire.api.event.Event;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.api.report.RunMode;
 import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.MalformedFrameException;
 import org.apache.maven.surefire.api.stream.AbstractStreamDecoder.Memento;
@@ -635,6 +636,13 @@ public class AbstractStreamDecoderTest
             return null;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            return null;
+        }
+
         @Override
         public File getEventStreamBinaryFile()
         {
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java
index 4bed5ca..3660174 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedNodeArg.java
@@ -23,6 +23,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
 import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 
 import javax.annotation.Nonnull;
 import java.io.File;
@@ -83,10 +84,17 @@ public final class ForkedNodeArg implements ForkNodeArguments
         return logger;
     }
 
+    @Nonnull
+    @Override
+    public ConsoleOutputReceiver getConsoleOutputReceiver()
+    {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public File getEventStreamBinaryFile()
     {
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     @Override
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
index 8ac1829..1f7a061 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/CommandChannelDecoderTest.java
@@ -24,6 +24,7 @@ import org.apache.maven.surefire.api.booter.Command;
 import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
 import org.apache.maven.surefire.api.booter.Shutdown;
 import org.apache.maven.surefire.api.fork.ForkNodeArguments;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.booter.ForkedNodeArg;
 import org.junit.Before;
 import org.junit.Rule;
@@ -400,6 +401,13 @@ public class CommandChannelDecoderTest
             return logger;
         }
 
+        @Nonnull
+        @Override
+        public ConsoleOutputReceiver getConsoleOutputReceiver()
+        {
+            throw new UnsupportedOperationException();
+        }
+
         boolean isCalled()
         {
             return !dumpStreamText.isEmpty() || !logWarningAtEnd.isEmpty();