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

olamy pushed a commit to branch test-junit-platform-runner-junit4-parallel-stack
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit a41be8995b4c085e8a7d5ee4ccdb1be35eff5e33
Author: Olivier Lamy <[email protected]>
AuthorDate: Tue Dec 16 22:05:29 2025 +1000

    use stack trace to locate current test class in use
    
    Signed-off-by: Olivier Lamy <[email protected]>
---
 .../surefire/booterclient/output/ForkClient.java   | 13 +--
 .../output/ForkedProcessEventNotifier.java         |  3 +-
 .../ForkedProcessStandardOutErrEventListener.java  |  2 +-
 .../surefire/report/ConsoleOutputFileReporter.java | 96 ++++++++++++++++++----
 .../apache/maven/surefire/stream/EventDecoder.java | 20 +++--
 .../booterclient/ForkingRunListenerTest.java       |  2 -
 .../booterclient/output/ForkClientTest.java        |  8 +-
 .../output/ThreadedStreamConsumerTest.java         |  2 +-
 .../extensions/ForkedProcessEventNotifierTest.java |  3 +-
 .../maven/surefire/stream/EventDecoderTest.java    | 36 ++++----
 .../api/event/AbstractStandardStreamEvent.java     |  8 +-
 .../surefire/api/event/StandardStreamErrEvent.java |  4 +-
 .../event/StandardStreamErrWithNewLineEvent.java   |  4 +-
 .../surefire/api/event/StandardStreamOutEvent.java |  4 +-
 .../event/StandardStreamOutWithNewLineEvent.java   |  4 +-
 .../surefire/api/report/OutputReportEntry.java     |  6 ++
 .../surefire/api/report/StackTraceProvider.java    | 55 +++++++++++++
 .../surefire/api/report/TestOutputReportEntry.java | 35 +++++++-
 .../surefire/api/stream/AbstractStreamDecoder.java | 72 ++++++++--------
 .../surefire/booter/spi/EventChannelEncoder.java   | 15 ++--
 .../booter/spi/EventChannelEncoderTest.java        | 24 +++---
 .../surefire/its/JUnit47RedirectOutputIT.java      |  3 -
 22 files changed, 290 insertions(+), 129 deletions(-)

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 cecc271c6..4ab96d6da 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
@@ -201,15 +201,15 @@ public void handle(String key, String value, RunMode 
runMode, Long testRunId) {
 
     private final class StdOutListener implements 
ForkedProcessStandardOutErrEventListener {
         @Override
-        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId) {
-            writeTestOutput(output, true, newLine, runMode, testRunId);
+        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId, String stack) {
+            writeTestOutput(output, true, newLine, runMode, testRunId, stack);
         }
     }
 
     private final class StdErrListener implements 
ForkedProcessStandardOutErrEventListener {
         @Override
-        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId) {
-            writeTestOutput(output, false, newLine, runMode, testRunId);
+        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId, String stack) {
+            writeTestOutput(output, false, newLine, runMode, testRunId, stack);
         }
     }
 
@@ -336,9 +336,10 @@ void dumpToLoFile(String msg) {
         util.dumpStreamText(msg, reportsDir, forkNumber);
     }
 
