| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- //===----------------------------------------------------------------------===//
- //
- // This source file is part of the Swift Argument Parser open source project
- //
- // Copyright (c) 2020 Apple Inc. and the Swift project authors
- // Licensed under Apache License v2.0 with Runtime Library Exception
- //
- // See https://swift.org/LICENSE.txt for license information
- //
- //===----------------------------------------------------------------------===//
- import ArgumentParserTestHelpers
- import ArgumentParserToolInfo
- import XCTest
- @testable import ArgumentParser
- final class DefaultSubcommandEndToEndTests: XCTestCase {
- }
- // MARK: -
- private struct Main: ParsableCommand {
- static let configuration = CommandConfiguration(
- subcommands: [Default.self, Foo.self, Bar.self],
- defaultSubcommand: Default.self
- )
- }
- private struct Default: ParsableCommand {
- enum Mode: String, CaseIterable, ExpressibleByArgument {
- case foo, bar, baz
- }
- @Option var mode: Mode = .foo
- }
- private struct Foo: ParsableCommand {}
- private struct Bar: ParsableCommand {}
- extension DefaultSubcommandEndToEndTests {
- func testDefaultSubcommand() {
- AssertParseCommand(Main.self, Default.self, []) { def in
- XCTAssertEqual(.foo, def.mode)
- }
- AssertParseCommand(Main.self, Default.self, ["--mode=bar"]) { def in
- XCTAssertEqual(.bar, def.mode)
- }
- AssertParseCommand(Main.self, Default.self, ["--mode", "bar"]) { def in
- XCTAssertEqual(.bar, def.mode)
- }
- AssertParseCommand(Main.self, Default.self, ["--mode", "baz"]) { def in
- XCTAssertEqual(.baz, def.mode)
- }
- }
- func testNonDefaultSubcommand() {
- AssertParseCommand(Main.self, Foo.self, ["foo"]) { _ in }
- AssertParseCommand(Main.self, Bar.self, ["bar"]) { _ in }
- AssertParseCommand(Main.self, Default.self, ["default", "--mode", "bar"]) {
- def in
- XCTAssertEqual(.bar, def.mode)
- }
- }
- func testParsingFailure() {
- XCTAssertThrowsError(try Main.parseAsRoot(["--mode", "qux"]))
- XCTAssertThrowsError(try Main.parseAsRoot(["qux"]))
- }
- }
- extension DefaultSubcommandEndToEndTests {
- fileprivate struct MyCommand: ParsableCommand {
- static let configuration = CommandConfiguration(
- subcommands: [
- Plugin.self, NonDefault.self, Other.self, Child.self, BadParent.self,
- ],
- defaultSubcommand: Plugin.self
- )
- @Option var foo: String?
- @OptionGroup
- var options: CommonOptions
- }
- fileprivate struct CommonOptions: ParsableArguments {
- @Flag(
- name: [.customLong("verbose"), .customShort("v")],
- help: "Enable verbose aoutput.")
- var verbose = false
- }
- fileprivate struct Plugin: ParsableCommand {
- @OptionGroup var options: CommonOptions
- @Argument var pluginName: String
- @Argument(parsing: .captureForPassthrough)
- var pluginArguments: [String] = []
- }
- fileprivate struct NonDefault: ParsableCommand {
- @OptionGroup var options: CommonOptions
- @Argument var pluginName: String
- @Argument(parsing: .captureForPassthrough)
- var pluginArguments: [String] = []
- }
- fileprivate struct Other: ParsableCommand {
- @OptionGroup var options: CommonOptions
- }
- fileprivate struct Child: ParsableCommand {
- @ParentCommand var parent: MyCommand
- }
- fileprivate struct BadParent: ParsableCommand {
- @ParentCommand var notMyParent: Other
- }
- func testAccessToParent() throws {
- AssertParseCommand(
- MyCommand.self, Child.self, ["--verbose", "--foo=bar", "child"]
- ) { child in
- XCTAssertEqual(child.parent.foo, "bar")
- XCTAssertEqual(child.parent.options.verbose, true)
- }
- }
- func testNotMyParent() throws {
- AssertParseCommandErrorMessage(
- MyCommand.self, BadParent.self, ["--verbose", "bad-parent"],
- "Command 'Other' is not a parent of the current command.")
- }
- func testNotLeakingParentOptions() throws {
- // Verify that the help for the child command doesn't leak the parent command's options in the help
- let childHelp = MyCommand.message(for: CleanExit.helpRequest(Child.self))
- XCTAssertEqual(
- childHelp,
- """
- USAGE: my-command child
- OPTIONS:
- -h, --help Show help information.
- """)
- // Now check that the foo option doesn't leak into the JSON dump
- let toolInfo = ToolInfoV0(commandStack: [MyCommand.self.asCommand])
- let arguments = toolInfo.command.arguments
- guard let arguments else {
- XCTFail(
- "MyCommand is expected to have a top-level command arguments in its tool info"
- )
- return
- }
- let subcommands = toolInfo.command.subcommands
- guard let subcommands else {
- XCTFail(
- "MyCommand is expected to have a top-level command arguments in its tool info"
- )
- return
- }
- // The foo option is present int he parent
- XCTAssertNotNil(arguments.first { $0.valueName == "foo" })
- let childInfo = subcommands.first { cmd in
- cmd.commandName == "child"
- }
- guard let childInfo else {
- XCTFail("The child subcommand is expected to be present in the tool info")
- return
- }
- guard let childArguments = childInfo.arguments else {
- XCTFail(
- "The child subcommand is expected to have arguments in the tool info")
- return
- }
- // It's not there in the child subcommand
- XCTAssertNil(childArguments.first { $0.valueName == "foo" })
- }
- func testRemainingDefaultImplicit() throws {
- AssertParseCommand(MyCommand.self, Plugin.self, ["my-plugin"]) { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, [])
- XCTAssertEqual(plugin.options.verbose, false)
- }
- AssertParseCommand(MyCommand.self, Plugin.self, ["my-plugin", "--verbose"])
- { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--verbose"])
- XCTAssertEqual(plugin.options.verbose, false)
- }
- AssertParseCommand(
- MyCommand.self, Plugin.self, ["--verbose", "my-plugin", "--verbose"]
- ) { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--verbose"])
- XCTAssertEqual(plugin.options.verbose, true)
- }
- AssertParseCommand(MyCommand.self, Plugin.self, ["my-plugin", "--help"]) {
- plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--help"])
- XCTAssertEqual(plugin.options.verbose, false)
- }
- }
- func testRemainingDefaultExplicit() throws {
- AssertParseCommand(MyCommand.self, Plugin.self, ["plugin", "my-plugin"]) {
- plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, [])
- XCTAssertEqual(plugin.options.verbose, false)
- }
- AssertParseCommand(
- MyCommand.self, Plugin.self, ["plugin", "my-plugin", "--verbose"]
- ) { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--verbose"])
- XCTAssertEqual(plugin.options.verbose, false)
- }
- AssertParseCommand(
- MyCommand.self, Plugin.self,
- ["--verbose", "plugin", "my-plugin", "--verbose"]
- ) { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--verbose"])
- XCTAssertEqual(plugin.options.verbose, true)
- }
- AssertParseCommand(
- MyCommand.self, Plugin.self,
- ["--verbose", "plugin", "my-plugin", "--help"]
- ) { plugin in
- XCTAssertEqual(plugin.pluginName, "my-plugin")
- XCTAssertEqual(plugin.pluginArguments, ["--help"])
- XCTAssertEqual(plugin.options.verbose, true)
- }
- }
- func testRemainingNonDefault() throws {
- AssertParseCommand(
- MyCommand.self, NonDefault.self, ["non-default", "my-plugin"]
- ) { nondef in
- XCTAssertEqual(nondef.pluginName, "my-plugin")
- XCTAssertEqual(nondef.pluginArguments, [])
- XCTAssertEqual(nondef.options.verbose, false)
- }
- AssertParseCommand(
- MyCommand.self, NonDefault.self,
- ["non-default", "my-plugin", "--verbose"]
- ) { nondef in
- XCTAssertEqual(nondef.pluginName, "my-plugin")
- XCTAssertEqual(nondef.pluginArguments, ["--verbose"])
- XCTAssertEqual(nondef.options.verbose, false)
- }
- AssertParseCommand(
- MyCommand.self, NonDefault.self,
- ["--verbose", "non-default", "my-plugin", "--verbose"]
- ) { nondef in
- XCTAssertEqual(nondef.pluginName, "my-plugin")
- XCTAssertEqual(nondef.pluginArguments, ["--verbose"])
- XCTAssertEqual(nondef.options.verbose, true)
- }
- AssertParseCommand(
- MyCommand.self, NonDefault.self,
- ["--verbose", "non-default", "my-plugin", "--help"]
- ) { nondef in
- XCTAssertEqual(nondef.pluginName, "my-plugin")
- XCTAssertEqual(nondef.pluginArguments, ["--help"])
- XCTAssertEqual(nondef.options.verbose, true)
- }
- }
- func testRemainingDefaultOther() throws {
- AssertParseCommand(MyCommand.self, Other.self, ["other"]) { other in
- XCTAssertEqual(other.options.verbose, false)
- }
- AssertParseCommand(MyCommand.self, Other.self, ["other", "--verbose"]) {
- other in
- XCTAssertEqual(other.options.verbose, true)
- }
- }
- func testRemainingDefaultFailure() {
- XCTAssertThrowsError(try MyCommand.parseAsRoot([]))
- XCTAssertThrowsError(try MyCommand.parseAsRoot(["--verbose"]))
- XCTAssertThrowsError(
- try MyCommand.parseAsRoot(["plugin", "--verbose", "my-plugin"]))
- }
- }
- extension DefaultSubcommandEndToEndTests {
- struct RootWithPassthroughDefault: ParsableCommand {
- static let configuration = CommandConfiguration(
- subcommands: [PassthroughDefault.self],
- defaultSubcommand: PassthroughDefault.self,
- helpNames: [.short, .long, .customLong("help", withSingleDash: true)]
- )
- }
- struct PassthroughDefault: ParsableCommand {
- @Argument(parsing: .captureForPassthrough)
- var remaining: [String] = []
- }
- // Test fix for https://github.com/apple/swift-package-manager/issues/7218
- func testHelpWithPassthroughDefault() throws {
- AssertParseCommand(
- RootWithPassthroughDefault.self, HelpCommand.self, ["-h"]
- ) { _ in }
- AssertParseCommand(
- RootWithPassthroughDefault.self, HelpCommand.self, ["-help"]
- ) { _ in }
- AssertParseCommand(
- RootWithPassthroughDefault.self, HelpCommand.self, ["--help"]
- ) { _ in }
- }
- }
- extension DefaultSubcommandEndToEndTests {
- struct NestedDefaultSubcommandHelp: ParsableCommand {
- static let configuration = CommandConfiguration(
- subcommands: [Default.self, Nested.self],
- defaultSubcommand: Default.self
- )
- struct Default: ParsableCommand {}
- struct Nested: ParsableCommand {
- static let configuration = CommandConfiguration(
- commandName: "nested",
- subcommands: [NestedDefault.self, NestedOther.self],
- defaultSubcommand: NestedDefault.self
- )
- }
- struct NestedDefault: ParsableCommand {}
- struct NestedOther: ParsableCommand {}
- }
- // Test fix for https://github.com/apple/swift-argument-parser/issues/865
- func testHelpWithNestedDefaultSubcommand() throws {
- AssertParseCommand(
- NestedDefaultSubcommandHelp.self, HelpCommand.self, ["--help"]
- ) { command in
- XCTAssert(command.commandStack.count == 1)
- XCTAssert(
- type(of: command.commandStack[0])
- == NestedDefaultSubcommandHelp.Type.self)
- }
- AssertParseCommand(
- NestedDefaultSubcommandHelp.self, HelpCommand.self, ["nested", "--help"]
- ) { command in
- XCTAssert(command.commandStack.count == 2)
- XCTAssert(
- type(of: command.commandStack[0])
- == NestedDefaultSubcommandHelp.Type.self)
- XCTAssert(
- type(of: command.commandStack[1])
- == NestedDefaultSubcommandHelp.Nested.Type.self)
- }
- }
- }
|