mirror of
https://github.com/apple/pkl.git
synced 2026-03-24 01:51:19 +01:00
Turn CLI commands into objects, self register subcommands (#935)
Usages of `RootCommand` no longer need to initialize a new instance, nor register subcommands.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.pkl.cli
|
||||
|
||||
import com.github.ajalt.clikt.core.BadParameterValue
|
||||
import com.github.ajalt.clikt.core.subcommands
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.createDirectory
|
||||
import kotlin.io.path.createSymbolicLinkPointingTo
|
||||
@@ -27,35 +26,34 @@ import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs
|
||||
import org.junit.jupiter.api.condition.OS
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.pkl.cli.commands.AnalyzeCommand
|
||||
import org.pkl.cli.commands.EvalCommand
|
||||
import org.pkl.cli.commands.RootCommand
|
||||
import org.pkl.commons.writeString
|
||||
import org.pkl.core.Release
|
||||
|
||||
class CliMainTest {
|
||||
|
||||
private val evalCmd = EvalCommand("")
|
||||
private val analyzeCommand = AnalyzeCommand("")
|
||||
private val cmd = RootCommand("pkl", "pkl version 1", "").subcommands(evalCmd, analyzeCommand)
|
||||
|
||||
@Test
|
||||
fun `duplicate CLI option produces meaningful error message`(@TempDir tempDir: Path) {
|
||||
val inputFile = tempDir.resolve("test.pkl").writeString("").toString()
|
||||
|
||||
assertThatCode {
|
||||
cmd.parse(arrayOf("eval", "--output-path", "path1", "--output-path", "path2", inputFile))
|
||||
RootCommand.parse(
|
||||
arrayOf("eval", "--output-path", "path1", "--output-path", "path2", inputFile)
|
||||
)
|
||||
}
|
||||
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
|
||||
|
||||
assertThatCode {
|
||||
cmd.parse(arrayOf("eval", "-o", "path1", "--output-path", "path2", inputFile))
|
||||
RootCommand.parse(arrayOf("eval", "-o", "path1", "--output-path", "path2", inputFile))
|
||||
}
|
||||
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `eval requires at least one file`() {
|
||||
assertThatCode { cmd.parse(arrayOf("eval")) }.hasMessage("""Missing argument "<modules>"""")
|
||||
assertThatCode { RootCommand.parse(arrayOf("eval")) }
|
||||
.hasMessage("""Missing argument "<modules>"""")
|
||||
}
|
||||
|
||||
// Can't reliably create symlinks on Windows.
|
||||
@@ -76,7 +74,7 @@ class CliMainTest {
|
||||
val inputFile = tempDir.resolve("test.pkl").writeString(code).toString()
|
||||
val outputFile = makeSymdir(tempDir, "out", "linkOut").resolve("test.pkl").toString()
|
||||
|
||||
assertThatCode { cmd.parse(arrayOf("eval", inputFile, "-o", outputFile)) }
|
||||
assertThatCode { RootCommand.parse(arrayOf("eval", inputFile, "-o", outputFile)) }
|
||||
.doesNotThrowAnyException()
|
||||
}
|
||||
|
||||
@@ -87,22 +85,24 @@ class CliMainTest {
|
||||
val error =
|
||||
"""Invalid value for "--multiple-file-output-path": Option is mutually exclusive with -o, --output-path and -x, --expression."""
|
||||
|
||||
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-x", "x", testIn)) }
|
||||
assertThatCode { RootCommand.parse(arrayOf("eval", "-m", testOut, "-x", "x", testIn)) }
|
||||
.hasMessage(error)
|
||||
|
||||
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-o", "/tmp/test", testIn)) }
|
||||
assertThatCode { RootCommand.parse(arrayOf("eval", "-m", testOut, "-o", "/tmp/test", testIn)) }
|
||||
.hasMessage(error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `showing version works`() {
|
||||
assertThatCode { cmd.parse(arrayOf("--version")) }.hasMessage("pkl version 1")
|
||||
assertThatCode { RootCommand.parse(arrayOf("--version")) }
|
||||
.hasMessage(Release.current().versionInfo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file paths get parsed into URIs`(@TempDir tempDir: Path) {
|
||||
cmd.parse(arrayOf("eval", makeInput(tempDir, "my file.txt")))
|
||||
RootCommand.parse(arrayOf("eval", makeInput(tempDir, "my file.txt")))
|
||||
|
||||
val evalCmd = RootCommand.registeredSubcommands().filterIsInstance<EvalCommand>().first()
|
||||
val modules = evalCmd.baseOptions.baseOptions(evalCmd.modules).normalizedSourceModules
|
||||
assertThat(modules).hasSize(1)
|
||||
assertThat(modules[0].path).endsWith("my file.txt")
|
||||
@@ -110,7 +110,8 @@ class CliMainTest {
|
||||
|
||||
@Test
|
||||
fun `invalid URIs are not accepted`() {
|
||||
val ex = assertThrows<BadParameterValue> { cmd.parse(arrayOf("eval", "file:my file.txt")) }
|
||||
val ex =
|
||||
assertThrows<BadParameterValue> { RootCommand.parse(arrayOf("eval", "file:my file.txt")) }
|
||||
|
||||
assertThat(ex.message).contains("URI `file:my file.txt` has invalid syntax")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,18 +16,16 @@
|
||||
package org.pkl.cli
|
||||
|
||||
import com.github.ajalt.clikt.core.MissingArgument
|
||||
import com.github.ajalt.clikt.core.subcommands
|
||||
import java.io.StringWriter
|
||||
import java.io.Writer
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatCode
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.pkl.cli.commands.EvalCommand
|
||||
import org.pkl.cli.commands.RootCommand
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.cli.CliException
|
||||
@@ -35,7 +33,6 @@ import org.pkl.commons.cli.CliTestOptions
|
||||
import org.pkl.commons.readString
|
||||
import org.pkl.commons.toUri
|
||||
import org.pkl.commons.writeString
|
||||
import org.pkl.core.Release
|
||||
|
||||
class CliTestRunnerTest {
|
||||
@Test
|
||||
@@ -381,7 +378,7 @@ class CliTestRunnerTest {
|
||||
val opts =
|
||||
CliBaseOptions(
|
||||
sourceModules = listOf(input.toUri(), input2.toUri()),
|
||||
settings = URI("pkl:settings")
|
||||
settings = URI("pkl:settings"),
|
||||
)
|
||||
val testOpts = CliTestOptions(junitDir = tempDir)
|
||||
val runner = CliTestRunner(opts, testOpts, noopWriter, noopWriter)
|
||||
@@ -391,12 +388,7 @@ class CliTestRunnerTest {
|
||||
@Test
|
||||
fun `no source modules specified has same message as pkl eval`() {
|
||||
val e1 = assertThrows<CliException> { CliTestRunner(CliBaseOptions(), CliTestOptions()).run() }
|
||||
val e2 =
|
||||
assertThrows<MissingArgument> {
|
||||
val rootCommand =
|
||||
RootCommand("pkl", Release.current().versionInfo(), "").subcommands(EvalCommand(""))
|
||||
rootCommand.parse(listOf("eval"))
|
||||
}
|
||||
val e2 = assertThrows<MissingArgument> { RootCommand.parse(listOf("eval")) }
|
||||
assertThat(e1).hasMessageContaining("Missing argument \"<modules>\"")
|
||||
assertThat(e1.message!!.replace("test", "eval")).isEqualTo(e2.helpMessage())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user