ServerCodeTranslatorSnippetBasedTests.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * Copyright 2023, 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 ServerCodeTranslatorSnippetBasedTests: XCTestCase {
  20. typealias MethodDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  21. typealias ServiceDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor
  22. func testServerCodeTranslatorUnaryMethod() throws {
  23. let method = MethodDescriptor(
  24. documentation: "Documentation for unaryMethod",
  25. name: "unaryMethod",
  26. isInputStreaming: false,
  27. isOutputStreaming: false,
  28. inputType: "NamespaceA_ServiceARequest",
  29. outputType: "NamespaceA_ServiceAResponse"
  30. )
  31. let service = ServiceDescriptor(
  32. documentation: "Documentation for ServiceA",
  33. name: "ServiceA",
  34. namespace: "namespaceA",
  35. methods: [method]
  36. )
  37. let expectedSwift =
  38. """
  39. /// Documentation for ServiceA
  40. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  41. /// Documentation for unaryMethod
  42. func unaryMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.unaryMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.unaryMethod.Output>
  43. }
  44. /// Conformance to `GRPCCore.RegistrableRPCService`.
  45. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  46. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  47. router.registerHandler(
  48. for: namespaceA.ServiceA.Methods.unaryMethod.descriptor,
  49. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.unaryMethod.Input>(),
  50. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.unaryMethod.Output>(),
  51. handler: { request in
  52. try await self.unaryMethod(request: request)
  53. }
  54. )
  55. }
  56. }
  57. /// Documentation for ServiceA
  58. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {
  59. /// Documentation for unaryMethod
  60. func unaryMethod(request: ServerRequest.Single<namespaceA.ServiceA.Methods.unaryMethod.Input>) async throws -> ServerResponse.Single<namespaceA.ServiceA.Methods.unaryMethod.Output>
  61. }
  62. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  63. public extension namespaceA.ServiceA.ServiceProtocol {
  64. func unaryMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.unaryMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.unaryMethod.Output> {
  65. let response = try await self.unaryMethod(request: ServerRequest.Single(stream: request))
  66. return ServerResponse.Stream(single: response)
  67. }
  68. }
  69. """
  70. try self.assertServerCodeTranslation(
  71. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  72. expectedSwift: expectedSwift
  73. )
  74. }
  75. func testServerCodeTranslatorInputStreamingMethod() throws {
  76. let method = MethodDescriptor(
  77. documentation: "Documentation for inputStreamingMethod",
  78. name: "inputStreamingMethod",
  79. isInputStreaming: true,
  80. isOutputStreaming: false,
  81. inputType: "NamespaceA_ServiceARequest",
  82. outputType: "NamespaceA_ServiceAResponse"
  83. )
  84. let service = ServiceDescriptor(
  85. documentation: "Documentation for ServiceA",
  86. name: "ServiceA",
  87. namespace: "namespaceA",
  88. methods: [method]
  89. )
  90. let expectedSwift =
  91. """
  92. /// Documentation for ServiceA
  93. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  94. /// Documentation for inputStreamingMethod
  95. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>
  96. }
  97. /// Conformance to `GRPCCore.RegistrableRPCService`.
  98. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  99. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  100. router.registerHandler(
  101. for: namespaceA.ServiceA.Methods.inputStreamingMethod.descriptor,
  102. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>(),
  103. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>(),
  104. handler: { request in
  105. try await self.inputStreamingMethod(request: request)
  106. }
  107. )
  108. }
  109. }
  110. /// Documentation for ServiceA
  111. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {
  112. /// Documentation for inputStreamingMethod
  113. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Single<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>
  114. }
  115. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  116. public extension namespaceA.ServiceA.ServiceProtocol {
  117. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Output> {
  118. let response = try await self.inputStreamingMethod(request: request)
  119. return ServerResponse.Stream(single: response)
  120. }
  121. }
  122. """
  123. try self.assertServerCodeTranslation(
  124. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  125. expectedSwift: expectedSwift
  126. )
  127. }
  128. func testServerCodeTranslatorOutputStreamingMethod() throws {
  129. let method = MethodDescriptor(
  130. documentation: "Documentation for outputStreamingMethod",
  131. name: "outputStreamingMethod",
  132. isInputStreaming: false,
  133. isOutputStreaming: true,
  134. inputType: "NamespaceA_ServiceARequest",
  135. outputType: "NamespaceA_ServiceAResponse"
  136. )
  137. let service = ServiceDescriptor(
  138. documentation: "Documentation for ServiceA",
  139. name: "ServiceA",
  140. namespace: "namespaceA",
  141. methods: [method]
  142. )
  143. let expectedSwift =
  144. """
  145. /// Documentation for ServiceA
  146. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  147. /// Documentation for outputStreamingMethod
  148. func outputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>
  149. }
  150. /// Conformance to `GRPCCore.RegistrableRPCService`.
  151. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  152. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  153. router.registerHandler(
  154. for: namespaceA.ServiceA.Methods.outputStreamingMethod.descriptor,
  155. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>(),
  156. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>(),
  157. handler: { request in
  158. try await self.outputStreamingMethod(request: request)
  159. }
  160. )
  161. }
  162. }
  163. /// Documentation for ServiceA
  164. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {
  165. /// Documentation for outputStreamingMethod
  166. func outputStreamingMethod(request: ServerRequest.Single<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>
  167. }
  168. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  169. public extension namespaceA.ServiceA.ServiceProtocol {
  170. func outputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output> {
  171. let response = try await self.outputStreamingMethod(request: ServerRequest.Single(stream: request))
  172. return response
  173. }
  174. }
  175. """
  176. try self.assertServerCodeTranslation(
  177. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  178. expectedSwift: expectedSwift
  179. )
  180. }
  181. func testServerCodeTranslatorBidirectionalStreamingMethod() throws {
  182. let method = MethodDescriptor(
  183. documentation: "Documentation for bidirectionalStreamingMethod",
  184. name: "bidirectionalStreamingMethod",
  185. isInputStreaming: true,
  186. isOutputStreaming: true,
  187. inputType: "NamespaceA_ServiceARequest",
  188. outputType: "NamespaceA_ServiceAResponse"
  189. )
  190. let service = ServiceDescriptor(
  191. documentation: "Documentation for ServiceA",
  192. name: "ServiceA",
  193. namespace: "namespaceA",
  194. methods: [method]
  195. )
  196. let expectedSwift =
  197. """
  198. /// Documentation for ServiceA
  199. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  200. /// Documentation for bidirectionalStreamingMethod
  201. func bidirectionalStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Output>
  202. }
  203. /// Conformance to `GRPCCore.RegistrableRPCService`.
  204. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  205. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  206. router.registerHandler(
  207. for: namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.descriptor,
  208. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Input>(),
  209. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Output>(),
  210. handler: { request in
  211. try await self.bidirectionalStreamingMethod(request: request)
  212. }
  213. )
  214. }
  215. }
  216. /// Documentation for ServiceA
  217. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {
  218. /// Documentation for bidirectionalStreamingMethod
  219. func bidirectionalStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.bidirectionalStreamingMethod.Output>
  220. }
  221. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  222. public extension namespaceA.ServiceA.ServiceProtocol {
  223. }
  224. """
  225. try self.assertServerCodeTranslation(
  226. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  227. expectedSwift: expectedSwift
  228. )
  229. }
  230. func testServerCodeTranslatorMultipleMethods() throws {
  231. let inputStreamingMethod = MethodDescriptor(
  232. documentation: "Documentation for inputStreamingMethod",
  233. name: "inputStreamingMethod",
  234. isInputStreaming: true,
  235. isOutputStreaming: false,
  236. inputType: "NamespaceA_ServiceARequest",
  237. outputType: "NamespaceA_ServiceAResponse"
  238. )
  239. let outputStreamingMethod = MethodDescriptor(
  240. documentation: "Documentation for outputStreamingMethod",
  241. name: "outputStreamingMethod",
  242. isInputStreaming: false,
  243. isOutputStreaming: true,
  244. inputType: "NamespaceA_ServiceARequest",
  245. outputType: "NamespaceA_ServiceAResponse"
  246. )
  247. let service = ServiceDescriptor(
  248. documentation: "Documentation for ServiceA",
  249. name: "ServiceA",
  250. namespace: "namespaceA",
  251. methods: [inputStreamingMethod, outputStreamingMethod]
  252. )
  253. let expectedSwift =
  254. """
  255. /// Documentation for ServiceA
  256. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  257. /// Documentation for inputStreamingMethod
  258. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>
  259. /// Documentation for outputStreamingMethod
  260. func outputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>
  261. }
  262. /// Conformance to `GRPCCore.RegistrableRPCService`.
  263. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  264. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  265. router.registerHandler(
  266. for: namespaceA.ServiceA.Methods.inputStreamingMethod.descriptor,
  267. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>(),
  268. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>(),
  269. handler: { request in
  270. try await self.inputStreamingMethod(request: request)
  271. }
  272. )
  273. router.registerHandler(
  274. for: namespaceA.ServiceA.Methods.outputStreamingMethod.descriptor,
  275. deserializer: ProtobufDeserializer<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>(),
  276. serializer: ProtobufSerializer<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>(),
  277. handler: { request in
  278. try await self.outputStreamingMethod(request: request)
  279. }
  280. )
  281. }
  282. }
  283. /// Documentation for ServiceA
  284. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {
  285. /// Documentation for inputStreamingMethod
  286. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Single<namespaceA.ServiceA.Methods.inputStreamingMethod.Output>
  287. /// Documentation for outputStreamingMethod
  288. func outputStreamingMethod(request: ServerRequest.Single<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output>
  289. }
  290. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  291. public extension namespaceA.ServiceA.ServiceProtocol {
  292. func inputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.inputStreamingMethod.Output> {
  293. let response = try await self.inputStreamingMethod(request: request)
  294. return ServerResponse.Stream(single: response)
  295. }
  296. func outputStreamingMethod(request: ServerRequest.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Input>) async throws -> ServerResponse.Stream<namespaceA.ServiceA.Methods.outputStreamingMethod.Output> {
  297. let response = try await self.outputStreamingMethod(request: ServerRequest.Single(stream: request))
  298. return response
  299. }
  300. }
  301. """
  302. try assertServerCodeTranslation(
  303. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  304. expectedSwift: expectedSwift
  305. )
  306. }
  307. func testServerCodeTranslatorNoNamespaceService() throws {
  308. let method = MethodDescriptor(
  309. documentation: "Documentation for MethodA",
  310. name: "methodA",
  311. isInputStreaming: false,
  312. isOutputStreaming: false,
  313. inputType: "NamespaceA_ServiceARequest",
  314. outputType: "NamespaceA_ServiceAResponse"
  315. )
  316. let service = ServiceDescriptor(
  317. documentation: "Documentation for ServiceA",
  318. name: "ServiceA",
  319. namespace: "",
  320. methods: [method]
  321. )
  322. let expectedSwift =
  323. """
  324. /// Documentation for ServiceA
  325. protocol ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  326. /// Documentation for MethodA
  327. func methodA(request: ServerRequest.Stream<ServiceA.Methods.methodA.Input>) async throws -> ServerResponse.Stream<ServiceA.Methods.methodA.Output>
  328. }
  329. /// Conformance to `GRPCCore.RegistrableRPCService`.
  330. public extension ServiceA.StreamingServiceProtocol {
  331. func registerRPCs(with router: inout GRPCCore.RPCRouter) {
  332. router.registerHandler(
  333. for: ServiceA.Methods.methodA.descriptor,
  334. deserializer: ProtobufDeserializer<ServiceA.Methods.methodA.Input>(),
  335. serializer: ProtobufSerializer<ServiceA.Methods.methodA.Output>(),
  336. handler: { request in
  337. try await self.methodA(request: request)
  338. }
  339. )
  340. }
  341. }
  342. /// Documentation for ServiceA
  343. protocol ServiceAServiceProtocol: ServiceA.StreamingServiceProtocol {
  344. /// Documentation for MethodA
  345. func methodA(request: ServerRequest.Single<ServiceA.Methods.methodA.Input>) async throws -> ServerResponse.Single<ServiceA.Methods.methodA.Output>
  346. }
  347. /// Partial conformance to `ServiceAStreamingServiceProtocol`.
  348. public extension ServiceA.ServiceProtocol {
  349. func methodA(request: ServerRequest.Stream<ServiceA.Methods.methodA.Input>) async throws -> ServerResponse.Stream<ServiceA.Methods.methodA.Output> {
  350. let response = try await self.methodA(request: ServerRequest.Single(stream: request))
  351. return ServerResponse.Stream(single: response)
  352. }
  353. }
  354. """
  355. try self.assertServerCodeTranslation(
  356. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  357. expectedSwift: expectedSwift
  358. )
  359. }
  360. func testServerCodeTranslatorMoreServicesOrder() throws {
  361. let serviceA = ServiceDescriptor(
  362. documentation: "Documentation for ServiceA",
  363. name: "ServiceA",
  364. namespace: "namespaceA",
  365. methods: []
  366. )
  367. let serviceB = ServiceDescriptor(
  368. documentation: "Documentation for ServiceB",
  369. name: "ServiceB",
  370. namespace: "namespaceA",
  371. methods: []
  372. )
  373. let expectedSwift =
  374. """
  375. /// Documentation for ServiceA
  376. protocol namespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
  377. /// Conformance to `GRPCCore.RegistrableRPCService`.
  378. public extension namespaceA.ServiceA.StreamingServiceProtocol {
  379. func registerRPCs(with router: inout GRPCCore.RPCRouter) {}
  380. }
  381. /// Documentation for ServiceA
  382. protocol namespaceA_ServiceAServiceProtocol: namespaceA.ServiceA.StreamingServiceProtocol {}
  383. /// Partial conformance to `namespaceA_ServiceAStreamingServiceProtocol`.
  384. public extension namespaceA.ServiceA.ServiceProtocol {
  385. }
  386. /// Documentation for ServiceB
  387. protocol namespaceA_ServiceBStreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
  388. /// Conformance to `GRPCCore.RegistrableRPCService`.
  389. public extension namespaceA.ServiceB.StreamingServiceProtocol {
  390. func registerRPCs(with router: inout GRPCCore.RPCRouter) {}
  391. }
  392. /// Documentation for ServiceB
  393. protocol namespaceA_ServiceBServiceProtocol: namespaceA.ServiceB.StreamingServiceProtocol {}
  394. /// Partial conformance to `namespaceA_ServiceBStreamingServiceProtocol`.
  395. public extension namespaceA.ServiceB.ServiceProtocol {
  396. }
  397. """
  398. try self.assertServerCodeTranslation(
  399. codeGenerationRequest: makeCodeGenerationRequest(services: [serviceA, serviceB]),
  400. expectedSwift: expectedSwift
  401. )
  402. }
  403. private func assertServerCodeTranslation(
  404. codeGenerationRequest: CodeGenerationRequest,
  405. expectedSwift: String
  406. ) throws {
  407. let translator = ServerCodeTranslator()
  408. let codeBlocks = try translator.translate(from: codeGenerationRequest)
  409. let renderer = TextBasedRenderer.default
  410. renderer.renderCodeBlocks(codeBlocks)
  411. let contents = renderer.renderedContents()
  412. try XCTAssertEqualWithDiff(contents, expectedSwift)
  413. }
  414. }
  415. #endif // os(macOS) || os(Linux)