IDLToStructuredSwiftTranslatorSnippetBasedTests.swift 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. #if os(macOS) || os(Linux) // swift-format doesn't like canImport(Foundation.Process)
  17. import XCTest
  18. @testable import GRPCCodeGen
  19. final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
  20. typealias MethodDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  21. typealias ServiceDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor
  22. func testImports() throws {
  23. var dependencies = [CodeGenerationRequest.Dependency]()
  24. dependencies.append(CodeGenerationRequest.Dependency(module: "Foo"))
  25. dependencies.append(
  26. CodeGenerationRequest.Dependency(item: .init(kind: .typealias, name: "Bar"), module: "Foo")
  27. )
  28. dependencies.append(
  29. CodeGenerationRequest.Dependency(item: .init(kind: .struct, name: "Baz"), module: "Foo")
  30. )
  31. dependencies.append(
  32. CodeGenerationRequest.Dependency(item: .init(kind: .class, name: "Bac"), module: "Foo")
  33. )
  34. dependencies.append(
  35. CodeGenerationRequest.Dependency(item: .init(kind: .enum, name: "Bap"), module: "Foo")
  36. )
  37. dependencies.append(
  38. CodeGenerationRequest.Dependency(item: .init(kind: .protocol, name: "Bat"), module: "Foo")
  39. )
  40. dependencies.append(
  41. CodeGenerationRequest.Dependency(item: .init(kind: .let, name: "Baq"), module: "Foo")
  42. )
  43. dependencies.append(
  44. CodeGenerationRequest.Dependency(item: .init(kind: .var, name: "Bag"), module: "Foo")
  45. )
  46. dependencies.append(
  47. CodeGenerationRequest.Dependency(item: .init(kind: .func, name: "Bak"), module: "Foo")
  48. )
  49. let expectedSwift =
  50. """
  51. /// Some really exciting license header 2023.
  52. import GRPCCore
  53. import Foo
  54. import typealias Foo.Bar
  55. import struct Foo.Baz
  56. import class Foo.Bac
  57. import enum Foo.Bap
  58. import protocol Foo.Bat
  59. import let Foo.Baq
  60. import var Foo.Bag
  61. import func Foo.Bak
  62. """
  63. try self.assertIDLToStructuredSwiftTranslation(
  64. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  65. expectedSwift: expectedSwift
  66. )
  67. }
  68. func testPreconcurrencyImports() throws {
  69. var dependencies = [CodeGenerationRequest.Dependency]()
  70. dependencies.append(CodeGenerationRequest.Dependency(module: "Foo", preconcurrency: .required))
  71. dependencies.append(
  72. CodeGenerationRequest.Dependency(
  73. item: .init(kind: .enum, name: "Bar"),
  74. module: "Foo",
  75. preconcurrency: .required
  76. )
  77. )
  78. dependencies.append(
  79. CodeGenerationRequest.Dependency(
  80. module: "Baz",
  81. preconcurrency: .requiredOnOS(["Deq", "Der"])
  82. )
  83. )
  84. let expectedSwift =
  85. """
  86. /// Some really exciting license header 2023.
  87. import GRPCCore
  88. @preconcurrency import Foo
  89. @preconcurrency import enum Foo.Bar
  90. #if os(Deq) || os(Der)
  91. @preconcurrency import Baz
  92. #else
  93. import Baz
  94. #endif
  95. """
  96. try self.assertIDLToStructuredSwiftTranslation(
  97. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  98. expectedSwift: expectedSwift
  99. )
  100. }
  101. func testSPIImports() throws {
  102. var dependencies = [CodeGenerationRequest.Dependency]()
  103. dependencies.append(CodeGenerationRequest.Dependency(module: "Foo", spi: "Secret"))
  104. dependencies.append(
  105. CodeGenerationRequest.Dependency(
  106. item: .init(kind: .enum, name: "Bar"),
  107. module: "Foo",
  108. spi: "Secret"
  109. )
  110. )
  111. let expectedSwift =
  112. """
  113. /// Some really exciting license header 2023.
  114. import GRPCCore
  115. @_spi(Secret) import Foo
  116. @_spi(Secret) import enum Foo.Bar
  117. """
  118. try self.assertIDLToStructuredSwiftTranslation(
  119. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  120. expectedSwift: expectedSwift
  121. )
  122. }
  123. private func assertIDLToStructuredSwiftTranslation(
  124. codeGenerationRequest: CodeGenerationRequest,
  125. expectedSwift: String
  126. ) throws {
  127. let translator = IDLToStructuredSwiftTranslator()
  128. let structuredSwift = try translator.translate(
  129. codeGenerationRequest: codeGenerationRequest,
  130. client: false,
  131. server: false
  132. )
  133. let renderer = TextBasedRenderer.default
  134. let sourceFile = try renderer.render(structured: structuredSwift)
  135. let contents = sourceFile.contents
  136. try XCTAssertEqualWithDiff(contents, expectedSwift)
  137. }
  138. func testSameNameServicesNoNamespaceError() throws {
  139. let serviceA = ServiceDescriptor(
  140. documentation: "Documentation for AService",
  141. name: "AService",
  142. namespace: "",
  143. methods: []
  144. )
  145. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceA])
  146. let translator = IDLToStructuredSwiftTranslator()
  147. XCTAssertThrowsError(
  148. ofType: CodeGenError.self,
  149. try translator.translate(
  150. codeGenerationRequest: codeGenerationRequest,
  151. client: true,
  152. server: true
  153. )
  154. ) {
  155. error in
  156. XCTAssertEqual(
  157. error as CodeGenError,
  158. CodeGenError(
  159. code: .nonUniqueServiceName,
  160. message: """
  161. Services in an empty namespace must have unique names. \
  162. AService is used as a name for multiple services without namespaces.
  163. """
  164. )
  165. )
  166. }
  167. }
  168. func testSameNameServicesSameNamespaceError() throws {
  169. let serviceA = ServiceDescriptor(
  170. documentation: "Documentation for AService",
  171. name: "AService",
  172. namespace: "namespacea",
  173. methods: []
  174. )
  175. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceA])
  176. let translator = IDLToStructuredSwiftTranslator()
  177. XCTAssertThrowsError(
  178. ofType: CodeGenError.self,
  179. try translator.translate(
  180. codeGenerationRequest: codeGenerationRequest,
  181. client: true,
  182. server: true
  183. )
  184. ) {
  185. error in
  186. XCTAssertEqual(
  187. error as CodeGenError,
  188. CodeGenError(
  189. code: .nonUniqueServiceName,
  190. message: """
  191. Services within the same namespace must have unique names. \
  192. AService is used as a name for multiple services in the namespacea namespace.
  193. """
  194. )
  195. )
  196. }
  197. }
  198. func testSameNameMethodsSameServiceError() throws {
  199. let methodA = MethodDescriptor(
  200. documentation: "Documentation for MethodA",
  201. name: "MethodA",
  202. isInputStreaming: false,
  203. isOutputStreaming: false,
  204. inputType: "NamespaceA_ServiceARequest",
  205. outputType: "NamespaceA_ServiceAResponse"
  206. )
  207. let service = ServiceDescriptor(
  208. documentation: "Documentation for AService",
  209. name: "AService",
  210. namespace: "namespacea",
  211. methods: [methodA, methodA]
  212. )
  213. let codeGenerationRequest = makeCodeGenerationRequest(services: [service])
  214. let translator = IDLToStructuredSwiftTranslator()
  215. XCTAssertThrowsError(
  216. ofType: CodeGenError.self,
  217. try translator.translate(
  218. codeGenerationRequest: codeGenerationRequest,
  219. client: true,
  220. server: true
  221. )
  222. ) {
  223. error in
  224. XCTAssertEqual(
  225. error as CodeGenError,
  226. CodeGenError(
  227. code: .nonUniqueMethodName,
  228. message: """
  229. Methods of a service must have unique names. \
  230. MethodA is used as a name for multiple methods of the AService service.
  231. """
  232. )
  233. )
  234. }
  235. }
  236. func testSameNameNoNamespaceServiceAndNamespaceError() throws {
  237. let serviceA = ServiceDescriptor(
  238. documentation: "Documentation for SameName service with no namespace",
  239. name: "SameName",
  240. namespace: "",
  241. methods: []
  242. )
  243. let serviceB = ServiceDescriptor(
  244. documentation: "Documentation for BService",
  245. name: "BService",
  246. namespace: "SameName",
  247. methods: []
  248. )
  249. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceB])
  250. let translator = IDLToStructuredSwiftTranslator()
  251. XCTAssertThrowsError(
  252. ofType: CodeGenError.self,
  253. try translator.translate(
  254. codeGenerationRequest: codeGenerationRequest,
  255. client: true,
  256. server: true
  257. )
  258. ) {
  259. error in
  260. XCTAssertEqual(
  261. error as CodeGenError,
  262. CodeGenError(
  263. code: .nonUniqueServiceName,
  264. message: """
  265. Services with no namespace must not have the same names as the namespaces. \
  266. SameName is used as a name for a service with no namespace and a namespace.
  267. """
  268. )
  269. )
  270. }
  271. }
  272. }
  273. #endif // os(macOS) || os(Linux)