diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 55fefbf..947f3c4 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -4,29 +4,28 @@ on: [ push, pull_request, workflow_dispatch ] jobs: build-bld-project: - runs-on: ubuntu-latest - strategy: matrix: - java-version: [ 17, 20 ] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant execute permission for bld - run: chmod +x bld - - - name: Download the dependencies + - name: Download dependencies run: ./bld download - - name: Run tests with bld + - name: Run tests run: ./bld compile test \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 2120d4c..88c2cd6 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -30,14 +30,14 @@ jobs: steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: 17 - name: Build Javadocs @@ -47,11 +47,11 @@ jobs: uses: actions/configure-pages@v3 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: # Upload generated Javadocs repository - path: 'build/javadoc/' + path: "build/javadoc/" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml index ade80da..4446c15 100644 --- a/.idea/copyright/Apache_License.xml +++ b/.idea/copyright/Apache_License.xml @@ -1,6 +1,6 @@ - + - + \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..81220b4 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index 4bb3867..a203de8 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,12 +2,12 @@ - + - + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 48cb4bf..2d63b46 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,14 @@ "type": "java", "name": "Run Tests", "request": "launch", - "mainClass": "rife.bld.extension.ExecOperationTest" + "mainClass": "org.junit.platform.console.ConsoleLauncher", + "args": [ + "--details=verbose", + "--scan-classpath", + "--disable-banner", + "--disable-ansi-colors", + "--exclude-engine=junit-platform-suite", + "--exclude-engine=junit-vintage"] } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 5633e79..ba429d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,13 +3,13 @@ "src/main/java", "src/main/resources", "src/test/java", - "src/bld/java" + "src/test/resources", + "src/bld/java", + "src/bld/resources" ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.8.0.jar", - "lib/compile/*.jar", - "lib/runtime/*.jar", - "lib/test/*.jar" + "${HOME}/.bld/dist/bld-2.2.1.jar", + "lib/**/*.jar" ] } diff --git a/README.md b/README.md index a9dc752..d049d9e 100755 --- a/README.md +++ b/README.md @@ -2,12 +2,20 @@ [](https://opensource.org/licenses/Apache-2.0) [](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) -[](https://rife2.com/bld) +[](https://rife2.com/bld) [](https://repo.rife2.com/#/releases/com/uwyn/rife2/bld-exec) [](https://repo.rife2.com/#/snapshots/com/uwyn/rife2/bld-exec) [](https://github.com/rife2/bld-exec/actions/workflows/bld.yml) -To install, please refer to the [extensions documentation](https://github.com/rife2/bld/wiki/Extensions). +To install the latest version, add the following to the `lib/bld/bld-wrapper.properties` file: + +```properties +bld.extension-exec=com.uwyn.rife2:bld-exec +``` + +For more information, please refer to the [extensions](https://github.com/rife2/bld/wiki/Extensions) documentation. + +## Execute a Command To execute a command at the command line, add the following to your build file: @@ -21,16 +29,15 @@ public void startServer() throws Exception { } ``` -### Failure Modes +## Exit Value -Use the `fail` function to specify whether data returned to the standard streams and/or an abnormal exit value -constitute a failure. +Use the `failOnExit` function to specify whether a command non-zero exit value (status) constitutes a failure. ```java @BuildCommand public void startServer() throws Exception { final List cmds; - if (System.getProperty("os.name").toLowerCase().contains("windows")) { + if (System.getProperty("os.name").toLowerCase().contains("win")) { cmds = List.of("cmd", "/c", "stop.bat"); } else { cmds = List.of("./stop.sh"); @@ -38,28 +45,14 @@ public void startServer() throws Exception { new ExecOperation() .fromProject(this) .command(cmds) - .fail(ExecFail.STDERR) + .failOneExit(false) .execute(); } ``` -The following predefined values are available: +## Work Directory -| Name | Failure When | -|:------------------|:-----------------------------------------------------------------| -| `ExecFail.EXIT` | Exit value > 0 | -| `ExecFail.NORMAL` | Exit value > 0 or any data to the standard error stream (stderr) | -| `ExecFail.OUTPUT` | Any data to the standard output stream (stdout) or stderr. | -| `ExecFail.STDERR` | Any data to stderr. | -| `ExecFail.STDOUT` | Any data to stdout. | -| `ExecFail.ALL` | Any of the conditions above. | -| `ExecFail.NONE` | Never fails. | - -`ExecFail.NORMAL` is the default value. - -## Working Directory - -You can also specify the working directory: +You can also specify the work directory: ```java @BuildCommand diff --git a/config/pmd.xml b/config/pmd.xml index c60ff7e..2641880 100644 --- a/config/pmd.xml +++ b/config/pmd.xml @@ -7,9 +7,9 @@ - - + + @@ -19,12 +19,13 @@ + - + @@ -34,8 +35,9 @@ - + + @@ -51,8 +53,6 @@ - - @@ -106,4 +106,4 @@ - \ No newline at end of file + diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 48bd8ab..305a313 100644 Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 3cb1d83..7470756 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,8 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-pmd=com.uwyn.rife2:bld-pmd:0.9.5 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2 +bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.8.0 +bld.version=2.2.1 diff --git a/src/bld/java/rife/bld/extension/ExecOperationBuild.java b/src/bld/java/rife/bld/extension/ExecOperationBuild.java index 4f6679e..e759b38 100644 --- a/src/bld/java/rife/bld/extension/ExecOperationBuild.java +++ b/src/bld/java/rife/bld/extension/ExecOperationBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,11 +22,9 @@ import rife.bld.publish.PublishDeveloper; import rife.bld.publish.PublishLicense; import rife.bld.publish.PublishScm; -import java.io.IOException; import java.util.List; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.RIFE2_RELEASES; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING; @@ -35,19 +33,21 @@ public class ExecOperationBuild extends Project { public ExecOperationBuild() { pkg = "rife.bld.extension"; name = "ExecOperation"; - version = version(0, 9, 1); + version = version(1, 0, 5); javaRelease = 17; + downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS); + scope(compile) - .include(dependency("com.uwyn.rife2", "bld", version(1, 8, 0))); + .include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1))); scope(test) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1))) - .include(dependency("org.assertj", "assertj-core", version(3, 25, 2))); + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); javadocOperation() .javadocOptions() @@ -58,28 +58,26 @@ public class ExecOperationBuild extends Project { publishOperation() .repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .repository(repository("github")) .info() .groupId("com.uwyn.rife2") .artifactId("bld-exec") .description("Command Line Execution Extension for bld") .url("https://github.com/rife2/bld-exec") - .developer( - new PublishDeveloper() - .id("ethauvin") - .name("Erik C. Thauvin") - .email("erik@thauvin.net") - .url("https://erik.thauvin.net/") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") ) - .license( - new PublishLicense() - .name("The Apache License, Version 2.0") - .url("http://www.apache.org/licenses/LICENSE-2.0.txt") + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt") ) - .scm( - new PublishScm() - .connection("scm:git:https://github.com/rife2/bld-exec.git") - .developerConnection("scm:git:git@github.com:rife2/bld-exec.git") - .url("https://github.com/rife2/bld-exec") + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/bld-exec.git") + .developerConnection("scm:git:git@github.com:rife2/bld-exec.git") + .url("https://github.com/rife2/bld-exec") ) .signKey(property("sign.key")) .signPassphrase(property("sign.passphrase")); @@ -89,15 +87,8 @@ public class ExecOperationBuild extends Project { new ExecOperationBuild().start(args); } - @BuildCommand(summary = "Generates JaCoCo Reports") - public void jacoco() throws IOException { - new JacocoReportOperation() - .fromProject(this) - .execute(); - } - @BuildCommand(summary = "Runs PMD analysis") - public void pmd() { + public void pmd() throws Exception { new PmdOperation() .fromProject(this) .failOnViolation(true) diff --git a/src/main/java/rife/bld/extension/ExecFail.java b/src/main/java/rife/bld/extension/ExecFail.java deleted file mode 100644 index a6221b6..0000000 --- a/src/main/java/rife/bld/extension/ExecFail.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed 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 - * - * https://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 rife.bld.extension; - -/** - * The failure modes enumeration. - * - * @author Erik C. Thauvin - * @since 1.0 - */ -public enum ExecFail { - ALL, EXIT, NONE, NORMAL, OUTPUT, STDERR, STDOUT -} diff --git a/src/main/java/rife/bld/extension/ExecOperation.java b/src/main/java/rife/bld/extension/ExecOperation.java index 07743c3..d81f484 100644 --- a/src/main/java/rife/bld/extension/ExecOperation.java +++ b/src/main/java/rife/bld/extension/ExecOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,14 @@ package rife.bld.extension; import rife.bld.BaseProject; import rife.bld.operations.AbstractOperation; +import rife.bld.operations.exceptions.ExitStatusException; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -35,10 +38,11 @@ import java.util.logging.Logger; */ public class ExecOperation extends AbstractOperation { private static final Logger LOGGER = Logger.getLogger(ExecOperation.class.getName()); - private final List args_ = new ArrayList<>(); - private final Set fail_ = new HashSet<>(); + private final Collection args_ = new ArrayList<>(); + private boolean failOnExit_ = true; private BaseProject project_; - private String workDir_; + private int timeout_ = 30; + private File workDir_; /** * Configures the command and arguments to be executed. @@ -54,10 +58,19 @@ public class ExecOperation extends AbstractOperation { * @see #command(Collection) */ public ExecOperation command(String... arg) { - args_.addAll(List.of(arg)); - return this; + return command(List.of(arg)); } + /** + * Returns the command and arguments to be executed. + * + * @return the command and arguments + */ + public Collection command() { + return args_; + } + + /** * Configures the command and arguments to be executed. * @@ -76,86 +89,62 @@ public class ExecOperation extends AbstractOperation { @Override public void execute() throws Exception { if (project_ == null) { - LOGGER.severe("A project must be specified."); - } - - var errorMessage = new StringBuilder(27); - - final File workDir; - if (workDir_ == null || workDir_.isBlank()) { - workDir = new File(project_.workDirectory().getAbsolutePath()); + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("A project must be specified."); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); } else { - workDir = new File(workDir_); - } - - if (workDir.isDirectory()) { - var pb = new ProcessBuilder(); - pb.command(args_); - pb.directory(workDir); + final File workDir = Objects.requireNonNullElseGet(workDir_, + () -> new File(project_.workDirectory().getAbsolutePath())); if (LOGGER.isLoggable(Level.INFO)) { - LOGGER.info(String.join(" ", args_)); + LOGGER.info("Working directory: " + workDir.getAbsolutePath()); } - var proc = pb.start(); - var err = proc.waitFor(30, TimeUnit.SECONDS); - var stdout = readStream(proc.getInputStream()); - var stderr = readStream(proc.getErrorStream()); + if (workDir.isDirectory()) { + var pb = new ProcessBuilder(); + pb.inheritIO(); + pb.command(args_.stream().toList()); + pb.directory(workDir); - if (!err) { - errorMessage.append("TIMEOUT"); - } else if (!fail_.contains(ExecFail.NONE)) { - var all = fail_.contains(ExecFail.ALL); - var output = fail_.contains(ExecFail.OUTPUT); - if ((all || fail_.contains(ExecFail.EXIT) || fail_.contains(ExecFail.NORMAL)) && proc.exitValue() > 0) { - errorMessage.append("EXIT ").append(proc.exitValue()); - if (!stderr.isEmpty()) { - errorMessage.append(", STDERR -> ").append(stderr.get(0)); - } else if (!stdout.isEmpty()) { - errorMessage.append(", STDOUT -> ").append(stdout.get(0)); + if (LOGGER.isLoggable(Level.INFO) && !silent()) { + LOGGER.info(String.join(" ", args_)); + } + + var proc = pb.start(); + var err = proc.waitFor(timeout_, TimeUnit.SECONDS); + + if (!err) { + proc.destroy(); + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("The command timed out."); } - } else if ((all || output || fail_.contains(ExecFail.STDERR) || fail_.contains(ExecFail.NORMAL)) - && !stderr.isEmpty()) { - errorMessage.append("STDERR -> ").append(stderr.get(0)); - } else if ((all || output || fail_.contains(ExecFail.STDOUT)) && !stdout.isEmpty()) { - errorMessage.append("STDOUT -> ").append(stdout.get(0)); + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } else if (proc.exitValue() != 0 && failOnExit_) { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("The command exit value/status is: " + proc.exitValue()); + } + ExitStatusException.throwOnFailure(proc.exitValue()); } - } - - if (LOGGER.isLoggable(Level.INFO) && errorMessage.isEmpty() && !stdout.isEmpty()) { - for (var l : stdout) { - LOGGER.info(l); + } else { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("Invalid working directory: " + workDir); } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); } - } else { - errorMessage.append("Invalid working directory: ").append(workDir.getCanonicalPath()); - } - - if (!errorMessage.isEmpty()) { - throw new IOException(errorMessage.toString()); } } /** - * Configure the failure mode. + * Configures whether the operation should fail if the command exit value/status is not 0. * - * The failure modes are: - * - * {@link ExecFail#EXIT}Exit value > 0 - * {@link ExecFail#NORMAL}Exit value > 0 or any data to the standard error stream (stderr) - * {@link ExecFail#OUTPUT}Any data to the standard output stream (stdout) or stderr - * {@link ExecFail#STDERR}Any data to stderr - * {@link ExecFail#STDOUT}Any data to stdout - * {@link ExecFail#ALL}Any of the conditions above - * {@link ExecFail#NONE}Never fails - * + * Default is {@code TRUE} * - * @param fail one or more failure modes - * @return this operation instance - * @see ExecFail + * @param failOnExit The fail on exit toggle + * @return this operation instance. */ - public ExecOperation fail(ExecFail... fail) { - fail_.addAll(Set.of(fail)); + public ExecOperation failOnExit(boolean failOnExit) { + failOnExit_ = failOnExit; return this; } @@ -170,16 +159,57 @@ public class ExecOperation extends AbstractOperation { return this; } - private List readStream(InputStream stream) { - var lines = new ArrayList(); - try (var scanner = new Scanner(stream)) { - while (scanner.hasNextLine()) { - lines.add(scanner.nextLine()); - } - } - return lines; + /** + * Returns whether the operation should fail if the command exit value/status is not 0. + * + * @return {@code true} or {@code false} + */ + public boolean isFailOnExit() { + return failOnExit_; } + /** + * Configure the command timeout. + * + * @param timeout The timeout in seconds + * @return this operation instance + */ + public ExecOperation timeout(int timeout) { + timeout_ = timeout; + return this; + } + + /** + * Returns the command timeout. + * + * @return the timeout + */ + public int timeout() { + return timeout_; + } + + /** + * Configures the working directory. + * + * @param dir the directory + * @return this operation instance + */ + public ExecOperation workDir(File dir) { + workDir_ = dir; + return this; + } + + /** + * Configures the working directory. + * + * @param dir the directory + * @return this operation instance + */ + public ExecOperation workDir(Path dir) { + return workDir(dir.toFile()); + } + + /** * Configures the working directory. * @@ -187,7 +217,15 @@ public class ExecOperation extends AbstractOperation { * @return this operation instance */ public ExecOperation workDir(String dir) { - workDir_ = dir; - return this; + return workDir(new File(dir)); + } + + /** + * Returns the working directory. + * + * @return the directory + */ + public File workDir() { + return workDir_; } } diff --git a/src/test/java/rife/bld/extension/ExecOperationTest.java b/src/test/java/rife/bld/extension/ExecOperationTest.java index 0746988..be2bd89 100644 --- a/src/test/java/rife/bld/extension/ExecOperationTest.java +++ b/src/test/java/rife/bld/extension/ExecOperationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,54 +17,30 @@ package rife.bld.extension; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import rife.bld.BaseProject; import rife.bld.Project; import rife.bld.WebProject; +import rife.bld.operations.exceptions.ExitStatusException; import java.io.File; -import java.io.IOException; import java.util.List; +import java.util.Locale; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; - +@SuppressWarnings("PMD.AvoidDuplicateLiterals") class ExecOperationTest { private static final String FOO = "foo"; - private static final String HELLO = "Hello"; + private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.US).contains("win"); @Test - void testAll() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new Project()) - .command("date") - .fail(ExecFail.ALL) - .execute() - ).isInstanceOf(IOException.class); - } - - @Test - void testCat() throws Exception { - var tmpFile = new File("hello.tmp"); - tmpFile.deleteOnExit(); - new ExecOperation() - .fromProject(new Project()) - .command("touch", tmpFile.getName()) - .fail(ExecFail.NORMAL) - .execute(); - - assertThat(tmpFile).exists(); - } - - @Test - void testCommandList() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new BaseProject()) - .command(List.of("logger", "-s", HELLO)) - .fail(ExecFail.STDERR) - .execute()).message().startsWith("STDERR -> ").endsWith(HELLO); + void testCommand() { + var op = new ExecOperation().fromProject(new WebProject()) + .command(FOO, "bar"); + assertThat(op.command()).containsExactly(FOO, "bar"); } @Test @@ -77,64 +53,102 @@ class ExecOperationTest { } @Test - void testExit() { + void testExitValue() { + List cat; + if (IS_WINDOWS) { + cat = List.of("cmd", "/c", "type", FOO); + } else { + cat = List.of("cat", FOO); + } assertThatCode(() -> new ExecOperation() .fromProject(new BaseProject()) - .command("tail", FOO) - .fail(ExecFail.EXIT) - .execute()).message().startsWith("EXIT "); + .command(cat) + .execute()).isInstanceOf(ExitStatusException.class); } @Test - void testNone() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new WebProject()) - .command("cat", FOO) - .fail(ExecFail.NONE) - .execute()).doesNotThrowAnyException(); + void testFailOnExit() { + List cat; + if (IS_WINDOWS) { + cat = List.of("cmd", "/c", "type", FOO); + } else { + cat = List.of("cat", FOO); + } + var op = new ExecOperation() + .fromProject(new BaseProject()) + .command(cat) + .failOnExit(false); + assertThat(op.isFailOnExit()).isFalse(); + assertThatCode(op::execute).doesNotThrowAnyException(); + + op.failOnExit(true); + assertThat(op.isFailOnExit()).isTrue(); } @Test - void testOutput() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new WebProject()) - .command("echo") - .fail(ExecFail.OUTPUT) - .execute() - ).message().isEqualTo("STDOUT -> "); + void testTimeout() { + List sleep; + if (IS_WINDOWS) { + sleep = List.of("cmd", "/c", "timeout", "/t", "10"); + } else { + sleep = List.of("sleep", "10"); + } + var op = new ExecOperation() + .fromProject(new BaseProject()) + .timeout(5) + .command(sleep); + assertThat(op.timeout()).isEqualTo(5); + assertThatCode(op::execute).isInstanceOf(ExitStatusException.class); } @Test - void testStdErr() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new BaseProject()) - .command("logger", "-s", HELLO) - .fail(ExecFail.STDERR) - .execute()).message().startsWith("STDERR -> ").endsWith(HELLO); - } + @EnabledOnOs({OS.LINUX, OS.MAC}) + void testTouch() throws Exception { + var tmpFile = new File("hello.tmp"); + tmpFile.deleteOnExit(); + new ExecOperation() + .fromProject(new Project()) + .timeout(10) + .command("touch", tmpFile.getName()) + .execute(); - @Test - void testStdOut() { - assertThatCode(() -> - new ExecOperation() - .fromProject(new BaseProject()) - .command("echo", HELLO) - .fail(ExecFail.STDOUT) - .execute()).message().isEqualTo("STDOUT -> Hello"); + assertThat(tmpFile).exists(); } @Test void testWorkDir() { + List echo; + if (IS_WINDOWS) { + echo = List.of("cmd", "/c", "echo", FOO); + } else { + echo = List.of("echo", FOO); + } + var workDir = new File(System.getProperty("java.io.tmpdir")); + var op = new ExecOperation() + .fromProject(new BaseProject()) + .command(echo) + .workDir(workDir); + assertThat(op.workDir()).as("as file").isEqualTo(workDir); + assertThatCode(op::execute).doesNotThrowAnyException(); + + var build = "build"; + op = op.workDir(build); + assertThat(op.workDir()).as("as string").isEqualTo(new File(build)); + assertThatCode(op::execute).doesNotThrowAnyException(); + + op = op.workDir(workDir.toPath()); + assertThat(op.workDir()).as("as path").isEqualTo(workDir); + assertThatCode(op::execute).doesNotThrowAnyException(); + } + + @Test + void testWorkDirInvalid() { assertThatCode(() -> new ExecOperation() .fromProject(new BaseProject()) .command("echo") .workDir(FOO) - .fail(ExecFail.NORMAL) - .execute()).message().startsWith("Invalid working directory: ").endsWith(FOO); + .execute()).isInstanceOf(ExitStatusException.class); } }
- * The failure modes are: - *
Exit value > 0
Exit value > 0 or any data to the standard error stream (stderr)
Any data to the standard output stream (stdout) or stderr
Any data to stderr
Any data to stdout
Any of the conditions above
Never fails