UsageGenerationTests.swift 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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 XCTest
  12. @testable import ArgumentParser
  13. final class UsageGenerationTests: XCTestCase {
  14. }
  15. func _testSynopsis<T: ParsableArguments>(
  16. _ type: T.Type,
  17. visibility: ArgumentVisibility = .default,
  18. expected: String,
  19. file: StaticString = #filePath,
  20. line: UInt = #line
  21. ) {
  22. let help = UsageGenerator(
  23. toolName: "example", parsable: T(), visibility: visibility, parent: nil)
  24. XCTAssertEqual(help.synopsis, expected, file: file, line: line)
  25. }
  26. // MARK: -
  27. extension UsageGenerationTests {
  28. func testNameSynopsis() {
  29. XCTAssertEqual(Name.long("foo").synopsisString, "--foo")
  30. XCTAssertEqual(Name.short("f").synopsisString, "-f")
  31. XCTAssertEqual(Name.longWithSingleDash("foo").synopsisString, "-foo")
  32. }
  33. }
  34. extension UsageGenerationTests {
  35. struct A: ParsableArguments {
  36. @Option() var firstName: String
  37. @Option() var title: String
  38. }
  39. func testSynopsis() {
  40. _testSynopsis(
  41. A.self, expected: "example --first-name <first-name> --title <title>")
  42. }
  43. struct B: ParsableArguments {
  44. @Option() var firstName: String?
  45. @Option() var title: String?
  46. }
  47. func testSynopsisWithOptional() {
  48. _testSynopsis(
  49. B.self, expected: "example [--first-name <first-name>] [--title <title>]")
  50. }
  51. struct C: ParsableArguments {
  52. @Flag var log: Bool = false
  53. @Flag() var verbose: Int
  54. }
  55. func testFlagSynopsis() {
  56. _testSynopsis(C.self, expected: "example [--log] [--verbose ...]")
  57. }
  58. struct D: ParsableArguments {
  59. @Argument() var firstName: String
  60. @Argument() var title: String?
  61. }
  62. func testPositionalSynopsis() {
  63. _testSynopsis(D.self, expected: "example <first-name> [<title>]")
  64. }
  65. struct E: ParsableArguments {
  66. @Option
  67. var name: String = "no-name"
  68. @Option
  69. var count: Int = 0
  70. @Argument
  71. var arg: String = "no-arg"
  72. }
  73. func testSynopsisWithDefaults() {
  74. _testSynopsis(
  75. E.self, expected: "example [--name <name>] [--count <count>] [<arg>]")
  76. }
  77. struct F: ParsableArguments {
  78. @Option() var name: [String] = []
  79. @Argument() var nameCounts: [Int] = []
  80. }
  81. func testSynopsisWithRepeats() {
  82. _testSynopsis(
  83. F.self, expected: "example [--name <name> ...] [<name-counts> ...]")
  84. }
  85. struct G: ParsableArguments {
  86. @Option(help: ArgumentHelp(valueName: "path"))
  87. var filePath: String?
  88. @Argument(help: ArgumentHelp(valueName: "user-home-path"))
  89. var homePath: String
  90. }
  91. func testSynopsisWithCustomization() {
  92. _testSynopsis(
  93. G.self, expected: "example [--file-path <path>] <user-home-path>")
  94. }
  95. struct H: ParsableArguments {
  96. @Option(help: .hidden) var firstName: String?
  97. @Argument(help: .hidden) var title: String?
  98. }
  99. func testSynopsisWithHidden() {
  100. _testSynopsis(H.self, expected: "example")
  101. _testSynopsis(
  102. H.self, visibility: .hidden,
  103. expected: "example [--first-name <first-name>] [<title>]")
  104. }
  105. struct I: ParsableArguments {
  106. enum Color {
  107. case red, blue
  108. @Sendable
  109. static func transform(_ string: String) throws -> Color {
  110. switch string {
  111. case "red":
  112. return .red
  113. case "blue":
  114. return .blue
  115. default:
  116. throw ValidationError("Not a valid string for 'Color'")
  117. }
  118. }
  119. }
  120. @Option(transform: Color.transform)
  121. var color: Color = .red
  122. }
  123. func testSynopsisWithDefaultValueAndTransform() {
  124. _testSynopsis(I.self, expected: "example [--color <color>]")
  125. }
  126. struct J: ParsableArguments {
  127. struct Foo {}
  128. @Option(transform: { _ in Foo() }) var req: Foo
  129. @Option(transform: { _ in Foo() }) var opt: Foo?
  130. }
  131. func testSynopsisWithTransform() {
  132. _testSynopsis(J.self, expected: "example --req <req> [--opt <opt>]")
  133. }
  134. struct K: ParsableArguments {
  135. @Option(
  136. name: [
  137. .short, .customLong("remote"), .customLong("when"),
  138. .customLong("there"),
  139. ],
  140. help: "Help Message")
  141. var time: String?
  142. }
  143. func testSynopsisWithMultipleCustomNames() {
  144. _testSynopsis(K.self, expected: "example [--remote <remote>]")
  145. }
  146. struct L: ParsableArguments {
  147. @Option(
  148. name: [
  149. .short, .short, .customLong("remote", withSingleDash: true), .short,
  150. .customLong("remote", withSingleDash: true),
  151. ],
  152. help: "Help Message")
  153. var time: String?
  154. }
  155. func testSynopsisWithSingleDashLongNameFirst() {
  156. _testSynopsis(L.self, expected: "example [-remote <remote>]")
  157. }
  158. struct M: ParsableArguments {
  159. enum Color: String, EnumerableFlag {
  160. case green, blue, yellow
  161. }
  162. @Flag var a: Bool = false
  163. @Flag var b: Bool = false
  164. @Flag var c: Bool = false
  165. @Flag var d: Bool = false
  166. @Flag var e: Bool = false
  167. @Flag var f: Bool = false
  168. @Flag var g: Bool = false
  169. @Flag var h: Bool = false
  170. @Flag var i: Bool = false
  171. @Flag var j: Bool = false
  172. @Flag var k: Bool = false
  173. @Flag var l: Bool = false
  174. @Flag(inversion: .prefixedEnableDisable)
  175. var optionalBool: Bool?
  176. @Flag var optionalColor: Color?
  177. @Option var option: Bool
  178. @Argument var input: String
  179. @Argument var output: String?
  180. }
  181. func testSynopsisWithTooManyOptions() {
  182. _testSynopsis(
  183. M.self,
  184. expected: "example [<options>] --option <option> <input> [<output>]")
  185. }
  186. struct N: ParsableArguments {
  187. @Flag var a: Bool = false
  188. @Flag var b: Bool = false
  189. var title = "defaulted value"
  190. var decode = false
  191. }
  192. func testNonwrappedValues() {
  193. _testSynopsis(N.self, expected: "example [--a] [--b]")
  194. _testSynopsis(N.self, visibility: .hidden, expected: "example [--a] [--b]")
  195. }
  196. struct O: ParsableArguments {
  197. @Argument var a: String
  198. @Argument(parsing: .postTerminator) var b: [String] = []
  199. }
  200. func testSynopsisWithPostTerminatorParsingStrategy() {
  201. _testSynopsis(O.self, expected: "example <a> -- [<b> ...]")
  202. }
  203. }