diff --git a/src/main/java/org/kiwiproject/base/process/ProcessHelper.java b/src/main/java/org/kiwiproject/base/process/ProcessHelper.java index fbfae612..bd9cf523 100644 --- a/src/main/java/org/kiwiproject/base/process/ProcessHelper.java +++ b/src/main/java/org/kiwiproject/base/process/ProcessHelper.java @@ -11,6 +11,7 @@ import javax.annotation.Nullable; import java.io.File; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -337,6 +338,18 @@ private Process launchPgrepWithParentPidFlag(long parentProcessId, ProcessHelper return processHelper.launch("pgrep", "-P", String.valueOf(parentProcessId)); } + /** + * Locate a program in the user's path, returning the result as a {@link Path}. + * + * @param program the program to locate + * @return an Optional containing the full {@link Path} to the program, or an empty Optional if not found + * @implNote If there is more than program found, only the first one is returned + * @see Processes#whichAsPath(String) + */ + public Optional whichAsPath(String program) { + return Processes.whichAsPath(program); + } + /** * Locate a program in the user's path. * diff --git a/src/main/java/org/kiwiproject/base/process/Processes.java b/src/main/java/org/kiwiproject/base/process/Processes.java index 31e47c2d..3c8ee787 100644 --- a/src/main/java/org/kiwiproject/base/process/Processes.java +++ b/src/main/java/org/kiwiproject/base/process/Processes.java @@ -27,6 +27,7 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.OptionalLong; @@ -677,6 +678,17 @@ private static void validateKilledBeforeTimeout(long processId, boolean killedBe } } + /** + * Locate a program in the user's path, returning the result as a {@link Path}. + * + * @param program the program to locate + * @return an Optional containing the full {@link Path} to the program, or an empty Optional if not found + * @implNote If there is more than program found, only the first one is returned + */ + public static Optional whichAsPath(String program) { + return which(program).map(Path::of); + } + /** * Locate a program in the user's path. * diff --git a/src/test/java/org/kiwiproject/base/process/ProcessHelperTest.java b/src/test/java/org/kiwiproject/base/process/ProcessHelperTest.java index 9867382f..a6a9b345 100644 --- a/src/test/java/org/kiwiproject/base/process/ProcessHelperTest.java +++ b/src/test/java/org/kiwiproject/base/process/ProcessHelperTest.java @@ -562,4 +562,23 @@ void shouldReturnEmptyOptional_WhenProgramDoesNotExistInPath() { assertThat(processes.which("killify")).isEmpty(); } } + + @Nested + class WhichAsPath { + + /** + * @implNote This test assumes {@link Processes#whichAsPath(String)} works. This is a trade-off to make this + * test much simpler than having to replicate its logic in this test. + */ + @Test + void shouldFindProgramThatExists() { + var lsPath = Processes.whichAsPath("ls").orElseThrow(); + assertThat(processes.whichAsPath("ls")).contains(lsPath); + } + + @Test + void shouldReturnEmptyOptional_WhenProgramDoesNotExistInPath() { + assertThat(processes.whichAsPath("killify")).isEmpty(); + } + } } diff --git a/src/test/java/org/kiwiproject/base/process/ProcessesTest.java b/src/test/java/org/kiwiproject/base/process/ProcessesTest.java index 3f7ebf10..3d86e89d 100644 --- a/src/test/java/org/kiwiproject/base/process/ProcessesTest.java +++ b/src/test/java/org/kiwiproject/base/process/ProcessesTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import java.nio.file.Path; import java.util.List; import java.util.concurrent.TimeUnit; @@ -267,4 +268,21 @@ void shouldReturnEmptyOptional_WhenProgramDoesNotExistInPath(String program) { } } + @Nested + class WhichAsPath { + + @ParameterizedTest + @ValueSource(strings = {"cp", "ls", "mv"}) + void shouldFindProgramThatExists(String program) { + assertThat(Processes.whichAsPath(program)) + .hasValueSatisfying(value -> assertThat(value).endsWith(Path.of(program))); + } + + @ParameterizedTest + @ValueSource(strings = {"foobar", "abc-xyz", "clunkerate"}) + void shouldReturnEmptyOptional_WhenProgramDoesNotExistInPath(String program) { + assertThat(Processes.whichAsPath(program)).isEmpty(); + } + } + }