add info to ProvResult & rename addProvResult to addResult

This commit is contained in:
ansgarz 2025-02-02 20:09:58 +01:00
parent 75f1521814
commit f33ce834f9
8 changed files with 107 additions and 58 deletions
.run
doc
src
main/kotlin/org/domaindrivenarchitecture/provs
desktop/infrastructure
framework
core
ubuntu/scheduledjobs/infrastructure
test/kotlin/org/domaindrivenarchitecture/provs/framework/core

View file

@ -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="" />

View file

@ -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

View file

@ -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")

View file

@ -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 {

View file

@ -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 ""
}
}

View file

@ -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.")
}
}

View file

@ -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" +

View file

@ -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)
}