diff --git a/.run/test_incl_extensive_container_tests.run.xml b/.run/test_incl_extensive_container_tests.run.xml index 459ccfc..69dd95e 100644 --- a/.run/test_incl_extensive_container_tests.run.xml +++ b/.run/test_incl_extensive_container_tests.run.xml @@ -1,6 +1,6 @@ <component name="ProjectRunConfigurationManager"> <configuration default="false" name="test_incl_extensive_container_tests" type="JUnit" factoryName="JUnit"> - <module name="org.domaindrivenarchitecture.provs.provs.test" /> + <module name="provs.test" /> <option name="PACKAGE_NAME" value="org" /> <option name="MAIN_CLASS_NAME" value="" /> <option name="METHOD_NAME" value="" /> diff --git a/doc/ForDevelopers.md b/doc/ForDevelopers.md index caabe33..5e67822 100644 --- a/doc/ForDevelopers.md +++ b/doc/ForDevelopers.md @@ -16,13 +16,14 @@ The success or failure is computed automatically in the following way: ### Recommended way -A task can be declared by +A task can be declared by ```kotlin fun Prov.myCustomTask() = task { /* ... code and subtasks come here ... */ } + // e.g. -fun Prov.myEchoTask() = task { - cmd("echo hello world!") +fun Prov.myEchoTask() = task { + cmd("echo hello world!") } ``` @@ -56,17 +57,18 @@ fun Prov.myCustomTask() {{ task { /* ... code and subtasks come here ... */ } }} ### Add custom results -If you want to add a result explicitly, you can use method `addResultToEval`. -This maxy be used e.g. to add explicitly an error line, like in: +If you want to add a result explicitly, you can use method `addResult`. +This may be used e.g. to add explicitly an error line or with additional info, like in: ```kotlin fun Prov.myCustomTask() = task { /* some other code ... */ - addResultToEval(ProvResult(false, err = "my error msg")) + addResult(false, err = "my error msg") /* some other code ... */ + addResult(true, info = "package was already installed") } ``` -or alternatively you can use `taskWithResult`. +or alternatively you can use `taskWithResult` and `return@taskWithResult ProvResult(false, err = "my error msg")`. #### TaskWithResult diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/ClojureScript.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/ClojureScript.kt index 657871c..6d1b572 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/ClojureScript.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/ClojureScript.kt @@ -9,7 +9,7 @@ import org.domaindrivenarchitecture.provs.framework.core.ProvResult fun Prov.installShadowCljs(): ProvResult = task { if (!chk(". .nvm/nvm.sh")) { - addProvResult(false, err = "nvm not installed!") + addResult(false, err = "nvm not installed!") } else { if (!chk("npm list -g --depth=0 | grep shadow-cljs")) { cmd(". .nvm/nvm.sh && npm install -g shadow-cljs") diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt index 8ad433a..b6cc544 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt @@ -217,12 +217,12 @@ open class Prov protected constructor( fun getSecret(command: String, removeNewlineSuffix: Boolean = false): Secret? { val result = cmdNoLog(command) return if (result.success && result.out != null) { - addProvResult(true, getCallingMethodName()) + addResult(true, getCallingMethodName()) val plainSecret = if (removeNewlineSuffix && result.out.takeLast(1) == "\n") result.out.dropLast(1) else result.out Secret(plainSecret) } else { - addProvResult(false, getCallingMethodName(), err = result.err, exception = result.exception) + addResult(false, getCallingMethodName(), err = result.err, exception = result.exception) null } } @@ -232,7 +232,7 @@ open class Prov protected constructor( * Adds a ProvResult to the overall success evaluation. * Intended for use in methods which do not automatically add results. */ - @Deprecated("since 0.39.7", replaceWith = ReplaceWith("addProvResult", )) + @Deprecated("since 0.39.7", replaceWith = ReplaceWith("addResult", )) fun addResultToEval(result: ProvResult) = taskWithResult { result } @@ -241,15 +241,16 @@ open class Prov protected constructor( * Adds a ProvResult to the overall success evaluation. * Intended for use in methods which do not automatically add results. */ - fun addProvResult( + fun addResult( success: Boolean, cmd: String? = null, out: String? = null, err: String? = null, exception: Exception? = null, - exit: String? = null + exit: String? = null, + info: String? = null, ) = taskWithResult { - ProvResult(success, cmd, out, err, exception, exit) + ProvResult(success, cmd, out, err, exception, exit, info) } /** @@ -272,6 +273,9 @@ open class Prov protected constructor( ProvResult(success) } + /** + * Adds the given text, which will be printed at the very end, after all tasks were executed. + */ fun addInfoText(text: String) { infoTexts.add(text) } @@ -492,14 +496,17 @@ open class Prov protected constructor( internal data class ResultLine(val level: Int, val method: String?, var provResult: ProvResult?) { override fun toString(): String { - val provResult = provResult - return if (provResult != null) { - prefix(level) + (if (provResult.success) "Success -- " else "FAILED -- ") + - method + " " + (provResult.cmd ?: "") + - (if (!provResult.success && provResult.err != null) " -- Error: " + provResult.err.escapeControlChars() else "") - } else - prefix(level) + method + " " + "... in progress ... " + val result = provResult + if (result != null) { + val status = if (result.success) "Success -- " else "FAILED -- " + val taskName = method + " " + (result.cmd ?: "") + val infoText = if (result.info != null) "-- Info: " + result.info.escapeControlChars() + " " else "" + val errorText = if (!result.success && result.err != null) "-- Error: " + result.err.escapeControlChars() else "" + return prefix(level) + status + taskName + infoText + errorText + } else { + return prefix(level) + method + " " + "... in progress ... " + } } fun inProgress(): String { diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvResult.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvResult.kt index 1e8f048..379218a 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvResult.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvResult.kt @@ -1,28 +1,26 @@ package org.domaindrivenarchitecture.provs.framework.core -data class ProvResult(val success: Boolean, - val cmd: String? = null, - val out: String? = null, - val err: String? = null, - val exception: Exception? = null, - val exit: String? = null) { +data class ProvResult( + val success: Boolean, + val cmd: String? = null, + val out: String? = null, + val err: String? = null, + val exception: Exception? = null, + val exit: String? = null, + val info: String? = null, +) { val outTrimmed: String? = out?.trim() - constructor(returnCode : Int) : this(returnCode == 0) + constructor(returnCode: Int) : this(returnCode == 0) override fun toString(): String { - return "ProvResult:: ${if (success) "Succeeded" else "FAILED"} -- ${if (!cmd.isNullOrEmpty()) "Name: " + - cmd.escapeNewline() + ", " else ""}${if (!out.isNullOrEmpty()) "Details: $out" else ""}" + + + return "ProvResult:: ${if (success) "Succeeded" else "FAILED"} -- ${ + if (!cmd.isNullOrEmpty()) "Name: " + + cmd.escapeNewline() + ", " else "" + }${if (!out.isNullOrEmpty()) "Details: $out" else ""}" + (exception?.run { " Exception: " + toString() } ?: "") } - - @Suppress("unused") - fun toShortString() : String { - return "ProvResult:: ${if (success) "Succeeded" else "FAILED"} -- " + - if (!success) - (if (out != null) "Details: $out " else "" + - if (err != null) " Error: " + err else "") else "" - } } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/scheduledjobs/infrastructure/CronJobs.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/scheduledjobs/infrastructure/CronJobs.kt index 8f67a6f..76b5fcb 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/scheduledjobs/infrastructure/CronJobs.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/scheduledjobs/infrastructure/CronJobs.kt @@ -1,7 +1,6 @@ package org.domaindrivenarchitecture.provs.framework.ubuntu.scheduledjobs.infrastructure import org.domaindrivenarchitecture.provs.framework.core.Prov -import org.domaindrivenarchitecture.provs.framework.core.ProvResult import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile @@ -24,6 +23,6 @@ fun Prov.createCronJob(cronFilename: String, schedule: String, command: String, createDirs("/etc/cron.d/", sudo = true) createFile("/etc/cron.d/$cronFilename", cronLine, "644", sudo = true, overwriteIfExisting = true) } else { - addResultToEval(ProvResult(false, err = "$command not found.")) + addResult(false, err = "$command not found.") } } diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt index 387a2a7..af96248 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt @@ -316,7 +316,7 @@ internal class ProvTest { "============================================== SUMMARY (test instance with no progress info) =============================================\n" + "> \u001B[92mSuccess\u001B[0m -- session \n" + "---> \u001B[92mSuccess\u001B[0m -- testMethodForOutputTest_with_mode_requireLast (requireLast) \n" + - "------> \u001B[93mFAILED\u001B[0m -- checkPrereq_evaluateToFailure (requireLast) -- Error: This is a test error.\n" + + "------> \u001B[93mFAILED\u001B[0m -- checkPrereq_evaluateToFailure (requireLast) -- Error: This is a test error.\n" + "------> \u001B[92mSuccess\u001B[0m -- sh \n" + "---------> \u001B[92mSuccess\u001B[0m -- cmd [echo -Start test-]\n" + "---------> \u001B[92mSuccess\u001B[0m -- cmd [echo Some output]\n" + @@ -362,7 +362,7 @@ internal class ProvTest { "---> \u001B[91mFAILED\u001B[0m -- testMethodForOutputTest_nested_with_failure \n" + "------> \u001B[91mFAILED\u001B[0m -- sub1 \n" + "---------> \u001B[92mSuccess\u001B[0m -- testMethodForOutputTest_nested_with_failure \n" + - "---------> \u001B[91mFAILED\u001B[0m -- sub1 (returned result) -- Error: Iamanerrormessage\n" + + "---------> \u001B[91mFAILED\u001B[0m -- sub1 (returned result) -- Error: Iamanerrormessage\n" + "------> \u001B[92mSuccess\u001B[0m -- cmd [echo -End test-]\n" + "----------------------------------------------------------------------------------------------------\n" + "Overall > \u001B[91mFAILED\u001B[0m \n" + @@ -418,6 +418,51 @@ internal class ProvTest { assertEquals(expectedOutput, outContent.toString().replace("\r", "")) } + @Test + @NonCi + fun prov_prints_info_text_correctly() { + + // given + setRootLoggingLevel(Level.OFF) + + val outContent = ByteArrayOutputStream() + val errContent = ByteArrayOutputStream() + val originalOut = System.out + val originalErr = System.err + + System.setOut(PrintStream(outContent)) + System.setErr(PrintStream(errContent)) + + // when + Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE).task("taskA") { + taskWithResult("taskB") { + addResult(true, info = "Package A always was already installed.") + addResult(true, info = "Package B always was already installed.") + } + addResult(false, err = "Package C was missing", info = "Pls install package C.") + } + + // then + System.setOut(originalOut) + System.setErr(originalErr) + + println(outContent.toString()) + + val expectedOutput = + "============================================== SUMMARY (test instance with no progress info) =============================================\n" + + "> \u001B[91mFAILED\u001B[0m -- taskA \n" + + "---> \u001B[92mSuccess\u001B[0m -- taskB \n" + + "------> \u001B[92mSuccess\u001B[0m -- addResult -- Info: Package A always was already installed. \n" + + "------> \u001B[92mSuccess\u001B[0m -- addResult -- Info: Package B always was already installed. \n" + + "---> \u001B[91mFAILED\u001B[0m -- addResult -- Info: Pls install package C. -- Error: Package C was missing\n" + + "----------------------------------------------------------------------------------------------------\n" + + "Overall > \u001B[91mFAILED\u001B[0m \n" + + "============================================ SUMMARY END ===========================================\n" + + "\n" + + assertEquals(expectedOutput, outContent.toString().replace("\r", "")) + } + @Test fun chk_returnsTrue() { // when @@ -490,7 +535,7 @@ internal class ProvTest { fun addResultToEval_success() { // given fun Prov.inner() { - addResultToEval(ProvResult(true)) + addResult(true) } fun Prov.outer() = task { @@ -509,7 +554,7 @@ internal class ProvTest { fun task_with_subtask_and_failed_result_fails() { // given fun Prov.inner() { - addResultToEval(ProvResult(true)) + addResult(true) } fun Prov.outer() = taskWithResult { @@ -547,7 +592,7 @@ internal class ProvTest { fun addResultToEval_failure() { // given fun Prov.inner() { - addResultToEval(ProvResult(false)) + addResult(false) } fun Prov.outer() = taskWithResult { @@ -665,11 +710,9 @@ internal class ProvTest { } optional("sub2c-optional") { taskWithResult("sub3a-taskWithResult") { - addResultToEval( - ProvResult( - false, - err = "returned-result - error msg B should be once in output - in addResultToEval" - ) + addResult( + false, + err = "returned-result - error msg B should be once in output - in addResult" ) } } @@ -679,7 +722,7 @@ internal class ProvTest { } } task("sub2e-task") { - addResultToEval(ProvResult(true)) + addResult(true) ProvResult( false, err = "error should NOT be in output as results of task (not taskWithResult) are ignored" @@ -725,16 +768,16 @@ internal class ProvTest { "> \u001B[91mFAILED\u001B[0m -- testMethodForOutputTest_with_returned_results \n" + "---> \u001B[91mFAILED\u001B[0m -- sub1 \n" + "------> \u001B[92mSuccess\u001B[0m -- sub2a \n" + - "------> \u001B[91mFAILED\u001B[0m -- sub2b -- Error: error msg A for sub2b should be shown as result of sub2b\n" + + "------> \u001B[91mFAILED\u001B[0m -- sub2b -- Error: error msg A for sub2b should be shown as result of sub2b\n" + "------> \u001B[92mSuccess\u001B[0m -- sub2c-optional \n" + "---------> \u001B[93mFAILED\u001B[0m -- sub3a-taskWithResult \n" + - "------------> \u001B[93mFAILED\u001B[0m -- addResultToEval -- Error: returned-result - error msg B should be once in output - in addResultToEval\n" + + "------------> \u001B[93mFAILED\u001B[0m -- addResult -- Error: returned-result - error msg B should be once in output - in addResult\n" + "------> \u001B[91mFAILED\u001B[0m -- sub2d-requireLast \n" + "---------> \u001B[91mFAILED\u001B[0m -- sub3b-taskWithResult without error message \n" + "------> \u001B[92mSuccess\u001B[0m -- sub2e-task \n" + - "---------> \u001B[92mSuccess\u001B[0m -- addResultToEval \n" + - "------> \u001B[91mFAILED\u001B[0m -- sub2f-taskWithResult -- Error: returned-result - error msg C should be once in output - at the end of sub3taskWithResult \n" + - "------> \u001B[91mFAILED\u001B[0m -- sub1 (returned result) -- Error: returned-result - error msg D should be once in output - at the end of sub1 \n" + + "---------> \u001B[92mSuccess\u001B[0m -- addResult \n" + + "------> \u001B[91mFAILED\u001B[0m -- sub2f-taskWithResult -- Error: returned-result - error msg C should be once in output - at the end of sub3taskWithResult \n" + + "------> \u001B[91mFAILED\u001B[0m -- sub1 (returned result) -- Error: returned-result - error msg D should be once in output - at the end of sub1 \n" + "----------------------------------------------------------------------------------------------------\n" + "Overall > \u001B[91mFAILED\u001B[0m \n" + "============================================ SUMMARY END ===========================================\n" + diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/docker/platforms/UbuntuHostDockerKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/docker/platforms/UbuntuHostDockerKtTest.kt index 1c35fff..2be8106 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/docker/platforms/UbuntuHostDockerKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/docker/platforms/UbuntuHostDockerKtTest.kt @@ -18,7 +18,7 @@ internal class UbuntuHostDockerKtTest { val containerName = "testContainer" val result = testLocal().task { runContainer(containerName) - addResultToEval(ProvResult(containerRuns(containerName))) + addResult(containerRuns(containerName)) exitAndRmContainer(containerName) }