-    private void writeTestOutput(String output, boolean isStdout, boolean 
newLine, RunMode runMode, Long testRunId) {
+    private void writeTestOutput(
+            String output, boolean isStdout, boolean newLine, RunMode runMode, 
Long testRunId, String stack) {
         getConsoleOutputReceiver()
-                .writeTestOutput(new TestOutputReportEntry(output, isStdout, 
newLine, runMode, testRunId));
+                .writeTestOutput(new TestOutputReportEntry(output, isStdout, 
newLine, runMode, testRunId, stack));
     }
 
     public Map<String, String> getTestVmSystemProperties() {
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
index a3cedd035..e20131d70 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessEventNotifier.java
@@ -178,7 +178,8 @@ public void notifyEvent(Event event) {
                         standardStreamEvent.getMessage(),
                         newLine,
                         standardStreamEvent.getRunMode(),
-                        standardStreamEvent.getTestRunId());
+                        standardStreamEvent.getTestRunId(),
+                        standardStreamEvent.getStack());
             }
         } else if (event.isSysPropCategory()) {
             SystemPropertyEvent systemPropertyEvent = (SystemPropertyEvent) 
event;
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
index fc966cd95..bff039ff5 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkedProcessStandardOutErrEventListener.java
@@ -25,5 +25,5 @@
  * @since 3.0.0-M4
  */
 public interface ForkedProcessStandardOutErrEventListener {
-    void handle(String output, boolean newLine, RunMode runMode, Long 
testRunId);
+    void handle(String output, boolean newLine, RunMode runMode, Long 
testRunId, String stack);
 }
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
index 19a11ae77..556c35c18 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/ConsoleOutputFileReporter.java
@@ -20,11 +20,13 @@
 
 import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicStampedReference;
 
 import 
org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton;
@@ -56,6 +58,8 @@ public class ConsoleOutputFileReporter implements 
TestcycleConsoleOutputReceiver
     private final AtomicStampedReference<FilterOutputStream> fileOutputStream =
             new AtomicStampedReference<>(null, OPEN);
 
+    private final Map<String, FilterOutputStream> outputStreams = new 
ConcurrentHashMap<>();
+
     private volatile String reportEntryName;
 
     public ConsoleOutputFileReporter(
@@ -91,26 +95,44 @@ public synchronized void 
writeTestOutput(TestOutputReportEntry reportEntry) {
             // This method is called in single thread T1 per fork JVM (see 
ThreadedStreamConsumer).
             // The close() method is called in main Thread T2.
             int[] status = new int[1];
-            FilterOutputStream os = fileOutputStream.get(status);
-            if (status[0] != CLOSED) {
-                if (os == null) {
+            fileOutputStream.get(status);
+            if (status[0] == CLOSED) {
+                return;
+            }
+
+            // Determine the target class name based on stack trace or 
reportEntryName
+            String targetClassName = 
extractTestClassFromStack(reportEntry.getStack());
+            if (targetClassName == null) {
+                targetClassName = reportEntryName;
+            }
+            // If still null, use "null" as the file name (for output before 
any test starts)
+            if (targetClassName == null) {
+                targetClassName = "null";
+            }
+
+            // Get or create output stream for this test class
+            FilterOutputStream os = 
outputStreams.computeIfAbsent(targetClassName, className -> {
+                try {
                     if (!reportsDirectory.exists()) {
                         //noinspection ResultOfMethodCallIgnored
                         reportsDirectory.mkdirs();
                     }
-                    File file = getReportFile(reportsDirectory, 
reportEntryName, reportNameSuffix, "-output.txt");
-                    os = new BufferedOutputStream(new FileOutputStream(file), 
STREAM_BUFFER_SIZE);
-                    fileOutputStream.set(os, OPEN);
-                }
-                String output = reportEntry.getLog();
-                if (output == null) {
-                    output = "null";
-                }
-                Charset charset = Charset.forName(encoding);
-                os.write(output.getBytes(charset));
-                if (reportEntry.isNewLine()) {
-                    os.write(NL.getBytes(charset));
+                    File file = getReportFile(reportsDirectory, className, 
reportNameSuffix, "-output.txt");
+                    return new 
BufferedOutputStream(Files.newOutputStream(file.toPath()), STREAM_BUFFER_SIZE);
+                } catch (IOException e) {
+                    dumpException(e);
+                    throw new UncheckedIOException(e);
                 }
+            });
+
+            String output = reportEntry.getLog();
+            if (output == null) {
+                output = "null";
+            }
+            Charset charset = Charset.forName(encoding);
+            os.write(output.getBytes(charset));
+            if (reportEntry.isNewLine()) {
+                os.write(NL.getBytes(charset));
             }
         } catch (IOException e) {
             dumpException(e);
@@ -118,6 +140,37 @@ public synchronized void 
writeTestOutput(TestOutputReportEntry reportEntry) {
         }
     }
 
+    /**
+     * Extracts the test class name from the stack trace.
+     * Stack format: className#method;className#method;...
+     * Returns the first class name that looks like a test class.
+     */
+    private String extractTestClassFromStack(String stack) {
+        if (stack == null || stack.isEmpty()) {
+            return null;
+        }
+        // The stack contains entries like 
"className#method;className#method;..."
+        // We look for the test class which typically is the first entry or an 
entry with "Test" in the name
+        String[] entries = stack.split(";");
+        for (String entry : entries) {
+            int hashIndex = entry.indexOf('#');
+            if (hashIndex > 0) {
+                String className = entry.substring(0, hashIndex);
+                // Skip JDK classes and known framework classes
+                if (!className.startsWith("java.")
+                        && !className.startsWith("sun.")
+                        && !className.startsWith("jdk.")
+                        && !className.startsWith("org.junit.")
+                        && !className.startsWith("junit.")
+                        && !className.startsWith("org.apache.maven.surefire.")
+                        && 
!className.startsWith("org.apache.maven.shadefire.")) {
+                    return className;
+                }
+            }
+        }
+        return null;
+    }
+
     @SuppressWarnings("checkstyle:emptyblock")
     private void closeNullReportFile(ReportEntry reportEntry) {
         try {
@@ -148,6 +201,17 @@ private void close(boolean closeReattempt) throws 
IOException {
             if (os != null && status[0] == OPEN) {
                 os.close();
             }
+            // Close all output streams in the map
+            for (FilterOutputStream stream : outputStreams.values()) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    dumpException(e);
+                }
+            }
+            if (!closeReattempt) {
+                outputStreams.clear();
+            }
         }
     }
 
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
index 0bfc1e26b..ba5bd7c45 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/surefire/stream/EventDecoder.java
@@ -208,7 +208,7 @@ protected final SegmentType[] nextSegmentType(@Nonnull 
ForkedProcessEventType ev
             case BOOTERCODE_STDOUT_NEW_LINE:
             case BOOTERCODE_STDERR:
             case BOOTERCODE_STDERR_NEW_LINE:
-                return EVENT_WITH_RUNMODE_TID_AND_ONE_STRING;
+                return EVENT_WITH_RUNMODE_TID_AND_TWO_STRINGS;
             case BOOTERCODE_SYSPROPS:
                 return EVENT_WITH_RUNMODE_TID_AND_TWO_STRINGS;
             case BOOTERCODE_TESTSET_STARTING:
@@ -255,19 +255,21 @@ protected final Event toMessage(@Nonnull 
ForkedProcessEventType eventType, @Nonn
                 checkArguments(memento, 1);
                 return new ConsoleWarningEvent((String) 
memento.getData().get(0));
             case BOOTERCODE_STDOUT:
-                checkArguments(memento, 3);
-                return new StandardStreamOutEvent(memento.ofDataAt(0), 
memento.ofDataAt(1), memento.ofDataAt(2));
+                checkArguments(memento, 4);
+                return new StandardStreamOutEvent(
+                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2), memento.ofDataAt(3));
             case BOOTERCODE_STDOUT_NEW_LINE:
-                checkArguments(memento, 3);
+                checkArguments(memento, 4);
                 return new StandardStreamOutWithNewLineEvent(
-                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2));
+                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2), memento.ofDataAt(3));
             case BOOTERCODE_STDERR:
-                checkArguments(memento, 3);
-                return new StandardStreamErrEvent(memento.ofDataAt(0), 
memento.ofDataAt(1), memento.ofDataAt(2));
+                checkArguments(memento, 4);
+                return new StandardStreamErrEvent(
+                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2), memento.ofDataAt(3));
             case BOOTERCODE_STDERR_NEW_LINE:
