From b684173485296519468a5134a0c7ed99d13fcba5 Mon Sep 17 00:00:00 2001 From: Kathrin Geilmann Date: Sat, 16 Jan 2021 00:12:53 +0100 Subject: [PATCH] Pass data from stdout and stderr of spawned processes line by line to the consumer - Fix for #596. This ensures, that the prefixes added by a consumer do not occur in the middle of the message, which happened in the old implementation if the message was longer than 256 bytes (often the case for stacktraces). --- .../mutationtest/build/WorkerFactory.java | 6 +++--- .../java/org/pitest/process/ProcessArgs.java | 8 +++---- .../java/org/pitest/util/StreamMonitor.java | 21 ++++++++++++------- .../pitest/functional/prelude/Prelude.java | 20 +++++++++--------- .../functional/prelude/PreludeTest.java | 4 ++-- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/WorkerFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/WorkerFactory.java index 50c5fd6bf..7cb91761d 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/WorkerFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/WorkerFactory.java @@ -17,7 +17,7 @@ import java.util.Collection; import java.util.function.Consumer; -import static org.pitest.functional.prelude.Prelude.printWith; +import static org.pitest.functional.prelude.Prelude.printlnWith; public class WorkerFactory { @@ -58,7 +58,7 @@ public MutationTestProcess createWorker( final ProcessArgs args = ProcessArgs.withClassPath(this.classPath) .andLaunchOptions(this.config.getLaunchOptions()) .andBaseDir(this.baseDir).andStdout(captureStdOutIfVerbose()) - .andStderr(printWith("stderr ")); + .andStderr(printlnWith("stderr ")); final SocketFinder sf = new SocketFinder(); return new MutationTestProcess( @@ -67,7 +67,7 @@ public MutationTestProcess createWorker( private Consumer captureStdOutIfVerbose() { if (this.verbose) { - return Prelude.printWith("stdout "); + return printlnWith("stdout "); } else { return Prelude.noSideEffect(String.class); } diff --git a/pitest-entry/src/main/java/org/pitest/process/ProcessArgs.java b/pitest-entry/src/main/java/org/pitest/process/ProcessArgs.java index 06db34deb..ed044c097 100644 --- a/pitest-entry/src/main/java/org/pitest/process/ProcessArgs.java +++ b/pitest-entry/src/main/java/org/pitest/process/ProcessArgs.java @@ -14,8 +14,8 @@ */ package org.pitest.process; -import static org.pitest.functional.prelude.Prelude.print; -import static org.pitest.functional.prelude.Prelude.printTo; +import static org.pitest.functional.prelude.Prelude.println; +import static org.pitest.functional.prelude.Prelude.printlnTo; import java.io.File; import java.util.Collections; @@ -28,8 +28,8 @@ public final class ProcessArgs { private final String launchClassPath; - private Consumer stdout = print(String.class); - private Consumer stdErr = printTo(String.class, System.err); + private Consumer stdout = println(String.class); + private Consumer stdErr = printlnTo(String.class, System.err); private List jvmArgs = Collections.emptyList(); private JavaAgent javaAgentFinder; private File workingDir = null; diff --git a/pitest-entry/src/main/java/org/pitest/util/StreamMonitor.java b/pitest-entry/src/main/java/org/pitest/util/StreamMonitor.java index 56c740935..6d7f6c769 100644 --- a/pitest-entry/src/main/java/org/pitest/util/StreamMonitor.java +++ b/pitest-entry/src/main/java/org/pitest/util/StreamMonitor.java @@ -14,18 +14,25 @@ */ package org.pitest.util; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.util.function.Consumer; import java.util.logging.Logger; public class StreamMonitor extends Thread implements Monitor { private static final Logger LOG = Log.getLogger(); - private final byte[] buf = new byte[256]; private final InputStream in; private final Consumer inputHandler; + /** + * Constructor. + * @param in stream to read from + * @param inputHandler all characters read from {@code in} will be forwarded + * line by line (without the newline) to this handler. + */ public StreamMonitor(final InputStream in, final Consumer inputHandler) { super("PIT Stream Monitor"); @@ -41,25 +48,25 @@ public void requestStart() { @Override public void run() { + BufferedReader reader = new BufferedReader(new InputStreamReader(this.in)); while (!this.isInterrupted()) { - readFromStream(); + readFromStream(reader); } } - private void readFromStream() { + private void readFromStream(final BufferedReader reader) { try { // If child JVM crashes reading stdout/stderr seems to sometimes // block and consume 100% cpu, so check stream is available first. // May still be an issue if child crashes during later read . . . - if (this.in.available() == 0) { + if (!reader.ready()) { Thread.sleep(100); return; } - int i; - while ((i = this.in.read(this.buf, 0, this.buf.length)) != -1) { - final String output = new String(this.buf, 0, i); + String output; + while ( ( output = reader.readLine() ) != null ) { this.inputHandler.accept(output); } diff --git a/pitest/src/main/java/org/pitest/functional/prelude/Prelude.java b/pitest/src/main/java/org/pitest/functional/prelude/Prelude.java index 6ff422ecd..0537872f7 100644 --- a/pitest/src/main/java/org/pitest/functional/prelude/Prelude.java +++ b/pitest/src/main/java/org/pitest/functional/prelude/Prelude.java @@ -73,25 +73,25 @@ public static final Function id(final Class type) { return id(); } - public static final Consumer print() { - return printTo(System.out); + public static final Consumer println() { + return printlnTo(System.out); } - public static final Consumer print(final Class type) { - return print(); + public static final Consumer println(final Class type) { + return println(); } - public static final Consumer printTo(final Class type, + public static final Consumer printlnTo(final Class type, final PrintStream stream) { - return printTo(stream); + return printlnTo(stream); } - public static final Consumer printTo(final PrintStream stream) { - return a -> stream.print(a); + public static final Consumer printlnTo(final PrintStream stream) { + return a -> stream.println(a); } - public static Consumer printWith(final T t) { - return a -> System.out.print(t + " : " + a); + public static Consumer printlnWith(final T t) { + return a -> System.out.println(t + " : " + a); } public static Predicate isGreaterThan(final T value) { diff --git a/pitest/src/test/java/org/pitest/functional/prelude/PreludeTest.java b/pitest/src/test/java/org/pitest/functional/prelude/PreludeTest.java index 0cfd498ce..4c09e22b8 100644 --- a/pitest/src/test/java/org/pitest/functional/prelude/PreludeTest.java +++ b/pitest/src/test/java/org/pitest/functional/prelude/PreludeTest.java @@ -27,8 +27,8 @@ public class PreludeTest { public void printToShouldPrintValueToStream() { final Integer i = Integer.valueOf(42); final PrintStream stream = Mockito.mock(PrintStream.class); - Prelude.printTo(stream).accept(i); - verify(stream).print(i); + Prelude.printlnTo(stream).accept(i); + verify(stream).println(i); } }