diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/testing/CliktTesting.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/testing/CliktTesting.kt index a2cabac91..836fafb58 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/testing/CliktTesting.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/testing/CliktTesting.kt @@ -8,6 +8,7 @@ import com.github.ajalt.clikt.parsers.shlex import com.github.ajalt.mordant.rendering.AnsiLevel import com.github.ajalt.mordant.terminal.Terminal import com.github.ajalt.mordant.terminal.TerminalRecorder +import kotlin.jvm.JvmName data class CliktCommandTestResult( /** Standard output captured from the command */ @@ -52,6 +53,33 @@ fun CliktCommand.test( return test(argvArray, stdin, envvars, includeSystemEnvvars, ansiLevel, width, height) } +/** + * Test this command, returning a result that captures the output and result status code. + * + * Note that only output printed with [echo][CliktCommand.echo] will be captured. Anything printed with [print] or + * [println] is not. + * + * @param argv The command line to send to the command + * @param stdin Content of stdin that will be read by prompt options. Multiple inputs should be separated by `\n`. + * @param envvars A map of environment variable name to value for envvars that can be read by the command + * @param includeSystemEnvvars Set to true to include the environment variables from the system in addition to those + * defined in [envvars] + * @param ansiLevel Defaults to no colored output; set to [AnsiLevel.TRUECOLOR] to include ANSI codes in the output. + * @param width The width of the terminal, used to wrap text + * @param height The height of the terminal + */ +@JvmName("varargTest") +fun CliktCommand.test( + vararg argv: String, + stdin: String = "", + envvars: Map = emptyMap(), + includeSystemEnvvars: Boolean = false, + ansiLevel: AnsiLevel = AnsiLevel.NONE, + width: Int = 79, + height: Int = 24, +): CliktCommandTestResult { + return test(argv.asList(), stdin, envvars, includeSystemEnvvars, ansiLevel, width, height) +} /** * Test this command, returning a result that captures the output and result status code. diff --git a/clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSource.kt b/clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSource.kt index e599b1dbd..942403661 100644 --- a/clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSource.kt +++ b/clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSource.kt @@ -57,19 +57,7 @@ object PropertiesValueSource { requireValid: Boolean = false, getKey: (Context, Option) -> String = ValueSource.getKey(joinSubcommands = "."), ): ValueSource { - val properties = Properties() - if (file.isFile) { - try { - file.bufferedReader().use { properties.load(it) } - } catch (e: Throwable) { - if (requireValid) throw InvalidFileFormat( - file.name, - e.message ?: "could not read file" - ) - } - } - - return from(properties, getKey) + return from(file.toPath(), requireValid, getKey) } /** diff --git a/docs/quickstart.md b/docs/quickstart.md index b46883ef5..8498c4d58 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -143,7 +143,7 @@ main function directly from within gradle. You can pass command line arguments t with the `--args` flag: ```shell -$ ./gradlew run --args="hello --count=3 --name=Clikt" +$ ./gradlew run --args="--count=3 Clikt" ``` A drawback to using the `run` gradle task is that it redirects stdout, so Clikt will not print diff --git a/docs/testing.md b/docs/testing.md index 56913ae52..f1f4e1ed6 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -42,7 +42,7 @@ You can set environment variables for your command by passing in a map of `envva @Test fun testHello() { val command = Hello() - val result = command.test(""", envvars=mapOf("HELLO_NAME" to "Foo")) + val result = command.test("", envvars=mapOf("HELLO_NAME" to "Foo")) assertEqual(result.stdout, "Hello, Foo!") } ``` diff --git a/samples/json/README.md b/samples/json/README.md index d5e1fe764..270d56680 100644 --- a/samples/json/README.md +++ b/samples/json/README.md @@ -1,6 +1,7 @@ # JSON Configuration files -This example shows how to read option values from JSON files using the kotlinx.serialization library. +This example shows how to read option values from JSON files using the `kotlinx.serialization` +library. It reads config files from the `config.json` file located in this directory. ``` ./runsample json subcommand diff --git a/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/JsonValueSource.kt b/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/JsonValueSource.kt index 824e2a8bf..412be16cb 100644 --- a/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/JsonValueSource.kt +++ b/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/JsonValueSource.kt @@ -5,10 +5,7 @@ import com.github.ajalt.clikt.core.InvalidFileFormat import com.github.ajalt.clikt.parameters.options.Option import com.github.ajalt.clikt.sources.ValueSource import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.* import java.io.File /** @@ -27,10 +24,17 @@ class JsonValueSource( } if (cursor == null) return emptyList() - // This implementation interprets a list as multiple invocations, but you could also - // implement it as a single invocation with multiple values. - if (cursor is JsonArray) return cursor.map { ValueSource.Invocation.value(it) } - return ValueSource.Invocation.just(cursor) + try { + // This implementation interprets a list as multiple invocations, but you could also + // implement it as a single invocation with multiple values. + if (cursor is JsonArray) return cursor.map { + ValueSource.Invocation.value(it.jsonPrimitive.content) + } + return ValueSource.Invocation.just(cursor.jsonPrimitive.content) + } catch (e: IllegalArgumentException) { + // This implementation skips invalid values, but you could handle them differently. + return emptyList() + } } companion object { @@ -41,12 +45,16 @@ class JsonValueSource( Json.parseToJsonElement(file.readText()) as? JsonObject ?: throw InvalidFileFormat(file.path, "object expected", 1) } catch (e: SerializationException) { - if (requireValid) throw InvalidFileFormat(file.name, e.message ?: "could not read file") + if (requireValid) { + throw InvalidFileFormat(file.name, e.message ?: "could not read file") + } JsonObject(emptyMap()) } return JsonValueSource(json) } - fun from(file: String, requireValid: Boolean = false): JsonValueSource = from(File(file), requireValid) + fun from(file: String, requireValid: Boolean = false): JsonValueSource { + return from(File(file), requireValid) + } } } diff --git a/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/main.kt b/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/main.kt index 9f7a45b8a..1f928c81b 100644 --- a/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/main.kt +++ b/samples/json/src/main/kotlin/com/github/ajalt/clikt/samples/json/main.kt @@ -20,7 +20,7 @@ class Cli : CliktCommand(help = "An example using json files for configuration v init { context { valueSources( - JsonValueSource.from(System.getProperty("user.dir") + "config.json"), + JsonValueSource.from(System.getProperty("user.dir") + "/config.json"), JsonValueSource.from(System.getProperty("user.dir") + "/samples/json/config.json") ) }