8373887: jpackage tests may potentially deadlock

Reviewed-by: almatvee
This commit is contained in:
Alexey Semenyuk 2025-12-18 01:46:45 +00:00
parent c16ce929c7
commit ea5834415d
2 changed files with 42 additions and 4 deletions

View File

@ -210,7 +210,13 @@ public class ExecutorTest extends JUnitAdapter {
assertEquals(0, result[0].getExitCode());
assertEquals(expectedCapturedSystemOut(commandWithDiscardedStreams), outputCapture.outLines());
// If we dump the subprocesses's output, and the command produced both STDOUT and STDERR,
// then the captured STDOUT may contain interleaved command's STDOUT and STDERR,
// not in sequential order (STDOUT followed by STDERR).
// In this case don't check the contents of the captured command's STDOUT.
if (toolProvider || outputCapture.outLines().isEmpty() || (command.stdout().isEmpty() || command.stderr().isEmpty())) {
assertEquals(expectedCapturedSystemOut(commandWithDiscardedStreams), outputCapture.outLines());
}
assertEquals(expectedCapturedSystemErr(commandWithDiscardedStreams), outputCapture.errLines());
assertEquals(expectedResultStdout(commandWithDiscardedStreams), result[0].stdout().getOutput());

View File

@ -32,6 +32,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.ArrayList;
@ -42,12 +43,15 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.jpackage.internal.util.function.ThrowingSupplier;
import jdk.jpackage.internal.util.function.ExceptionBox;
public final class Executor extends CommandArguments<Executor> {
@ -465,9 +469,37 @@ public final class Executor extends CommandArguments<Executor> {
trace("Execute " + sb.toString() + "...");
Process process = builder.start();
final var output = combine(
processProcessStream(outputStreamsControl.stdout(), process.getInputStream()),
processProcessStream(outputStreamsControl.stderr(), process.getErrorStream()));
var stdoutGobbler = CompletableFuture.<Optional<List<String>>>supplyAsync(() -> {
try {
return processProcessStream(outputStreamsControl.stdout(), process.getInputStream());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
var stderrGobbler = CompletableFuture.<Optional<List<String>>>supplyAsync(() -> {
try {
return processProcessStream(outputStreamsControl.stderr(), process.getErrorStream());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
final CommandOutput output;
try {
output = combine(stdoutGobbler.join(), stderrGobbler.join());
} catch (CompletionException ex) {
var cause = ex.getCause();
switch (cause) {
case UncheckedIOException uioex -> {
throw uioex.getCause();
}
default -> {
throw ExceptionBox.toUnchecked(ExceptionBox.unbox(cause));
}
}
}
final int exitCode = process.waitFor();
trace("Done. Exit code: " + exitCode);