| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- //===----------------------------------------------------------------------===//
- //
- // 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 ArgumentParser
- import ArgumentParserTestHelpers
- import XCTest
- final class SubcommandEndToEndTests: XCTestCase {
- }
- // MARK: Single value String
- private struct Foo: ParsableCommand {
- static let configuration =
- CommandConfiguration(subcommands: [CommandA.self, CommandB.self])
- @Option() var name: String
- }
- private struct CommandA: ParsableCommand {
- static let configuration = CommandConfiguration(commandName: "a")
- @OptionGroup() var foo: Foo
- @Option() var bar: Int
- }
- private struct CommandB: ParsableCommand {
- static let configuration = CommandConfiguration(commandName: "b")
- @OptionGroup() var foo: Foo
- @Option() var baz: String
- }
- // swift-format-ignore: AlwaysUseLowerCamelCase
- // https://github.com/apple/swift-argument-parser/issues/710
- extension SubcommandEndToEndTests {
- func testParsing_SubCommand() throws {
- AssertParseCommand(
- Foo.self, CommandA.self, ["--name", "Foo", "a", "--bar", "42"]
- ) { a in
- XCTAssertEqual(a.bar, 42)
- XCTAssertEqual(a.foo.name, "Foo")
- }
- AssertParseCommand(
- Foo.self, CommandB.self, ["--name", "A", "b", "--baz", "abc"]
- ) { b in
- XCTAssertEqual(b.baz, "abc")
- XCTAssertEqual(b.foo.name, "A")
- }
- }
- func testParsing_SubCommand_manual() throws {
- AssertParseCommand(
- Foo.self, CommandA.self, ["--name", "Foo", "a", "--bar", "42"]
- ) { a in
- XCTAssertEqual(a.bar, 42)
- XCTAssertEqual(a.foo.name, "Foo")
- }
- AssertParseCommand(Foo.self, Foo.self, ["--name", "Foo"]) { foo in
- XCTAssertEqual(foo.name, "Foo")
- }
- }
- func testParsing_SubCommand_help() throws {
- let helpFoo = Foo.message(for: CleanExit.helpRequest())
- let helpA = Foo.message(for: CleanExit.helpRequest(CommandA.self))
- let helpB = Foo.message(for: CleanExit.helpRequest(CommandB.self))
- AssertEqualStrings(
- actual: helpFoo,
- expected: """
- USAGE: foo --name <name> <subcommand>
- OPTIONS:
- --name <name>
- -h, --help Show help information.
- SUBCOMMANDS:
- a
- b
- See 'foo help <subcommand>' for detailed help.
- """)
- AssertEqualStrings(
- actual: helpA,
- expected: """
- USAGE: foo a --name <name> --bar <bar>
- OPTIONS:
- --name <name>
- --bar <bar>
- -h, --help Show help information.
- """)
- AssertEqualStrings(
- actual: helpB,
- expected: """
- USAGE: foo b --name <name> --baz <baz>
- OPTIONS:
- --name <name>
- --baz <baz>
- -h, --help Show help information.
- """)
- }
- func testParsing_SubCommand_fails() throws {
- XCTAssertThrowsError(
- try Foo.parse(["--name", "Foo", "a", "--baz", "42"]),
- "'baz' is not an option for the 'a' subcommand.")
- XCTAssertThrowsError(
- try Foo.parse(["--name", "Foo", "b", "--bar", "42"]),
- "'bar' is not an option for the 'b' subcommand.")
- }
- }
- private struct Math: ParsableCommand {
- enum Operation: String, ExpressibleByArgument {
- case add
- case multiply
- }
- @Option(help: "The operation to perform")
- var operation: Operation = .add
- @Flag(name: [.short, .long])
- var verbose: Bool = false
- @Argument(help: "The first operand")
- var operands: [Int] = []
- var didRun = false
- mutating func run() {
- XCTAssertEqual(operation, .multiply)
- XCTAssertTrue(verbose)
- XCTAssertEqual(operands, [5, 11])
- didRun = true
- }
- }
- // swift-format-ignore: AlwaysUseLowerCamelCase
- // https://github.com/apple/swift-argument-parser/issues/710
- extension SubcommandEndToEndTests {
- func testParsing_SingleCommand() throws {
- var mathCommand =
- try Math.parseAsRoot(["--operation", "multiply", "-v", "5", "11"])
- as! Math
- XCTAssertFalse(mathCommand.didRun)
- mathCommand.run()
- XCTAssertTrue(mathCommand.didRun)
- }
- }
- // MARK: Nested Command Arguments Validated
- struct BaseCommand: ParsableCommand {
- enum BaseCommandError: Error {
- case baseCommandFailure
- case subCommandFailure
- }
- static let baseFlagValue = "base"
- static let configuration = CommandConfiguration(
- commandName: "base",
- subcommands: [SubCommand.self]
- )
- @Option()
- var baseFlag: String
- mutating func validate() throws {
- guard baseFlag == BaseCommand.baseFlagValue else {
- throw BaseCommandError.baseCommandFailure
- }
- }
- }
- extension BaseCommand {
- struct SubCommand: ParsableCommand {
- static let subFlagValue = "sub"
- static let configuration = CommandConfiguration(
- commandName: "sub",
- subcommands: [SubSubCommand.self]
- )
- @Option()
- var subFlag: String
- mutating func validate() throws {
- guard subFlag == SubCommand.subFlagValue else {
- throw BaseCommandError.subCommandFailure
- }
- }
- }
- }
- extension BaseCommand.SubCommand {
- struct SubSubCommand: ParsableCommand, TestableParsableArguments {
- let didValidateExpectation = XCTestExpectation(
- singleExpectation: "did validate subcommand")
- static let configuration = CommandConfiguration(
- commandName: "subsub"
- )
- @Flag
- var subSubFlag: Bool = false
- private enum CodingKeys: CodingKey {
- case subSubFlag
- }
- }
- }
- // swift-format-ignore: AlwaysUseLowerCamelCase
- // https://github.com/apple/swift-argument-parser/issues/710
- extension SubcommandEndToEndTests {
- func testValidate_subcommands() {
- // provide a value to base-flag that will throw
- AssertErrorMessage(
- BaseCommand.self,
- ["--base-flag", "foo", "sub", "--sub-flag", "foo", "subsub"],
- "baseCommandFailure"
- )
- // provide a value to sub-flag that will throw
- AssertErrorMessage(
- BaseCommand.self,
- [
- "--base-flag", BaseCommand.baseFlagValue, "sub", "--sub-flag", "foo",
- "subsub",
- ],
- "subCommandFailure"
- )
- // provide a valid command and make sure both validates succeed
- AssertParseCommand(
- BaseCommand.self,
- BaseCommand.SubCommand.SubSubCommand.self,
- [
- "--base-flag", BaseCommand.baseFlagValue, "sub", "--sub-flag",
- BaseCommand.SubCommand.subFlagValue, "subsub", "--sub-sub-flag",
- ]
- ) { cmd in
- XCTAssertTrue(cmd.subSubFlag)
- // make sure that the instance of SubSubCommand provided
- // had its validate method called, not just that any instance of SubSubCommand was validated
- wait(for: [cmd.didValidateExpectation], timeout: 0.1)
- }
- }
- }
- // MARK: Version flags
- private struct A: ParsableCommand {
- static let configuration = CommandConfiguration(
- version: "1.0.0",
- subcommands: [HasVersionFlag.self, NoVersionFlag.self])
- struct HasVersionFlag: ParsableCommand {
- @Flag var version: Bool = false
- }
- struct NoVersionFlag: ParsableCommand {
- @Flag var hello: Bool = false
- }
- }
- extension SubcommandEndToEndTests {
- func testParsingVersionFlags() throws {
- AssertErrorMessage(A.self, ["--version"], "1.0.0")
- AssertErrorMessage(A.self, ["no-version-flag", "--version"], "1.0.0")
- AssertParseCommand(
- A.self, A.HasVersionFlag.self, ["has-version-flag", "--version"]
- ) { cmd in
- XCTAssertTrue(cmd.version)
- }
- }
- }
|