From 7433f380907659ca4d3d8aee587b95c7ff45687b Mon Sep 17 00:00:00 2001 From: Yahor Urbanovich Date: Wed, 20 Nov 2024 21:04:10 +0300 Subject: [PATCH] Cover CLI by tests --- cli/build.gradle.kts | 24 ++- .../valkyrie/cli/command/IconPackCommand.kt | 4 +- .../valkyrie/cli/IconPackCliTest.kt | 193 ++++++++++++++++++ .../valkyrie/cli/common/CliTestType.kt | 6 + .../cli/{ => common}/CommandLineTestRunner.kt | 18 +- 5 files changed, 225 insertions(+), 20 deletions(-) create mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IconPackCliTest.kt create mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CliTestType.kt rename cli/src/test/kotlin/io/github/composegears/valkyrie/cli/{ => common}/CommandLineTestRunner.kt (56%) diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index e174cf93..57d409fa 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -8,6 +8,20 @@ plugins { val baseName = "valkyrie" val versionName = rootProject.providers.gradleProperty("VERSION_NAME").get() +application { + mainClass = "io.github.composegears.valkyrie.cli.MainKt" + applicationName = "valkyrie" + version = versionName +} + +sourceSets { + test { + resources { + srcDir("$rootDir/components/sharedTestResources") + } + } +} + buildConfig { buildConfigField("VERSION_NAME", versionName) packageName = "io.github.composegears.valkyrie.cli" @@ -22,11 +36,6 @@ tasks.withType().configureEach { attributes["Implementation-Version"] = versionName } } -application { - mainClass = "io.github.composegears.valkyrie.cli.MainKt" - applicationName = "valkyrie" - version = versionName -} val buildWithR8 by tasks.registering(JavaExec::class) { dependsOn(tasks.installShadowDist) @@ -59,6 +68,11 @@ val buildCLI by tasks.registering(Zip::class) { destinationDirectory.set(layout.buildDirectory.dir("distributions/")) } +tasks.test { + dependsOn(buildWithR8) + systemProperty("CLI_PATH", layout.buildDirectory.file("install/cli-shadow/bin").get().asFile.path) +} + val r8: Configuration by configurations.creating dependencies { diff --git a/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/IconPackCommand.kt b/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/IconPackCommand.kt index 4b0da5dd..a90fbe4b 100644 --- a/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/IconPackCommand.kt +++ b/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/IconPackCommand.kt @@ -5,7 +5,7 @@ import com.github.ajalt.clikt.core.Context import com.github.ajalt.clikt.core.context import com.github.ajalt.clikt.output.MordantHelpFormatter import com.github.ajalt.clikt.parameters.options.option -import com.github.ajalt.clikt.parameters.options.varargValues +import com.github.ajalt.clikt.parameters.options.split import io.github.composegears.valkyrie.cli.ext.booleanOption import io.github.composegears.valkyrie.cli.ext.intOption import io.github.composegears.valkyrie.cli.ext.outputInfo @@ -49,7 +49,7 @@ internal class IconPackCommand : CliktCommand(name = "iconpack") { private val nestedPacks by option( "--nested-packs", help = "Nested packs (e.g. Filled, Colored)", - ).varargValues() + ).split(",") private val indentSize by intOption( "--indent-size", diff --git a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IconPackCliTest.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IconPackCliTest.kt new file mode 100644 index 00000000..5db11cb3 --- /dev/null +++ b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IconPackCliTest.kt @@ -0,0 +1,193 @@ +package io.github.composegears.valkyrie.cli + +import assertk.assertThat +import assertk.assertions.isEqualTo +import io.github.composegears.valkyrie.cli.IconPackCommand.IconPackName +import io.github.composegears.valkyrie.cli.IconPackCommand.IndentSize +import io.github.composegears.valkyrie.cli.IconPackCommand.NestedPacks +import io.github.composegears.valkyrie.cli.IconPackCommand.OutputPath +import io.github.composegears.valkyrie.cli.IconPackCommand.PackageName +import io.github.composegears.valkyrie.cli.IconPackCommand.UseExplicitMode +import io.github.composegears.valkyrie.cli.common.CliTestType +import io.github.composegears.valkyrie.cli.common.CliTestType.DirectMain +import io.github.composegears.valkyrie.cli.common.CliTestType.JarTerminal +import io.github.composegears.valkyrie.cli.common.CommandLineTestRunner +import io.github.composegears.valkyrie.extensions.ResourceUtils.getResourceText +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.createTempDirectory +import kotlin.io.path.readText +import kotlin.properties.Delegates +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +class IconPackCliTest { + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate icon pack`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.kt", + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate icon pack explicit mode`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.explicit.kt", + useExplicitMode = UseExplicitMode(true), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested packs`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested packs explicit`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.explicit.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + useExplicitMode = UseExplicitMode(true), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested indent 1 packs`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.indent1.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + indentSize = IndentSize(1), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested indent 2 packs`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.indent2.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + indentSize = IndentSize(2), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested indent 3 packs`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.indent3.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + indentSize = IndentSize(3), + ) + } + + @ParameterizedTest + @EnumSource(value = CliTestType::class) + fun `generate nested indent 6 packs`(cliTestType: CliTestType) { + testIconPack( + cliTestType = cliTestType, + expectedResource = "iconpack/IconPack.nested.indent6.kt", + nestedPacks = NestedPacks(listOf("Filled", "Colored")), + indentSize = IndentSize(6), + ) + } + + private fun testIconPack( + cliTestType: CliTestType, + expectedResource: String, + useExplicitMode: UseExplicitMode? = null, + nestedPacks: NestedPacks? = null, + indentSize: IndentSize? = null, + ) { + generateIconPack( + cliTestType = cliTestType, + packCommands = listOfNotNull( + OutputPath(tempDir.absolutePathString()), + PackageName(name = "io.github.composegears.valkyrie.icons"), + IconPackName(name = "ValkyrieIcons"), + useExplicitMode, + nestedPacks, + indentSize, + ), + ) + + val files = tempDir.toFile().listFiles().orEmpty() + assertThat(files.size).isEqualTo(1) + + val result = tempDir.resolve("ValkyrieIcons.kt").readText() + val expected = getResourceText(expectedResource) + assertThat(result).isEqualTo(expected) + } + + private fun generateIconPack(cliTestType: CliTestType, packCommands: List) { + val commands = listOf("iconpack") + packCommands.map { it.command } + + when (cliTestType) { + DirectMain -> main(*commands.toTypedArray()) + JarTerminal -> CommandLineTestRunner(commands).run() + } + } + + companion object { + // Workaround for https://github.com/junit-team/junit5/issues/2811. + @JvmStatic + private var tempDir: Path by Delegates.notNull() + + @JvmStatic + @BeforeAll + fun before() { + tempDir = createTempDirectory() + } + + @JvmStatic + @AfterAll + fun after() { + tempDir.toFile().delete() + } + } +} + +private sealed interface IconPackCommand { + val command: String + + data class OutputPath(val path: String) : IconPackCommand { + override val command: String = "--output-path=$path" + } + + data class PackageName(val name: String) : IconPackCommand { + override val command: String = "--package-name=$name" + } + + data class IconPackName(val name: String) : IconPackCommand { + override val command: String = "--iconpack-name=$name" + } + + data class NestedPacks(val packs: List) : IconPackCommand { + override val command: String = "--nested-packs=${packs.joinToString(separator = ",")}" + } + + data class IndentSize(val size: Int) : IconPackCommand { + override val command: String = "--indent-size=$size" + } + + data class UseExplicitMode(val use: Boolean) : IconPackCommand { + override val command: String = "--use-explicit-mode=$use" + } +} diff --git a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CliTestType.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CliTestType.kt new file mode 100644 index 00000000..8b6267ce --- /dev/null +++ b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CliTestType.kt @@ -0,0 +1,6 @@ +package io.github.composegears.valkyrie.cli.common + +enum class CliTestType { + DirectMain, + JarTerminal, +} diff --git a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/CommandLineTestRunner.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CommandLineTestRunner.kt similarity index 56% rename from cli/src/test/kotlin/io/github/composegears/valkyrie/cli/CommandLineTestRunner.kt rename to cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CommandLineTestRunner.kt index bbb56207..b4154c2e 100644 --- a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/CommandLineTestRunner.kt +++ b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/CommandLineTestRunner.kt @@ -1,6 +1,4 @@ -package io.github.composegears.valkyrie.cli - -import io.github.composegears.valkyrie.cli.command.SUCCESS_MESSAGE +package io.github.composegears.valkyrie.cli.common class CommandLineTestRunner( private var commands: List, @@ -11,16 +9,10 @@ class CommandLineTestRunner( val exitCode = process.waitFor() val err = process.errorStream.readBytes().toString(Charsets.UTF_8) - val out = process.inputStream.readBytes().toString(Charsets.UTF_8).let { - // If ANSI escape codes are not supported, remove them from the output - if (System.console() == null) it.replace("\u001B\\[.*?m".toRegex(), "") else it - } + if (exitCode != 0 || err.isNotEmpty()) { error("Error occurred when running command line: $err") } - if (!out.startsWith(SUCCESS_MESSAGE)) { - error("Output is not correct: $out") - } } companion object { @@ -28,11 +20,11 @@ class CommandLineTestRunner( private val cliPath = System.getProperty("CLI_PATH") ?: error("CLI_PATH must not be null.") private fun cliCommand(arguments: List) = buildList { - // Binary Jar is not executable on Windows. if (isWindows) { - addAll(listOf("java", "-jar")) + add("$cliPath/valkyrie.bat") + } else { + add("$cliPath/valkyrie") } - add(cliPath) addAll(arguments) } }