NestedCommandEndToEndTests.swift 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Argument Parser open source project
  4. //
  5. // Copyright (c) 2020 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. import ArgumentParser
  12. import ArgumentParserTestHelpers
  13. import XCTest
  14. final class NestedCommandEndToEndTests: XCTestCase {}
  15. // MARK: Single value String
  16. private struct Foo: ParsableCommand {
  17. static let configuration =
  18. CommandConfiguration(subcommands: [Build.self, Package.self])
  19. @Flag(name: .short)
  20. var verbose: Bool = false
  21. struct Build: ParsableCommand {
  22. @OptionGroup() var foo: Foo
  23. @Argument()
  24. var input: String
  25. }
  26. struct Package: ParsableCommand {
  27. static let configuration =
  28. CommandConfiguration(
  29. subcommands: [Clean.self, Config.self],
  30. aliases: ["pkg"])
  31. @Flag(name: .short)
  32. var force: Bool = false
  33. struct Clean: ParsableCommand {
  34. @OptionGroup() var foo: Foo
  35. @OptionGroup() var package: Package
  36. }
  37. struct Config: ParsableCommand {
  38. static let configuration = CommandConfiguration(aliases: ["cfg"])
  39. @OptionGroup() var foo: Foo
  40. @OptionGroup() var package: Package
  41. }
  42. }
  43. }
  44. // swift-format-ignore: AlwaysUseLowerCamelCase
  45. private func AssertParseFooCommand<A>(
  46. _ type: A.Type, _ arguments: [String], file: StaticString = #filePath,
  47. line: UInt = #line, closure: (A) throws -> Void
  48. ) where A: ParsableCommand {
  49. AssertParseCommand(
  50. Foo.self, type, arguments, file: file, line: line, closure: closure)
  51. }
  52. // swift-format-ignore: AlwaysUseLowerCamelCase
  53. // https://github.com/apple/swift-argument-parser/issues/710
  54. extension NestedCommandEndToEndTests {
  55. func testParsing_package() throws {
  56. AssertParseFooCommand(Foo.Package.self, ["package"]) { package in
  57. XCTAssertFalse(package.force)
  58. }
  59. AssertParseFooCommand(Foo.Package.self, ["pkg"]) { package in
  60. XCTAssertFalse(package.force)
  61. }
  62. AssertParseFooCommand(Foo.Package.Clean.self, ["package", "clean"]) {
  63. clean in
  64. XCTAssertEqual(clean.foo.verbose, false)
  65. XCTAssertEqual(clean.package.force, false)
  66. }
  67. AssertParseFooCommand(Foo.Package.Clean.self, ["pkg", "clean"]) { clean in
  68. XCTAssertEqual(clean.foo.verbose, false)
  69. XCTAssertEqual(clean.package.force, false)
  70. }
  71. AssertParseFooCommand(Foo.Package.Clean.self, ["package", "-f", "clean"]) {
  72. clean in
  73. XCTAssertEqual(clean.foo.verbose, false)
  74. XCTAssertEqual(clean.package.force, true)
  75. }
  76. AssertParseFooCommand(Foo.Package.Clean.self, ["pkg", "-f", "clean"]) {
  77. clean in
  78. XCTAssertEqual(clean.foo.verbose, false)
  79. XCTAssertEqual(clean.package.force, true)
  80. }
  81. AssertParseFooCommand(Foo.Package.Config.self, ["package", "-v", "config"])
  82. { config in
  83. XCTAssertEqual(config.foo.verbose, true)
  84. XCTAssertEqual(config.package.force, false)
  85. }
  86. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-v", "cfg"]) {
  87. config in
  88. XCTAssertEqual(config.foo.verbose, true)
  89. XCTAssertEqual(config.package.force, false)
  90. }
  91. AssertParseFooCommand(Foo.Package.Config.self, ["package", "config", "-v"])
  92. { config in
  93. XCTAssertEqual(config.foo.verbose, true)
  94. XCTAssertEqual(config.package.force, false)
  95. }
  96. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "cfg", "-v"]) {
  97. config in
  98. XCTAssertEqual(config.foo.verbose, true)
  99. XCTAssertEqual(config.package.force, false)
  100. }
  101. AssertParseFooCommand(Foo.Package.Config.self, ["-v", "package", "config"])
  102. { config in
  103. XCTAssertEqual(config.foo.verbose, true)
  104. XCTAssertEqual(config.package.force, false)
  105. }
  106. AssertParseFooCommand(Foo.Package.Config.self, ["-v", "pkg", "cfg"]) {
  107. config in
  108. XCTAssertEqual(config.foo.verbose, true)
  109. XCTAssertEqual(config.package.force, false)
  110. }
  111. AssertParseFooCommand(Foo.Package.Config.self, ["package", "-f", "config"])
  112. { config in
  113. XCTAssertEqual(config.foo.verbose, false)
  114. XCTAssertEqual(config.package.force, true)
  115. }
  116. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-f", "cfg"]) {
  117. config in
  118. XCTAssertEqual(config.foo.verbose, false)
  119. XCTAssertEqual(config.package.force, true)
  120. }
  121. AssertParseFooCommand(Foo.Package.Config.self, ["package", "config", "-f"])
  122. { config in
  123. XCTAssertEqual(config.foo.verbose, false)
  124. XCTAssertEqual(config.package.force, true)
  125. }
  126. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "cfg", "-f"]) {
  127. config in
  128. XCTAssertEqual(config.foo.verbose, false)
  129. XCTAssertEqual(config.package.force, true)
  130. }
  131. AssertParseFooCommand(
  132. Foo.Package.Config.self, ["package", "-v", "config", "-f"]
  133. ) { config in
  134. XCTAssertEqual(config.foo.verbose, true)
  135. XCTAssertEqual(config.package.force, true)
  136. }
  137. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-v", "cfg", "-f"]) {
  138. config in
  139. XCTAssertEqual(config.foo.verbose, true)
  140. XCTAssertEqual(config.package.force, true)
  141. }
  142. AssertParseFooCommand(
  143. Foo.Package.Config.self, ["package", "-f", "config", "-v"]
  144. ) { config in
  145. XCTAssertEqual(config.foo.verbose, true)
  146. XCTAssertEqual(config.package.force, true)
  147. }
  148. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-f", "cfg", "-v"]) {
  149. config in
  150. XCTAssertEqual(config.foo.verbose, true)
  151. XCTAssertEqual(config.package.force, true)
  152. }
  153. AssertParseFooCommand(
  154. Foo.Package.Config.self, ["package", "-vf", "config"]
  155. ) { config in
  156. XCTAssertEqual(config.foo.verbose, true)
  157. XCTAssertEqual(config.package.force, true)
  158. }
  159. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-vf", "cfg"]) {
  160. config in
  161. XCTAssertEqual(config.foo.verbose, true)
  162. XCTAssertEqual(config.package.force, true)
  163. }
  164. AssertParseFooCommand(
  165. Foo.Package.Config.self, ["package", "-fv", "config"]
  166. ) { config in
  167. XCTAssertEqual(config.foo.verbose, true)
  168. XCTAssertEqual(config.package.force, true)
  169. }
  170. AssertParseFooCommand(Foo.Package.Config.self, ["pkg", "-fv", "cfg"]) {
  171. config in
  172. XCTAssertEqual(config.foo.verbose, true)
  173. XCTAssertEqual(config.package.force, true)
  174. }
  175. }
  176. func testParsing_build() throws {
  177. AssertParseFooCommand(Foo.Build.self, ["build", "file"]) { build in
  178. XCTAssertEqual(build.foo.verbose, false)
  179. XCTAssertEqual(build.input, "file")
  180. }
  181. }
  182. func testParsing_fails() throws {
  183. XCTAssertThrowsError(try Foo.parseAsRoot(["clean", "package"]))
  184. XCTAssertThrowsError(try Foo.parseAsRoot(["clean", "pkg"]))
  185. XCTAssertThrowsError(try Foo.parseAsRoot(["config", "package"]))
  186. XCTAssertThrowsError(try Foo.parseAsRoot(["cfg", "pkg"]))
  187. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "c"]))
  188. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "c"]))
  189. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "build"]))
  190. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "build"]))
  191. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "build", "clean"]))
  192. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "build", "clean"]))
  193. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "clean", "foo"]))
  194. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "clean", "foo"]))
  195. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "config", "bar"]))
  196. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "cfg", "bar"]))
  197. XCTAssertThrowsError(try Foo.parseAsRoot(["package", "clean", "build"]))
  198. XCTAssertThrowsError(try Foo.parseAsRoot(["pkg", "clean", "build"]))
  199. XCTAssertThrowsError(try Foo.parseAsRoot(["build"]))
  200. XCTAssertThrowsError(try Foo.parseAsRoot(["build", "-f"]))
  201. XCTAssertThrowsError(try Foo.parseAsRoot(["build", "--build"]))
  202. XCTAssertThrowsError(try Foo.parseAsRoot(["build", "--build", "12"]))
  203. XCTAssertThrowsError(try Foo.parseAsRoot(["-f", "package", "clean"]))
  204. XCTAssertThrowsError(try Foo.parseAsRoot(["-f", "pkg", "clean"]))
  205. XCTAssertThrowsError(try Foo.parseAsRoot(["-f", "package", "config"]))
  206. XCTAssertThrowsError(try Foo.parseAsRoot(["-f", "pkg", "config"]))
  207. }
  208. }
  209. private struct Options: ParsableArguments {
  210. @Option() var firstName: String?
  211. }
  212. private struct UniqueOptions: ParsableArguments {
  213. @Option() var lastName: String?
  214. }
  215. private struct Super: ParsableCommand {
  216. static var configuration: CommandConfiguration {
  217. .init(subcommands: [Sub1.self, Sub2.self])
  218. }
  219. @OptionGroup() var options: Options
  220. struct Sub1: ParsableCommand {
  221. @OptionGroup() var options: Options
  222. }
  223. struct Sub2: ParsableCommand {
  224. @OptionGroup() var options: UniqueOptions
  225. }
  226. }
  227. // swift-format-ignore: AlwaysUseLowerCamelCase
  228. // https://github.com/apple/swift-argument-parser/issues/710
  229. extension NestedCommandEndToEndTests {
  230. func testParsing_SharedOptions() throws {
  231. AssertParseCommand(Super.self, Super.self, []) { sup in
  232. XCTAssertNil(sup.options.firstName)
  233. }
  234. AssertParseCommand(Super.self, Super.self, ["--first-name", "Foo"]) { sup in
  235. XCTAssertEqual("Foo", sup.options.firstName)
  236. }
  237. AssertParseCommand(Super.self, Super.Sub1.self, ["sub1"]) { sub1 in
  238. XCTAssertNil(sub1.options.firstName)
  239. }
  240. AssertParseCommand(
  241. Super.self, Super.Sub1.self, ["sub1", "--first-name", "Foo"]
  242. ) { sub1 in
  243. XCTAssertEqual("Foo", sub1.options.firstName)
  244. }
  245. AssertParseCommand(
  246. Super.self, Super.Sub2.self, ["sub2", "--last-name", "Foo"]
  247. ) { sub2 in
  248. XCTAssertEqual("Foo", sub2.options.lastName)
  249. }
  250. }
  251. }