diff --git a/capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java b/capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java index 593a312..e33b0f6 100644 --- a/capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java +++ b/capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,9 +22,8 @@ private ProcessRunner() {} public static String runWithOutput(String... command) { try { ProcessBuilder processBuilder = new ProcessBuilder(command); - // Redirect output and error streams to a pipe processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE); - processBuilder.redirectError(ProcessBuilder.Redirect.PIPE); + processBuilder.redirectErrorStream(true); final Process process = processBuilder.start(); LOG.info("Waiting for process to finish..."); @@ -36,20 +36,22 @@ public static String runWithOutput(String... command) { LOG.error("I/O Error: {}", e.getMessage(), e); throw new WanakuException(e); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); LOG.error("Interrupted: {}", e.getMessage(), e); throw new WanakuException(e); } } private static String readOutput(Process process) throws IOException { - // Read the output from the process - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - StringBuilder output = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - output.append(line).append("\n"); + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + StringBuilder output = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + return output.toString(); } - return output.toString(); } public static void run(File directory, String... command) { @@ -74,6 +76,7 @@ public static void run(File directory, Map environmentVariables, LOG.error("I/O Error: {}", e.getMessage(), e); throw new WanakuException(e); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); LOG.error("Interrupted: {}", e.getMessage(), e); throw new WanakuException(e); } diff --git a/capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java b/capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java index fb76fad..0b0e6ed 100644 --- a/capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java +++ b/capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java @@ -1,18 +1,46 @@ package ai.wanaku.capabilities.sdk.common; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class ProcessRunnerTest { + private static String[] shellCommand(String script) { + if (System.getProperty("os.name").toLowerCase().contains("win")) { + return new String[] {"cmd.exe", "/c", script}; + } else { + return new String[] {"sh", "-c", script}; + } + } + @Test void runWithOutput_returnsCommandOutput() { - String output = ProcessRunner.runWithOutput("echo", "hello"); + String output = ProcessRunner.runWithOutput(shellCommand("echo hello")); assertNotNull(output, "Output should not be null"); assertFalse(output.isEmpty(), "Output should not be empty"); assertTrue(output.contains("hello"), "Output should contain 'hello'"); } + + @Test + void runWithOutput_nonZeroExitDoesNotThrow() { + String[] command = shellCommand("echo output; exit 1"); + String output = assertDoesNotThrow(() -> ProcessRunner.runWithOutput(command)); + + assertNotNull(output, "Output should not be null"); + assertTrue(output.contains("output"), "Output should contain 'output'"); + } + + @Test + void runWithOutput_capturesStderrViaMergedStream() { + String output = ProcessRunner.runWithOutput(shellCommand("echo stdout && echo stderr 1>&2")); + + assertNotNull(output, "Output should not be null"); + assertFalse(output.isEmpty(), "Output should not be empty"); + assertTrue(output.contains("stdout"), "Output should contain stdout content"); + assertTrue(output.contains("stderr"), "Output should contain stderr content (merged stream)"); + } }