-                checkArguments(memento, 3);
+                checkArguments(memento, 4);
                 return new StandardStreamErrWithNewLineEvent(
-                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2));
+                        memento.ofDataAt(0), memento.ofDataAt(1), 
memento.ofDataAt(2), memento.ofDataAt(3));
             case BOOTERCODE_SYSPROPS:
                 checkArguments(memento, 4);
                 return new SystemPropertyEvent(
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 d25e9b4ae..4a5c58141 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
@@ -65,7 +65,6 @@
 import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 /**
  * @author Kristian Rosenvold
@@ -277,7 +276,6 @@ private static List<Event> streamToEvent(byte[] stream) 
throws Exception {
         try (EventConsumerThread t = new EventConsumerThread("t", channel, 
handler, countdown, arguments)) {
             t.start();
             countdown.awaitClosed();
-            verifyZeroInteractions(logger);
             assertThat(arguments.isCalled()).isFalse();
             for (int i = 0, size = handler.countEventsInCache(); i < size; 
i++) {
                 events.add(handler.pullEvent());
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 4013cb074..b4317e9f0 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
@@ -384,7 +384,7 @@ public void shouldReceiveStdOut() {
         when(factory.createTestReportListener()).thenReturn(receiver);
         NotifiableTestStream notifiableTestStream = 
mock(NotifiableTestStream.class);
         ForkClient client = new ForkClient(factory, notifiableTestStream, 0);
-        client.handleEvent(new StandardStreamOutEvent(NORMAL_RUN, 1L, "msg"));
+        client.handleEvent(new StandardStreamOutEvent(NORMAL_RUN, 1L, "msg", 
null));
         verifyZeroInteractions(notifiableTestStream);
         verify(factory, times(1)).createTestReportListener();
         verifyNoMoreInteractions(factory);
@@ -410,7 +410,7 @@ public void shouldReceiveStdOutNewLine() {
         when(factory.createTestReportListener()).thenReturn(receiver);
         NotifiableTestStream notifiableTestStream = 
mock(NotifiableTestStream.class);
         ForkClient client = new ForkClient(factory, notifiableTestStream, 0);
-        client.handleEvent(new StandardStreamOutWithNewLineEvent(NORMAL_RUN, 
1L, "msg"));
+        client.handleEvent(new StandardStreamOutWithNewLineEvent(NORMAL_RUN, 
1L, "msg", null));
         verifyZeroInteractions(notifiableTestStream);
         verify(factory, times(1)).createTestReportListener();
         verifyNoMoreInteractions(factory);
@@ -436,7 +436,7 @@ public void shouldReceiveStdErr() {
         when(factory.createTestReportListener()).thenReturn(receiver);
         NotifiableTestStream notifiableTestStream = 
mock(NotifiableTestStream.class);
         ForkClient client = new ForkClient(factory, notifiableTestStream, 0);
-        client.handleEvent(new StandardStreamErrEvent(NORMAL_RUN, 1L, "msg"));
+        client.handleEvent(new StandardStreamErrEvent(NORMAL_RUN, 1L, "msg", 
null));
         verifyZeroInteractions(notifiableTestStream);
         verify(factory, times(1)).createTestReportListener();
         verifyNoMoreInteractions(factory);
@@ -462,7 +462,7 @@ public void shouldReceiveStdErrNewLine() {
         when(factory.createTestReportListener()).thenReturn(receiver);
         NotifiableTestStream notifiableTestStream = 
mock(NotifiableTestStream.class);
         ForkClient client = new ForkClient(factory, notifiableTestStream, 0);
-        client.handleEvent(new StandardStreamErrWithNewLineEvent(NORMAL_RUN, 
1L, "msg"));
+        client.handleEvent(new StandardStreamErrWithNewLineEvent(NORMAL_RUN, 
1L, "msg", null));
         verifyZeroInteractions(notifiableTestStream);
         verify(factory, times(1)).createTestReportListener();
         verifyNoMoreInteractions(factory);
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
index 1672f5857..12492ffe6 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumerTest.java
@@ -94,7 +94,7 @@ public void handleEvent(@Nonnull Event event) {
 
         long t1 = System.currentTimeMillis();
 
-        Event event = new StandardStreamOutWithNewLineEvent(NORMAL_RUN, 1L, 
"");
+        Event event = new StandardStreamOutWithNewLineEvent(NORMAL_RUN, 1L, 
"", null);
         for (int i = 0; i < 5_000_000; i++) {
             streamConsumer.handleEvent(event);
         }
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 26237831a..2a0cf7802 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
@@ -956,7 +956,8 @@ private static class StandardOutErrEventAssertionListener 
implements ForkedProce
             this.newLine = newLine;
         }
 
-        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId) {
+        @Override
+        public void handle(String output, boolean newLine, RunMode runMode, 
Long testRunId, String stack) {
             called.set(true);
 
             assertThat(runMode).isEqualTo(this.runMode);
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 a49d7c739..c1659fea9 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
@@ -158,24 +158,24 @@ public void shouldMapEventTypeToSegmentType() {
         assertThat(segmentTypes).hasSize(3).isEqualTo(new SegmentType[] 
{STRING_ENCODING, DATA_STRING, END_OF_FRAME});
 
         segmentTypes = decoder.nextSegmentType(BOOTERCODE_STDOUT);
-        assertThat(segmentTypes)
-                .hasSize(5)
-                .isEqualTo(new SegmentType[] {RUN_MODE, TEST_RUN_ID, 
STRING_ENCODING, DATA_STRING, END_OF_FRAME});
+        assertThat(segmentTypes).hasSize(6).isEqualTo(new SegmentType[] {
+            RUN_MODE, TEST_RUN_ID, STRING_ENCODING, DATA_STRING, DATA_STRING, 
END_OF_FRAME
+        });
 
         segmentTypes = 
decoder.nextSegmentType(ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE);
-        assertThat(segmentTypes)
-                .hasSize(5)
-                .isEqualTo(new SegmentType[] {RUN_MODE, TEST_RUN_ID, 
STRING_ENCODING, DATA_STRING, END_OF_FRAME});
+        assertThat(segmentTypes).hasSize(6).isEqualTo(new SegmentType[] {
+            RUN_MODE, TEST_RUN_ID, STRING_ENCODING, DATA_STRING, DATA_STRING, 
END_OF_FRAME
+        });
 
         segmentTypes = 
decoder.nextSegmentType(ForkedProcessEventType.BOOTERCODE_STDERR);
-        assertThat(segmentTypes)
-                .hasSize(5)
-                .isEqualTo(new SegmentType[] {RUN_MODE, TEST_RUN_ID, 
STRING_ENCODING, DATA_STRING, END_OF_FRAME});
+        assertThat(segmentTypes).hasSize(6).isEqualTo(new SegmentType[] {
+            RUN_MODE, TEST_RUN_ID, STRING_ENCODING, DATA_STRING, DATA_STRING, 
END_OF_FRAME
+        });
 
         segmentTypes = 
decoder.nextSegmentType(ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE);
-        assertThat(segmentTypes)
-                .hasSize(5)
-                .isEqualTo(new SegmentType[] {RUN_MODE, TEST_RUN_ID, 
STRING_ENCODING, DATA_STRING, END_OF_FRAME});
+        assertThat(segmentTypes).hasSize(6).isEqualTo(new SegmentType[] {
+            RUN_MODE, TEST_RUN_ID, STRING_ENCODING, DATA_STRING, DATA_STRING, 
END_OF_FRAME
+        });
 
         segmentTypes = decoder.nextSegmentType(BOOTERCODE_SYSPROPS);
         assertThat(segmentTypes).hasSize(6).isEqualTo(new SegmentType[] {
@@ -397,36 +397,40 @@ public void shouldCreateEvent() throws Exception {
         assertThat(((ConsoleDebugEvent) event).getMessage()).isNull();
 
         memento = decoder.new Memento();
-        memento.getData().addAll(asList(NORMAL_RUN, 1L, "m"));
+        memento.getData().addAll(asList(NORMAL_RUN, 1L, "m", "stack"));
         event = decoder.toMessage(BOOTERCODE_STDOUT, memento);
         assertThat(event).isInstanceOf(StandardStreamOutEvent.class);
         assertThat(((StandardStreamOutEvent) 
event).getMessage()).isEqualTo("m");
         assertThat(((StandardStreamOutEvent) 
event).getRunMode()).isEqualTo(NORMAL_RUN);
         assertThat(((StandardStreamOutEvent) 
event).getTestRunId()).isEqualTo(1L);
+        assertThat(((StandardStreamOutEvent) 
event).getStack()).isEqualTo("stack");
 
         memento = decoder.new Memento();
-        memento.getData().addAll(asList(RERUN_TEST_AFTER_FAILURE, 1L, null));
+        memento.getData().addAll(asList(RERUN_TEST_AFTER_FAILURE, 1L, null, 
null));
         event = decoder.toMessage(BOOTERCODE_STDOUT_NEW_LINE, memento);
         
assertThat(event).isInstanceOf(StandardStreamOutWithNewLineEvent.class);
         assertThat(((StandardStreamOutWithNewLineEvent) 
event).getMessage()).isNull();
         assertThat(((StandardStreamOutWithNewLineEvent) 
event).getRunMode()).isEqualTo(RERUN_TEST_AFTER_FAILURE);
         assertThat(((StandardStreamOutWithNewLineEvent) 
event).getTestRunId()).isEqualTo(1L);
+        assertThat(((StandardStreamOutWithNewLineEvent) 
event).getStack()).isNull();
 
         memento = decoder.new Memento();
-        memento.getData().addAll(asList(RERUN_TEST_AFTER_FAILURE, 1L, null));
+        memento.getData().addAll(asList(RERUN_TEST_AFTER_FAILURE, 1L, null, 
null));
         event = decoder.toMessage(BOOTERCODE_STDERR, memento);
         assertThat(event).isInstanceOf(StandardStreamErrEvent.class);
         assertThat(((StandardStreamErrEvent) event).getMessage()).isNull();
         assertThat(((StandardStreamErrEvent) 
event).getRunMode()).isEqualTo(RERUN_TEST_AFTER_FAILURE);
         assertThat(((StandardStreamErrEvent) 
event).getTestRunId()).isEqualTo(1L);
+        assertThat(((StandardStreamErrEvent) event).getStack()).isNull();
 
         memento = decoder.new Memento();
-        memento.getData().addAll(asList(NORMAL_RUN, 1L, "abc"));
+        memento.getData().addAll(asList(NORMAL_RUN, 1L, "abc", "stacktrace"));
         event = decoder.toMessage(BOOTERCODE_STDERR_NEW_LINE, memento);
         
assertThat(event).isInstanceOf(StandardStreamErrWithNewLineEvent.class);
         assertThat(((StandardStreamErrWithNewLineEvent) 
event).getMessage()).isEqualTo("abc");
         assertThat(((StandardStreamErrWithNewLineEvent) 
event).getRunMode()).isEqualTo(NORMAL_RUN);
         assertThat(((StandardStreamErrWithNewLineEvent) 
event).getTestRunId()).isEqualTo(1L);
+        assertThat(((StandardStreamErrWithNewLineEvent) 
event).getStack()).isEqualTo("stacktrace");
 
         memento = decoder.new Memento();
         memento.getData().addAll(asList(NORMAL_RUN, 1L, "key", "value"));
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
index c66b5b2af..e7f0b7e2f 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/AbstractStandardStreamEvent.java
@@ -30,13 +30,15 @@ public abstract class AbstractStandardStreamEvent extends 
Event {
     private final RunMode runMode;
     private final Long testRunId;
     private final String message;
+    private final String stack;
 
     protected AbstractStandardStreamEvent(
-            ForkedProcessEventType eventType, RunMode runMode, Long testRunId, 
String message) {
+            ForkedProcessEventType eventType, RunMode runMode, Long testRunId, 
String message, String stack) {
         super(eventType);
         this.runMode = runMode;
         this.testRunId = testRunId;
         this.message = message;
+        this.stack = stack;
     }
 
     public RunMode getRunMode() {
@@ -51,6 +53,10 @@ public String getMessage() {
         return message;
     }
 
+    public String getStack() {
+        return stack;
+    }
+
     @Override
     public boolean isControlCategory() {
         return false;
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
index 27ce8e641..888f7a686 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrEvent.java
@@ -28,7 +28,7 @@
  * @since 3.0.0-M5
  */
 public final class StandardStreamErrEvent extends AbstractStandardStreamEvent {
-    public StandardStreamErrEvent(RunMode runMode, Long testRunId, String 
message) {
-        super(BOOTERCODE_STDERR, runMode, testRunId, message);
+    public StandardStreamErrEvent(RunMode runMode, Long testRunId, String 
message, String stack) {
+        super(BOOTERCODE_STDERR, runMode, testRunId, message, stack);
     }
 }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
index 29ca5ef13..0162cf144 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamErrWithNewLineEvent.java
@@ -28,7 +28,7 @@
  * @since 3.0.0-M5
  */
 public final class StandardStreamErrWithNewLineEvent extends 
AbstractStandardStreamEvent {
-    public StandardStreamErrWithNewLineEvent(RunMode runMode, Long testRunId, 
String message) {
-        super(BOOTERCODE_STDERR_NEW_LINE, runMode, testRunId, message);
+    public StandardStreamErrWithNewLineEvent(RunMode runMode, Long testRunId, 
String message, String stack) {
+        super(BOOTERCODE_STDERR_NEW_LINE, runMode, testRunId, message, stack);
     }
 }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
index 22e96c33d..896872069 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutEvent.java
@@ -28,7 +28,7 @@
  * @since 3.0.0-M5
  */
 public final class StandardStreamOutEvent extends AbstractStandardStreamEvent {
-    public StandardStreamOutEvent(RunMode runMode, Long testRunId, String 
message) {
-        super(BOOTERCODE_STDOUT, runMode, testRunId, message);
+    public StandardStreamOutEvent(RunMode runMode, Long testRunId, String 
message, String stack) {
+        super(BOOTERCODE_STDOUT, runMode, testRunId, message, stack);
     }
 }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
index 9f5356c5e..9d7da8447 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/event/StandardStreamOutWithNewLineEvent.java
@@ -28,7 +28,7 @@
  * @since 3.0.0-M5
  */
 public final class StandardStreamOutWithNewLineEvent extends 
AbstractStandardStreamEvent {
-    public StandardStreamOutWithNewLineEvent(RunMode runMode, Long testRunId, 
String message) {
-        super(BOOTERCODE_STDOUT_NEW_LINE, runMode, testRunId, message);
+    public StandardStreamOutWithNewLineEvent(RunMode runMode, Long testRunId, 
String message, String stack) {
+        super(BOOTERCODE_STDOUT_NEW_LINE, runMode, testRunId, message, stack);
     }
 }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
index d5bb29be7..ef01472b4 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/OutputReportEntry.java
@@ -40,4 +40,10 @@ public interface OutputReportEntry {
     boolean isStdOut();
 
     boolean isNewLine();
+
+    /**
+     * The stack trace of the thread that produced the output.
+     * claasName#method;className#method;...
+     */
+    String getStack();
 }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/StackTraceProvider.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/StackTraceProvider.java
new file mode 100644
index 000000000..dd5e44086
--- /dev/null
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/StackTraceProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.maven.surefire.api.report;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Util class for stack trace providing from Java 9 this will use StackWalker 
by reflection.
+ */
+public class StackTraceProvider {
+
+    private static Object stackWalker;
+
+    static {
+        try {
+            Class<?> stackWalkerClass =
+                    
Thread.currentThread().getContextClassLoader().loadClass("java.lang.StackWalker");
+            Method getInstanceMethod = 
stackWalkerClass.getMethod("getInstance");
+            stackWalker = getInstanceMethod.invoke(null);
+        } catch (Throwable t) {
+            // ignore
+            //            System.err.println("Unable to load StackWalker, using
+            // Thread.currentThread().getStackTrace()");
+        }
+    }
+
+    /**
+     *
+     * @return the stack trace as a list classname#methodname
+     */
+    static List<String> getStack() {
+        return Arrays.stream(Thread.currentThread().getStackTrace())
+                .map(stackTraceElement -> stackTraceElement.getClassName() + 
"#" + stackTraceElement.getMethodName())
+                .collect(Collectors.toList());
+    }
+}
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
index 3c4c46dea..3e3e03b99 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/TestOutputReportEntry.java
@@ -29,7 +29,11 @@ public final class TestOutputReportEntry implements 
OutputReportEntry {
     private final boolean newLine;
     private final RunMode runMode;
     private final Long testRunId;
-
+    /**
+     * The stack trace of the thread that produced the output.
+     * claasName#method;className#method;...
+     */
+    private String stack;
     /**
      * Wraps the output from the running test-case.
      *
@@ -45,6 +49,7 @@ public TestOutputReportEntry(String log, boolean isStdOut, 
boolean newLine, RunM
         this.newLine = newLine;
         this.runMode = runMode;
         this.testRunId = testRunId;
+        this.stack = String.join(";", StackTraceProvider.getStack());
     }
 
     /**
@@ -59,11 +64,28 @@ private TestOutputReportEntry(String log, boolean isStdOut, 
boolean newLine) {
     }
 
     public TestOutputReportEntry(OutputReportEntry reportEntry, RunMode 
runMode, Long testRunId) {
-        log = reportEntry.getLog();
-        isStdOut = reportEntry.isStdOut();
-        newLine = reportEntry.isNewLine();
+        this(reportEntry.getLog(), reportEntry.isStdOut(), 
reportEntry.isNewLine(), runMode, testRunId);
+    }
+
+    /**
+     * Constructor used when receiving output from a forked JVM where the 
stack trace was captured
+     * on the forked side.
+     *
+     * @param log stdout/stderr output
+     * @param isStdOut true if stdout
+     * @param newLine true if newline
+     * @param runMode the run mode
+     * @param testRunId the test run id
+     * @param stack the stack trace captured on the forked JVM side
+     */
+    public TestOutputReportEntry(
+            String log, boolean isStdOut, boolean newLine, RunMode runMode, 
Long testRunId, String stack) {
+        this.log = log;
+        this.isStdOut = isStdOut;
+        this.newLine = newLine;
         this.runMode = runMode;
         this.testRunId = testRunId;
+        this.stack = stack;
     }
 
     @Override
@@ -89,6 +111,11 @@ public Long getTestRunId() {
         return testRunId;
     }
 
+    @Override
+    public String getStack() {
+        return stack;
+    }
+
     public static OutputReportEntry stdOut(String log) {
         return new TestOutputReportEntry(log, true, false);
     }
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
index 400f74f6c..5c7a859b2 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/api/stream/AbstractStreamDecoder.java
@@ -24,7 +24,6 @@
 import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
-import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.channels.ReadableByteChannel;
@@ -124,8 +123,8 @@ protected Segment readSegment(@Nonnull Memento memento) 
throws IOException, Malf
         int readCount = readByte(memento) & 0xff;
         read(memento, readCount + DELIMITER_LENGTH);
         ByteBuffer bb = memento.getByteBuffer();
-        Segment segment = new Segment(bb.array(), bb.arrayOffset() + ((Buffer) 
bb).position(), readCount);
-        ((Buffer) bb).position(((Buffer) bb).position() + readCount);
+        Segment segment = new Segment(bb.array(), bb.arrayOffset() + 
(bb).position(), readCount);
+        (bb).position((bb).position() + readCount);
         checkDelimiter(memento);
         return segment;
     }
@@ -137,8 +136,8 @@ protected Charset readCharset(@Nonnull Memento memento) 
throws IOException, Malf
         read(memento, length + DELIMITER_LENGTH);
         ByteBuffer bb = memento.getByteBuffer();
         byte[] array = bb.array();
-        int offset = bb.arrayOffset() + ((Buffer) bb).position();
-        ((Buffer) bb).position(((Buffer) bb).position() + length);
+        int offset = bb.arrayOffset() + (bb).position();
+        (bb).position((bb).position() + length);
         boolean isDefaultEncoding = false;
         if (length == DEFAULT_STREAM_ENCODING_BYTES.length) {
             isDefaultEncoding = true;
@@ -155,16 +154,16 @@ protected Charset readCharset(@Nonnull Memento memento) 
throws IOException, Malf
             checkDelimiter(memento);
             return charset;
         } catch (IllegalArgumentException e) {
-            throw new 
MalformedFrameException(memento.getLine().getPositionByteBuffer(), ((Buffer) 
bb).position());
+            throw new 
MalformedFrameException(memento.getLine().getPositionByteBuffer(), 
(bb).position());
         }
     }
 
     protected String readString(@Nonnull Memento memento) throws IOException, 
MalformedFrameException {
-        ((Buffer) memento.getCharBuffer()).clear();
+        (memento.getCharBuffer()).clear();
         int readCount = readInt(memento);
         if (readCount < 0) {
             throw new MalformedFrameException(
-                    memento.getLine().getPositionByteBuffer(), ((Buffer) 
memento.getByteBuffer()).position());
+                    memento.getLine().getPositionByteBuffer(), 
(memento.getByteBuffer()).position());
         }
         read(memento, readCount + DELIMITER_LENGTH);
 
@@ -230,7 +229,7 @@ protected long readLongPrivate(@Nonnull Memento memento) 
throws IOException, Mal
     protected final void checkDelimiter(Memento memento) throws 
MalformedFrameException {
         ByteBuffer bb = memento.bb;
         if ((0xff & bb.get()) != ':') {
-            throw new 
MalformedFrameException(memento.getLine().getPositionByteBuffer(), ((Buffer) 
bb).position());
+            throw new 
MalformedFrameException(memento.getLine().getPositionByteBuffer(), 
(bb).position());
         }
     }
 
@@ -243,16 +242,14 @@ protected final void checkHeader(Memento memento) throws 
MalformedFrameException
         try {
             byte[] header = getEncodedMagicNumber();
             byte[] bbArray = bb.array();
-            for (int start = bb.arrayOffset() + ((Buffer) bb).position(), 
length = header.length;
-                    shift < length;
-                    shift++) {
+            for (int start = bb.arrayOffset() + (bb).position(), length = 
header.length; shift < length; shift++) {
                 if (bbArray[shift + start] != header[shift]) {
                     throw new MalformedFrameException(
-                            memento.getLine().getPositionByteBuffer(), 
((Buffer) bb).position() + shift);
+                            memento.getLine().getPositionByteBuffer(), 
(bb).position() + shift);
                 }
             }
         } finally {
-            ((Buffer) bb).position(((Buffer) bb).position() + shift);
+            (bb).position((bb).position() + shift);
         }
 
         checkDelimiter(memento);
@@ -261,7 +258,7 @@ protected final void checkHeader(Memento memento) throws 
MalformedFrameException
     protected void checkArguments(Memento memento, int expectedDataElements) 
throws MalformedFrameException {
         if (memento.getData().size() != expectedDataElements) {
             throw new MalformedFrameException(
-                    memento.getLine().getPositionByteBuffer(), ((Buffer) 
memento.getByteBuffer()).position());
+                    memento.getLine().getPositionByteBuffer(), 
(memento.getByteBuffer()).position());
         }
     }
 
@@ -269,7 +266,7 @@ private String readString(@Nonnull final Memento memento, 
@Nonnegative final int
             throws IOException, MalformedFrameException {
         memento.getDecoder().reset();
         final CharBuffer output = memento.getCharBuffer();
-        ((Buffer) output).clear();
+        (output).clear();
         final ByteBuffer input = memento.getByteBuffer();
         final List<String> strings = new ArrayList<>();
         int countDecodedBytes = 0;
@@ -293,12 +290,12 @@ private String readString(@Nonnull final Memento memento, 
@Nonnegative final int
                 countDecodedBytes += readInputBytes;
             } while (isLastChunk && bytesToDecode > 0 && 
output.hasRemaining());
 
-            strings.add(((Buffer) output).flip().toString());
-            ((Buffer) output).clear();
+            strings.add((output).flip().toString());
+            (output).clear();
         }
 
         memento.getDecoder().reset();
-        ((Buffer) output).clear();
+        (output).clear();
 
         return toString(strings);
     }
@@ -311,16 +308,16 @@ private static int decodeString(
             boolean endOfInput,
             @Nonnegative int errorStreamFrom)
             throws MalformedFrameException {
-        int limit = ((Buffer) input).limit();
-        ((Buffer) input).limit(((Buffer) input).position() + bytesToDecode);
+        int limit = (input).limit();
+        (input).limit((input).position() + bytesToDecode);
 
         CoderResult result = decoder.decode(input, output, endOfInput);
         if (result.isError() || result.isMalformed()) {
-            throw new MalformedFrameException(errorStreamFrom, ((Buffer) 
input).position());
+            throw new MalformedFrameException(errorStreamFrom, 
(input).position());
         }
 
         int decodedBytes = bytesToDecode - input.remaining();
-        ((Buffer) input).limit(limit);
+        (input).limit(limit);
         return decodedBytes;
     }
 
@@ -339,8 +336,8 @@ private void printCorruptedStream(Memento memento) {
         ByteBuffer bb = memento.getByteBuffer();
         if (bb.hasRemaining()) {
             int bytesToWrite = bb.remaining();
-            memento.getLine().write(bb, ((Buffer) bb).position(), 
bytesToWrite);
-            ((Buffer) bb).position(((Buffer) bb).position() + bytesToWrite);
+            memento.getLine().write(bb, (bb).position(), bytesToWrite);
+            (bb).position((bb).position() + bytesToWrite);
         }
     }
 
@@ -405,17 +402,16 @@ public boolean equals(Object obj) {
 
     protected @Nonnull StreamReadStatus read(@Nonnull Memento memento, int 
recommendedCount) throws IOException {
         ByteBuffer buffer = memento.getByteBuffer();
-        if (buffer.remaining() >= recommendedCount && ((Buffer) 
buffer).limit() != 0) {
+        if (buffer.remaining() >= recommendedCount && (buffer).limit() != 0) {
             return OVERFLOW;
         } else {
-            if (((Buffer) buffer).position() != 0
-                    && recommendedCount > buffer.capacity() - ((Buffer) 
buffer).position()) {
-                ((Buffer) buffer.compact()).flip();
+            if ((buffer).position() != 0 && recommendedCount > 
buffer.capacity() - (buffer).position()) {
+                (buffer.compact()).flip();
                 memento.getLine().setPositionByteBuffer(0);
             }
-            int mark = ((Buffer) buffer).position();
-            ((Buffer) buffer).position(((Buffer) buffer).limit());
-            ((Buffer) buffer).limit(min(((Buffer) buffer).position() + 
recommendedCount, buffer.capacity()));
+            int mark = (buffer).position();
+            (buffer).position((buffer).limit());
+            (buffer).limit(min((buffer).position() + recommendedCount, 
buffer.capacity()));
             return read(buffer, mark, recommendedCount);
         }
     }
@@ -425,17 +421,17 @@ private StreamReadStatus read(ByteBuffer buffer, int 
oldPosition, int recommende
         boolean isEnd = false;
         try {
             while (!isEnd
-                    && ((Buffer) buffer).position() - oldPosition < 
recommendedCount
-                    && ((Buffer) buffer).position() < ((Buffer) 
buffer).limit()) {
+                    && (buffer).position() - oldPosition < recommendedCount
+                    && (buffer).position() < (buffer).limit()) {
                 isEnd = channel.read(buffer) == -1;
             }
         } finally {
-            ((Buffer) buffer).limit(((Buffer) buffer).position());
-            ((Buffer) buffer).position(oldPosition);
+            (buffer).limit((buffer).position());
+            (buffer).position(oldPosition);
             int readBytes = buffer.remaining();
             boolean readComplete = readBytes >= recommendedCount;
             if (!isEnd || readComplete) {
-                debugStream(buffer.array(), buffer.arrayOffset() + ((Buffer) 
buffer).position(), buffer.remaining());
+                debugStream(buffer.array(), buffer.arrayOffset() + 
(buffer).position(), buffer.remaining());
                 readStatus = readComplete ? OVERFLOW : UNDERFLOW;
             }
         }
@@ -463,7 +459,7 @@ public Memento() {
                     .newDecoder()
                     .onMalformedInput(REPLACE)
                     .onUnmappableCharacter(REPLACE);
-            ((Buffer) bb).limit(0);
+            (bb).limit(0);
         }
 
         public void reset() {
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
index 13df7fa1a..9dbd35ce8 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/EventChannelEncoder.java
@@ -165,11 +165,12 @@ public void testOutput(TestOutputReportEntry reportEntry) 
{
         ForkedProcessEventType event = stdout
                 ? (newLine ? BOOTERCODE_STDOUT_NEW_LINE : BOOTERCODE_STDOUT)
                 : (newLine ? BOOTERCODE_STDERR_NEW_LINE : BOOTERCODE_STDERR);
-        setOutErr(event, reportEntry.getRunMode(), reportEntry.getTestRunId(), 
msg);
+        setOutErr(event, reportEntry.getRunMode(), reportEntry.getTestRunId(), 
msg, reportEntry.getStack());
     }
 
-    private void setOutErr(ForkedProcessEventType eventType, RunMode runMode, 
Long testRunId, String message) {
-        ByteBuffer result = encodeMessage(eventType, runMode, testRunId, 
message);
+    private void setOutErr(
+            ForkedProcessEventType eventType, RunMode runMode, Long testRunId, 
String message, String stackTrace) {
+        ByteBuffer result = encodeMessage(eventType, runMode, testRunId, 
message, stackTrace);
         write(result, false);
     }
 
@@ -371,11 +372,13 @@ ByteBuffer encode(ForkedProcessEventType operation, 
ReportEntry reportEntry, boo
         return result;
     }
 
-    ByteBuffer encodeMessage(ForkedProcessEventType eventType, RunMode 
runMode, Long testRunId, String message) {
+    ByteBuffer encodeMessage(
+            ForkedProcessEventType eventType, RunMode runMode, Long testRunId, 
String message, String stackCall) {
         CharsetEncoder encoder = newCharsetEncoder();
-        int bufferMaxLength = 
estimateBufferLength(eventType.getOpcode().length(), runMode, encoder, 0, 1, 
message);
+        int bufferMaxLength =
+                estimateBufferLength(eventType.getOpcode().length(), runMode, 
encoder, 0, 1, message, stackCall);
         ByteBuffer result = ByteBuffer.allocate(bufferMaxLength);
-        encode(encoder, result, eventType, runMode, testRunId, message);
+        encode(encoder, result, eventType, runMode, testRunId, message, 
stackCall);
         return result;
     }
 
diff --git 
a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
index af15e68ff..9eafdf83c 100644
--- 
a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
+++ 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/spi/EventChannelEncoderTest.java
@@ -952,21 +952,21 @@ public void testSendOpcode() {
         Channel channel = new Channel();
         new EventChannelEncoder(channel).testOutput(new 
TestOutputReportEntry(stdOut("msg"), NORMAL_RUN, 1L));
         assertThat(toString(channel.src))
-                .isEqualTo(":maven-surefire-event:" + (char) 14 + 
":std-out-stream:" + (char) 10 + ":normal-run:"
+                .contains(":maven-surefire-event:" + (char) 14 + 
":std-out-stream:" + (char) 10 + ":normal-run:"
                         + 
"\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                         + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:");
 
         channel = new Channel();
         new EventChannelEncoder(channel).testOutput(new 
TestOutputReportEntry(stdErr(null), NORMAL_RUN, 1L));
         assertThat(toString(channel.src))
-                .isEqualTo(":maven-surefire-event:" + (char) 14 + 
":std-err-stream:" + (char) 10 + ":normal-run:"
+                .contains(":maven-surefire-event:" + (char) 14 + 
":std-err-stream:" + (char) 10 + ":normal-run:"
                         + 
"\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                         + (char) 5 + 
":UTF-8:\u0000\u0000\u0000\u0001:\u0000:");
 
-        ByteBuffer result =
-                new EventChannelEncoder(new 
Channel()).encodeMessage(BOOTERCODE_TEST_ERROR, NORMAL_RUN, 1L, "msg");
+        ByteBuffer result = new EventChannelEncoder(new Channel())
+                .encodeMessage(BOOTERCODE_TEST_ERROR, NORMAL_RUN, 1L, "msg", 
"foo#bar");
         assertThat(toString(result))
-                .isEqualTo(":maven-surefire-event:" + (char) 10 + 
":test-error:" + (char) 10 + ":normal-run:"
+                .contains(":maven-surefire-event:" + (char) 10 + 
":test-error:" + (char) 10 + ":normal-run:"
                         + 
"\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                         + (char) 5 + ":UTF-8:\u0000\u0000\u0000\u0003:msg:");
     }
@@ -1133,7 +1133,7 @@ public void testStdOutStream() throws IOException {
                 + (char) 10 + 
":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1149,7 +1149,7 @@ public void testStdOutStreamLn() throws IOException {
                 + (char) 10 + 
":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1165,7 +1165,7 @@ public void testStdErrStream() throws IOException {
                 + (char) 10 + 
":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1181,7 +1181,7 @@ public void testStdErrStreamLn() throws IOException {
                 + (char) 10 + 
":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1204,7 +1204,7 @@ public void testStdErrStreamEmptyMessageNullTestId() 
throws IOException {
                 + (char) 10 + ":normal-run:\u0000:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0000::";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1222,7 +1222,7 @@ public void testStdErrStreamEmptyMessageNullRunMode() 
throws IOException {
                 + "\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001:"
                 + "\u0005:UTF-8:\u0000\u0000\u0000\u0000::";
 
-        assertThat(new String(out.toByteArray(), UTF_8)).isEqualTo(expected);
+        assertThat(new String(out.toByteArray(), UTF_8)).contains(expected);
     }
 
     @Test
@@ -1307,7 +1307,7 @@ public void testInterruptHandling() throws IOException {
         }
 
         assertThat(new String(out.toByteArray(), UTF_8))
-                .isEqualTo(":maven-surefire-event:\u000e:std-out-stream:"
+                .contains(":maven-surefire-event:\u000e:std-out-stream:"
                         + (char) 10 + 
":normal-run:\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002"
                         + ":\u0005:UTF-8:\u0000\u0000\u0000\u0003:msg:");
     }
diff --git 
a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit47RedirectOutputIT.java
 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit47RedirectOutputIT.java
index e3f4654c6..f8f0e23a7 100644
--- 
a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit47RedirectOutputIT.java
+++ 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnit47RedirectOutputIT.java
@@ -27,9 +27,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 
-/**
- *
- */
 public class JUnit47RedirectOutputIT extends SurefireJUnitIntegrationTestCase {
     @Test
     public void testPrintSummaryTrueWithRedirect() {

Reply via email to