StructuredSwift+MetadataTests.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * Copyright 2024, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Testing
  17. @testable import GRPCCodeGen
  18. extension StructuedSwiftTests {
  19. @Suite("Metadata")
  20. struct Metadata {
  21. @Test("@available(...)")
  22. func grpcAvailability() async throws {
  23. let availability: AvailabilityDescription = .grpc
  24. let structDecl = StructDescription(name: "Ignored")
  25. let expected = """
  26. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  27. struct Ignored {}
  28. """
  29. #expect(render(.guarded(availability, .struct(structDecl))) == expected)
  30. }
  31. @Test("typealias Input = <Name>", arguments: AccessModifier.allCases)
  32. func methodInputTypealias(access: AccessModifier) {
  33. let decl: TypealiasDescription = .methodInput(accessModifier: access, name: "Foo")
  34. let expected = "\(access) typealias Input = Foo"
  35. #expect(render(.typealias(decl)) == expected)
  36. }
  37. @Test("typealias Output = <Name>", arguments: AccessModifier.allCases)
  38. func methodOutputTypealias(access: AccessModifier) {
  39. let decl: TypealiasDescription = .methodOutput(accessModifier: access, name: "Foo")
  40. let expected = "\(access) typealias Output = Foo"
  41. #expect(render(.typealias(decl)) == expected)
  42. }
  43. @Test(
  44. "static let descriptor = GRPCCore.MethodDescriptor(...)",
  45. arguments: AccessModifier.allCases
  46. )
  47. func staticMethodDescriptorProperty(access: AccessModifier) {
  48. let decl: VariableDescription = .methodDescriptor(
  49. accessModifier: access,
  50. serviceNamespace: "FooService",
  51. literalMethodName: "Bar"
  52. )
  53. let expected = """
  54. \(access) static let descriptor = GRPCCore.MethodDescriptor(
  55. service: FooService.descriptor.fullyQualifiedService,
  56. method: "Bar"
  57. )
  58. """
  59. #expect(render(.variable(decl)) == expected)
  60. }
  61. @Test(
  62. "static let descriptor = GRPCCore.ServiceDescriptor.<Name>",
  63. arguments: AccessModifier.allCases
  64. )
  65. func staticServiceDescriptorProperty(access: AccessModifier) {
  66. let decl: VariableDescription = .serviceDescriptor(
  67. accessModifier: access,
  68. namespacedProperty: "foo"
  69. )
  70. let expected = "\(access) static let descriptor = GRPCCore.ServiceDescriptor.foo"
  71. #expect(render(.variable(decl)) == expected)
  72. }
  73. @Test("extension GRPCCore.ServiceDescriptor { ... }", arguments: AccessModifier.allCases)
  74. func staticServiceDescriptorPropertyExtension(access: AccessModifier) {
  75. let decl: ExtensionDescription = .serviceDescriptor(
  76. accessModifier: access,
  77. propertyName: "foo",
  78. literalNamespace: "echo",
  79. literalService: "EchoService"
  80. )
  81. let expected = """
  82. extension GRPCCore.ServiceDescriptor {
  83. \(access) static let foo = Self(
  84. package: "echo",
  85. service: "EchoService"
  86. )
  87. }
  88. """
  89. #expect(render(.extension(decl)) == expected)
  90. }
  91. @Test(
  92. "static let descriptors: [GRPCCore.MethodDescriptor] = [...]",
  93. arguments: AccessModifier.allCases
  94. )
  95. func staticMethodDescriptorsArray(access: AccessModifier) {
  96. let decl: VariableDescription = .methodDescriptorsArray(
  97. accessModifier: access,
  98. methodNamespaceNames: ["Foo", "Bar", "Baz"]
  99. )
  100. let expected = """
  101. \(access) static let descriptors: [GRPCCore.MethodDescriptor] = [
  102. Foo.descriptor,
  103. Bar.descriptor,
  104. Baz.descriptor
  105. ]
  106. """
  107. #expect(render(.variable(decl)) == expected)
  108. }
  109. @Test("enum <Method> { ... }", arguments: AccessModifier.allCases)
  110. func methodNamespaceEnum(access: AccessModifier) {
  111. let decl: EnumDescription = .methodNamespace(
  112. accessModifier: access,
  113. name: "Foo",
  114. literalMethod: "Foo",
  115. serviceNamespace: "Bar_Baz",
  116. inputType: "FooInput",
  117. outputType: "FooOutput"
  118. )
  119. let expected = """
  120. \(access) enum Foo {
  121. \(access) typealias Input = FooInput
  122. \(access) typealias Output = FooOutput
  123. \(access) static let descriptor = GRPCCore.MethodDescriptor(
  124. service: Bar_Baz.descriptor.fullyQualifiedService,
  125. method: "Foo"
  126. )
  127. }
  128. """
  129. #expect(render(.enum(decl)) == expected)
  130. }
  131. @Test("enum Method { ... }", arguments: AccessModifier.allCases)
  132. func methodsNamespaceEnum(access: AccessModifier) {
  133. let decl: EnumDescription = .methodsNamespace(
  134. accessModifier: access,
  135. serviceNamespace: "Bar_Baz",
  136. methods: [
  137. .init(
  138. documentation: "",
  139. name: .init(base: "Foo", generatedUpperCase: "Foo", generatedLowerCase: "foo"),
  140. isInputStreaming: false,
  141. isOutputStreaming: false,
  142. inputType: "FooInput",
  143. outputType: "FooOutput"
  144. )
  145. ]
  146. )
  147. let expected = """
  148. \(access) enum Method {
  149. \(access) enum Foo {
  150. \(access) typealias Input = FooInput
  151. \(access) typealias Output = FooOutput
  152. \(access) static let descriptor = GRPCCore.MethodDescriptor(
  153. service: Bar_Baz.descriptor.fullyQualifiedService,
  154. method: "Foo"
  155. )
  156. }
  157. \(access) static let descriptors: [GRPCCore.MethodDescriptor] = [
  158. Foo.descriptor
  159. ]
  160. }
  161. """
  162. #expect(render(.enum(decl)) == expected)
  163. }
  164. @Test("enum Method { ... } (no methods)", arguments: AccessModifier.allCases)
  165. func methodsNamespaceEnumNoMethods(access: AccessModifier) {
  166. let decl: EnumDescription = .methodsNamespace(
  167. accessModifier: access,
  168. serviceNamespace: "Bar_Baz",
  169. methods: []
  170. )
  171. let expected = """
  172. \(access) enum Method {
  173. \(access) static let descriptors: [GRPCCore.MethodDescriptor] = []
  174. }
  175. """
  176. #expect(render(.enum(decl)) == expected)
  177. }
  178. @Test("enum <Service> { ... }", arguments: AccessModifier.allCases)
  179. func serviceNamespaceEnum(access: AccessModifier) {
  180. let decl: EnumDescription = .serviceNamespace(
  181. accessModifier: access,
  182. name: "Foo",
  183. serviceDescriptorProperty: "foo",
  184. client: false,
  185. server: false,
  186. methods: [
  187. .init(
  188. documentation: "",
  189. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  190. isInputStreaming: false,
  191. isOutputStreaming: false,
  192. inputType: "BarInput",
  193. outputType: "BarOutput"
  194. )
  195. ]
  196. )
  197. let expected = """
  198. \(access) enum Foo {
  199. \(access) static let descriptor = GRPCCore.ServiceDescriptor.foo
  200. \(access) enum Method {
  201. \(access) enum Bar {
  202. \(access) typealias Input = BarInput
  203. \(access) typealias Output = BarOutput
  204. \(access) static let descriptor = GRPCCore.MethodDescriptor(
  205. service: Foo.descriptor.fullyQualifiedService,
  206. method: "Bar"
  207. )
  208. }
  209. \(access) static let descriptors: [GRPCCore.MethodDescriptor] = [
  210. Bar.descriptor
  211. ]
  212. }
  213. }
  214. """
  215. #expect(render(.enum(decl)) == expected)
  216. }
  217. @Test(
  218. "enum <Service> { ... } (no methods)",
  219. arguments: AccessModifier.allCases,
  220. [(true, true), (false, false), (true, false), (false, true)]
  221. )
  222. func serviceNamespaceEnumNoMethods(access: AccessModifier, config: (client: Bool, server: Bool))
  223. {
  224. let decl: EnumDescription = .serviceNamespace(
  225. accessModifier: access,
  226. name: "Foo",
  227. serviceDescriptorProperty: "foo",
  228. client: config.client,
  229. server: config.server,
  230. methods: []
  231. )
  232. var expected = """
  233. \(access) enum Foo {
  234. \(access) static let descriptor = GRPCCore.ServiceDescriptor.foo
  235. \(access) enum Method {
  236. \(access) static let descriptors: [GRPCCore.MethodDescriptor] = []
  237. }\n
  238. """
  239. if config.server {
  240. expected += """
  241. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  242. \(access) typealias StreamingServiceProtocol = Foo_StreamingServiceProtocol
  243. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  244. \(access) typealias ServiceProtocol = Foo_ServiceProtocol
  245. """
  246. }
  247. if config.client {
  248. if config.server {
  249. expected += "\n"
  250. }
  251. expected += """
  252. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  253. \(access) typealias ClientProtocol = Foo_ClientProtocol
  254. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  255. \(access) typealias Client = Foo_Client
  256. """
  257. }
  258. if config.client || config.server {
  259. expected += "\n}"
  260. } else {
  261. expected += "}"
  262. }
  263. #expect(render(.enum(decl)) == expected)
  264. }
  265. }
  266. }