From 7ea9c2fadd92a9b978f2c39e4ec9621a20c03baf 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 | 8 +- .../cli/command/SvgXmlToImageVectorCommand.kt | 5 +- .../valkyrie/cli/IconPackCliTest.kt | 193 ++++++++ .../valkyrie/cli/IntegrationTest.kt | 51 -- .../cli/SvgXmlToImageVectorCliTest.kt | 463 ++++++++++++++++++ .../valkyrie/cli/common/CliTestType.kt | 6 + .../cli/{ => common}/CommandLineTestRunner.kt | 18 +- .../cli/common/OutputFormatResource.kt | 14 + 9 files changed, 707 insertions(+), 75 deletions(-) create mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IconPackCliTest.kt delete mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IntegrationTest.kt create mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/SvgXmlToImageVectorCliTest.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%) create mode 100644 cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/OutputFormatResource.kt 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..26370797 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,12 +5,13 @@ 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 import io.github.composegears.valkyrie.cli.ext.requiredPathOption import io.github.composegears.valkyrie.cli.ext.requiredStringOption +import io.github.composegears.valkyrie.cli.ext.stringOption import io.github.composegears.valkyrie.extensions.writeToKt import io.github.composegears.valkyrie.generator.iconpack.IconPackGenerator import io.github.composegears.valkyrie.generator.iconpack.IconPackGeneratorConfig @@ -41,15 +42,16 @@ internal class IconPackCommand : CliktCommand(name = "iconpack") { help = "Package name of IconPack object", ) - private val iconPackName by requiredStringOption( + private val iconPackName by stringOption( "--iconpack-name", help = "IconPack object name", + default = "", ) 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/main/kotlin/io/github/composegears/valkyrie/cli/command/SvgXmlToImageVectorCommand.kt b/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/SvgXmlToImageVectorCommand.kt index 38ea22d3..0174ea87 100644 --- a/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/SvgXmlToImageVectorCommand.kt +++ b/cli/src/main/kotlin/io/github/composegears/valkyrie/cli/command/SvgXmlToImageVectorCommand.kt @@ -58,9 +58,10 @@ internal class SvgXmlToImageVectorCommand : CliktCommand(name = "svgxml2imagevec help = "The package name of the generated sources. Usually equal to IconPack package", ) - private val iconPackName by requiredStringOption( + private val iconPackName by stringOption( "--iconpack-name", help = "The name of the existing IconPack", + default = "", ) private val nestedPackName by stringOption( @@ -136,8 +137,6 @@ private fun CliktCommand.outputFormatOption() = option( } }.default(OutputFormat.BackingProperty) -internal const val SUCCESS_MESSAGE = "Converting completed." - /** * Converts SVG or XML files to ImageVector files. */ 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..f35bfe88 --- /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(IconPackCommand::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/IntegrationTest.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IntegrationTest.kt deleted file mode 100644 index 2e7b4062..00000000 --- a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/IntegrationTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.composegears.valkyrie.cli - -import io.github.composegears.valkyrie.extensions.ResourceUtils.getResourcePath -import io.github.composegears.valkyrie.generator.imagevector.OutputFormat -import java.nio.file.Path -import kotlin.io.path.createTempDirectory -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource - -class IntegrationTest { - - @ParameterizedTest - @Disabled - @MethodSource("testMatrix") - fun exportAndImport(arg: Pair) { - val (useCli, outputFormat) = arg - - val inputXml = getResourcePath("/xml") - val inputSvg = getResourcePath("/svg") - val outputDir = tempDir.resolve("kt") - } - - companion object { - // Workaround for https://github.com/junit-team/junit5/issues/2811. - @JvmStatic - private lateinit var tempDir: Path - - @JvmStatic - @BeforeAll - fun before() { - tempDir = createTempDirectory() - } - - @JvmStatic - @AfterAll - fun after() { - tempDir.toFile().delete() - } - - @JvmStatic - fun testMatrix() = listOf( - false to OutputFormat.BackingProperty, - false to OutputFormat.LazyProperty, - true to OutputFormat.BackingProperty, - true to OutputFormat.LazyProperty, - ) - } -} diff --git a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/SvgXmlToImageVectorCliTest.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/SvgXmlToImageVectorCliTest.kt new file mode 100644 index 00000000..cc49a788 --- /dev/null +++ b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/SvgXmlToImageVectorCliTest.kt @@ -0,0 +1,463 @@ +package io.github.composegears.valkyrie.cli + +import assertk.assertThat +import assertk.assertions.isEqualTo +import io.github.composegears.valkyrie.cli.SvgXmlCommand.AddTrailingComma +import io.github.composegears.valkyrie.cli.SvgXmlCommand.GeneratePreview +import io.github.composegears.valkyrie.cli.SvgXmlCommand.IconPackName +import io.github.composegears.valkyrie.cli.SvgXmlCommand.ImageVectorOutputFormat +import io.github.composegears.valkyrie.cli.SvgXmlCommand.IndentSize +import io.github.composegears.valkyrie.cli.SvgXmlCommand.InputPath +import io.github.composegears.valkyrie.cli.SvgXmlCommand.NestedPackName +import io.github.composegears.valkyrie.cli.SvgXmlCommand.OutputPath +import io.github.composegears.valkyrie.cli.SvgXmlCommand.PackageName +import io.github.composegears.valkyrie.cli.SvgXmlCommand.UseExplicitMode +import io.github.composegears.valkyrie.cli.SvgXmlCommand.UseFlatPackage +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.cli.common.toResourceText +import io.github.composegears.valkyrie.extensions.ResourceUtils.getResourcePath +import io.github.composegears.valkyrie.generator.imagevector.OutputFormat +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.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class SvgXmlToImageVectorCliTest { + + private var tempDir: Path by Delegates.notNull() + + @BeforeEach + fun before() { + tempDir = createTempDirectory() + } + + @AfterEach + fun after() { + tempDir.toFile().delete() + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `batch processing xml`(arg: Pair) { + val (cliTestType, outputFormat) = arg + val input = getResourcePath("imagevector/xml") + + convert( + cliTestType = cliTestType, + svgXmlCommands = listOfNotNull( + InputPath(input.absolutePathString()), + OutputPath(tempDir.absolutePathString()), + PackageName("io.github.composegears.valkyrie.icons"), + ImageVectorOutputFormat(outputFormat), + ), + ) + + val files = tempDir.toFile().listFiles().orEmpty() + assertThat(files.size).isEqualTo(10) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `batch processing svg`(arg: Pair) { + val (cliTestType, outputFormat) = arg + val input = getResourcePath("imagevector/svg") + + convert( + cliTestType = cliTestType, + svgXmlCommands = listOfNotNull( + InputPath(input.absolutePathString()), + OutputPath(tempDir.absolutePathString()), + PackageName("io.github.composegears.valkyrie.icons"), + ImageVectorOutputFormat(outputFormat), + ), + ) + + val files = tempDir.toFile().listFiles().orEmpty() + assertThat(files.size).isEqualTo(3) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `flat package without icon pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_flat_package.xml", + expectedKtName = "FlatPackage.kt", + useFlatPackage = UseFlatPackage(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `flat package with icon pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_flat_package.xml", + expectedKtName = "FlatPackage.pack.kt", + actualKtName = "FlatPackage.kt", + iconPackName = IconPackName("ValkyrieIcons"), + useFlatPackage = UseFlatPackage(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `flat package with nested icon pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_flat_package.xml", + expectedKtName = "FlatPackage.pack.nested.kt", + actualKtName = "FlatPackage.kt", + iconPackName = IconPackName("ValkyrieIcons"), + nestedPackName = NestedPackName("Filled"), + useFlatPackage = UseFlatPackage(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `generation with explicit mode`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.explicit.kt", + actualKtName = "WithoutPath.kt", + useExplicitMode = UseExplicitMode(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `generation without icon pack with indent 1`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.indent1.kt", + actualKtName = "WithoutPath.kt", + indentSize = IndentSize(1), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `generation without icon pack with indent 2`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.indent2.kt", + actualKtName = "WithoutPath.kt", + indentSize = IndentSize(2), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `generation without icon pack with indent 3`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.indent3.kt", + actualKtName = "WithoutPath.kt", + indentSize = IndentSize(3), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `generation without icon pack with indent 6`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.indent6.kt", + actualKtName = "WithoutPath.kt", + indentSize = IndentSize(6), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `preview generation without icon pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.preview.kt", + actualKtName = "WithoutPath.kt", + generatePreview = GeneratePreview(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `preview generation with icon pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.pack.preview.kt", + actualKtName = "WithoutPath.kt", + iconPackName = IconPackName("ValkyrieIcons"), + generatePreview = GeneratePreview(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `preview generation with nested pack`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.pack.nested.preview.kt", + actualKtName = "WithoutPath.kt", + iconPackName = IconPackName("ValkyrieIcons"), + nestedPackName = NestedPackName("Filled"), + generatePreview = GeneratePreview(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `empty path xml`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_without_path.xml", + expectedKtName = "WithoutPath.pack.kt", + actualKtName = "WithoutPath.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon only with path`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_only_path.xml", + expectedKtName = "OnlyPath.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with path and solid color`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_fill_color_stroke.xml", + expectedKtName = "FillColorStroke.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with path and solid color trailing`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_fill_color_stroke.xml", + expectedKtName = "FillColorStroke.trailing.kt", + actualKtName = "FillColorStroke.kt", + iconPackName = IconPackName("ValkyrieIcons"), + addTrailingComma = AddTrailingComma(true), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with all path params`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_all_path_params.xml", + expectedKtName = "AllPathParams.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with all group params`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_all_group_params.xml", + expectedKtName = "AllGroupParams.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with several path`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_several_path.xml", + expectedKtName = "SeveralPath.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with transparent fill color`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/ic_transparent_fill_color.xml", + expectedKtName = "TransparentFillColor.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with named arguments`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/icon_with_named_args.xml", + expectedKtName = "IconWithNamedArgs.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `icon with shorthand color`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/xml/icon_with_shorthand_color.xml", + expectedKtName = "IconWithShorthandColor.kt", + iconPackName = IconPackName("ValkyrieIcons"), + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `svg linear gradient parsing`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/svg/ic_linear_gradient.svg", + expectedKtName = "LinearGradient.kt", + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `svg radial gradient parsing`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/svg/ic_radial_gradient.svg", + expectedKtName = "RadialGradient.kt", + ) + } + + @ParameterizedTest + @MethodSource("testMatrix") + fun `svg linear gradient with stroke parsing`(arg: Pair) { + arg.testConversion( + inputResource = "imagevector/svg/ic_linear_gradient_with_stroke.svg", + expectedKtName = "LinearGradientWithStroke.kt", + ) + } + + private fun Pair.testConversion( + inputResource: String, + expectedKtName: String, + actualKtName: String = expectedKtName, + iconPackName: IconPackName? = null, + useExplicitMode: UseExplicitMode? = null, + nestedPackName: NestedPackName? = null, + useFlatPackage: UseFlatPackage? = null, + indentSize: IndentSize? = null, + generatePreview: GeneratePreview? = null, + addTrailingComma: AddTrailingComma? = null, + ) { + val (cliTestType, outputFormat) = this + val input = getResourcePath(inputResource) + + convert( + cliTestType = cliTestType, + svgXmlCommands = listOfNotNull( + InputPath(input.absolutePathString()), + OutputPath(tempDir.absolutePathString()), + PackageName("io.github.composegears.valkyrie.icons"), + iconPackName, + nestedPackName, + ImageVectorOutputFormat(outputFormat), + generatePreview, + useFlatPackage, + useExplicitMode, + addTrailingComma, + indentSize, + ), + ) + + val expected = outputFormat.toResourceText( + pathToBackingProperty = "imagevector/kt/backing/$expectedKtName", + pathToLazyProperty = "imagevector/kt/lazy/$expectedKtName", + ) + + val files = tempDir.toFile().listFiles().orEmpty() + assertThat(files.size).isEqualTo(1) + + val result = tempDir.resolve(actualKtName).readText() + assertThat(result).isEqualTo(expected) + } + + private fun convert( + cliTestType: CliTestType, + svgXmlCommands: List, + ) { + val commands = listOf("svgxml2imagevector") + svgXmlCommands.map(SvgXmlCommand::command) + + when (cliTestType) { + DirectMain -> main(*commands.toTypedArray()) + JarTerminal -> CommandLineTestRunner(commands).run() + } + } + + companion object { + @JvmStatic + fun testMatrix() = listOf( + DirectMain to OutputFormat.BackingProperty, + DirectMain to OutputFormat.LazyProperty, + JarTerminal to OutputFormat.BackingProperty, + JarTerminal to OutputFormat.LazyProperty, + ) + } +} + +private sealed interface SvgXmlCommand { + val command: String + + data class InputPath(val path: String) : SvgXmlCommand { + override val command: String = "--input-path=$path" + } + + data class OutputPath(val path: String) : SvgXmlCommand { + override val command: String = "--output-path=$path" + } + + data class PackageName(val name: String) : SvgXmlCommand { + override val command: String = "--package-name=$name" + } + + data class IconPackName(val name: String) : SvgXmlCommand { + override val command: String = "--iconpack-name=$name" + } + + data class NestedPackName(val name: String) : SvgXmlCommand { + override val command: String = "--nested-pack-name=$name" + } + + data class ImageVectorOutputFormat(val format: OutputFormat) : SvgXmlCommand { + override val command: String + get() { + val format = when (format) { + OutputFormat.BackingProperty -> "backing-property" + OutputFormat.LazyProperty -> "lazy-property" + } + return "--output-format=$format" + } + } + + data class GeneratePreview(val value: Boolean) : SvgXmlCommand { + override val command: String = "--generate-preview=$value" + } + + data class UseFlatPackage(val value: Boolean) : SvgXmlCommand { + override val command: String = "--flatpackage=$value" + } + + data class UseExplicitMode(val value: Boolean) : SvgXmlCommand { + override val command: String = "--explicit-mode=$value" + } + + data class AddTrailingComma(val value: Boolean) : SvgXmlCommand { + override val command: String = "--trailing-comma=$value" + } + + data class IndentSize(val size: Int) : SvgXmlCommand { + override val command: String = "--indent-size=$size" + } +} 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) } } diff --git a/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/OutputFormatResource.kt b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/OutputFormatResource.kt new file mode 100644 index 00000000..e09f1a9a --- /dev/null +++ b/cli/src/test/kotlin/io/github/composegears/valkyrie/cli/common/OutputFormatResource.kt @@ -0,0 +1,14 @@ +package io.github.composegears.valkyrie.cli.common + +import io.github.composegears.valkyrie.extensions.ResourceUtils.getResourceText +import io.github.composegears.valkyrie.generator.imagevector.OutputFormat +import io.github.composegears.valkyrie.generator.imagevector.OutputFormat.BackingProperty +import io.github.composegears.valkyrie.generator.imagevector.OutputFormat.LazyProperty + +fun OutputFormat.toResourceText( + pathToBackingProperty: String, + pathToLazyProperty: String, +): String = when (this) { + BackingProperty -> getResourceText(pathToBackingProperty) + LazyProperty -> getResourceText(pathToLazyProperty) +}