ClientCodeTranslatorSnippetBasedTests.swift 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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 ClientCodeTranslatorSnippetBasedTests: XCTestCase {
  20. typealias MethodDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  21. typealias ServiceDescriptor = GRPCCodeGen.CodeGenerationRequest.ServiceDescriptor
  22. typealias Name = GRPCCodeGen.CodeGenerationRequest.Name
  23. func testClientCodeTranslatorUnaryMethod() throws {
  24. let method = MethodDescriptor(
  25. documentation: "/// Documentation for MethodA",
  26. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  27. isInputStreaming: false,
  28. isOutputStreaming: false,
  29. inputType: "NamespaceA_ServiceARequest",
  30. outputType: "NamespaceA_ServiceAResponse"
  31. )
  32. let service = ServiceDescriptor(
  33. documentation: "/// Documentation for ServiceA",
  34. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  35. namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
  36. methods: [method]
  37. )
  38. let expectedSwift =
  39. """
  40. /// Documentation for ServiceA
  41. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  42. public protocol NamespaceA_ServiceAClientProtocol: Sendable {
  43. /// Documentation for MethodA
  44. func methodA<R>(
  45. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  46. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  47. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  48. options: CallOptions,
  49. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R
  50. ) async throws -> R where R: Sendable
  51. }
  52. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  53. extension NamespaceA_ServiceA.ClientProtocol {
  54. public func methodA<R>(
  55. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  56. options: CallOptions = .defaults,
  57. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  58. try $0.message
  59. }
  60. ) async throws -> R where R: Sendable {
  61. try await self.methodA(
  62. request: request,
  63. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  64. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  65. options: options,
  66. body
  67. )
  68. }
  69. }
  70. /// Documentation for ServiceA
  71. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  72. public struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  73. private let client: GRPCCore.GRPCClient
  74. public init(wrapping client: GRPCCore.GRPCClient) {
  75. self.client = client
  76. }
  77. /// Documentation for MethodA
  78. public func methodA<R>(
  79. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  80. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  81. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  82. options: CallOptions = .defaults,
  83. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  84. try $0.message
  85. }
  86. ) async throws -> R where R: Sendable {
  87. try await self.client.unary(
  88. request: request,
  89. descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor,
  90. serializer: serializer,
  91. deserializer: deserializer,
  92. options: options,
  93. handler: body
  94. )
  95. }
  96. }
  97. """
  98. try self.assertClientCodeTranslation(
  99. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  100. expectedSwift: expectedSwift,
  101. accessLevel: .public
  102. )
  103. }
  104. func testClientCodeTranslatorClientStreamingMethod() throws {
  105. let method = MethodDescriptor(
  106. documentation: "/// Documentation for MethodA",
  107. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  108. isInputStreaming: true,
  109. isOutputStreaming: false,
  110. inputType: "NamespaceA_ServiceARequest",
  111. outputType: "NamespaceA_ServiceAResponse"
  112. )
  113. let service = ServiceDescriptor(
  114. documentation: "/// Documentation for ServiceA",
  115. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  116. namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
  117. methods: [method]
  118. )
  119. let expectedSwift =
  120. """
  121. /// Documentation for ServiceA
  122. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  123. public protocol NamespaceA_ServiceAClientProtocol: Sendable {
  124. /// Documentation for MethodA
  125. func methodA<R>(
  126. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  127. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  128. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  129. options: CallOptions,
  130. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R
  131. ) async throws -> R where R: Sendable
  132. }
  133. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  134. extension NamespaceA_ServiceA.ClientProtocol {
  135. public func methodA<R>(
  136. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  137. options: CallOptions = .defaults,
  138. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  139. try $0.message
  140. }
  141. ) async throws -> R where R: Sendable {
  142. try await self.methodA(
  143. request: request,
  144. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  145. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  146. options: options,
  147. body
  148. )
  149. }
  150. }
  151. /// Documentation for ServiceA
  152. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  153. public struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  154. private let client: GRPCCore.GRPCClient
  155. public init(wrapping client: GRPCCore.GRPCClient) {
  156. self.client = client
  157. }
  158. /// Documentation for MethodA
  159. public func methodA<R>(
  160. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  161. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  162. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  163. options: CallOptions = .defaults,
  164. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  165. try $0.message
  166. }
  167. ) async throws -> R where R: Sendable {
  168. try await self.client.clientStreaming(
  169. request: request,
  170. descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor,
  171. serializer: serializer,
  172. deserializer: deserializer,
  173. options: options,
  174. handler: body
  175. )
  176. }
  177. }
  178. """
  179. try self.assertClientCodeTranslation(
  180. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  181. expectedSwift: expectedSwift,
  182. accessLevel: .public
  183. )
  184. }
  185. func testClientCodeTranslatorServerStreamingMethod() throws {
  186. let method = MethodDescriptor(
  187. documentation: "/// Documentation for MethodA",
  188. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  189. isInputStreaming: false,
  190. isOutputStreaming: true,
  191. inputType: "NamespaceA_ServiceARequest",
  192. outputType: "NamespaceA_ServiceAResponse"
  193. )
  194. let service = ServiceDescriptor(
  195. documentation: "/// Documentation for ServiceA",
  196. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  197. namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
  198. methods: [method]
  199. )
  200. let expectedSwift =
  201. """
  202. /// Documentation for ServiceA
  203. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  204. public protocol NamespaceA_ServiceAClientProtocol: Sendable {
  205. /// Documentation for MethodA
  206. func methodA<R>(
  207. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  208. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  209. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  210. options: CallOptions,
  211. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  212. ) async throws -> R where R: Sendable
  213. }
  214. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  215. extension NamespaceA_ServiceA.ClientProtocol {
  216. public func methodA<R>(
  217. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  218. options: CallOptions = .defaults,
  219. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  220. ) async throws -> R where R: Sendable {
  221. try await self.methodA(
  222. request: request,
  223. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  224. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  225. options: options,
  226. body
  227. )
  228. }
  229. }
  230. /// Documentation for ServiceA
  231. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  232. public struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  233. private let client: GRPCCore.GRPCClient
  234. public init(wrapping client: GRPCCore.GRPCClient) {
  235. self.client = client
  236. }
  237. /// Documentation for MethodA
  238. public func methodA<R>(
  239. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  240. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  241. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  242. options: CallOptions = .defaults,
  243. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  244. ) async throws -> R where R: Sendable {
  245. try await self.client.serverStreaming(
  246. request: request,
  247. descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor,
  248. serializer: serializer,
  249. deserializer: deserializer,
  250. options: options,
  251. handler: body
  252. )
  253. }
  254. }
  255. """
  256. try self.assertClientCodeTranslation(
  257. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  258. expectedSwift: expectedSwift,
  259. accessLevel: .public
  260. )
  261. }
  262. func testClientCodeTranslatorBidirectionalStreamingMethod() throws {
  263. let method = MethodDescriptor(
  264. documentation: "/// Documentation for MethodA",
  265. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  266. isInputStreaming: true,
  267. isOutputStreaming: true,
  268. inputType: "NamespaceA_ServiceARequest",
  269. outputType: "NamespaceA_ServiceAResponse"
  270. )
  271. let service = ServiceDescriptor(
  272. documentation: "/// Documentation for ServiceA",
  273. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  274. namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
  275. methods: [method]
  276. )
  277. let expectedSwift =
  278. """
  279. /// Documentation for ServiceA
  280. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  281. public protocol NamespaceA_ServiceAClientProtocol: Sendable {
  282. /// Documentation for MethodA
  283. func methodA<R>(
  284. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  285. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  286. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  287. options: CallOptions,
  288. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  289. ) async throws -> R where R: Sendable
  290. }
  291. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  292. extension NamespaceA_ServiceA.ClientProtocol {
  293. public func methodA<R>(
  294. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  295. options: CallOptions = .defaults,
  296. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  297. ) async throws -> R where R: Sendable {
  298. try await self.methodA(
  299. request: request,
  300. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  301. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  302. options: options,
  303. body
  304. )
  305. }
  306. }
  307. /// Documentation for ServiceA
  308. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  309. public struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  310. private let client: GRPCCore.GRPCClient
  311. public init(wrapping client: GRPCCore.GRPCClient) {
  312. self.client = client
  313. }
  314. /// Documentation for MethodA
  315. public func methodA<R>(
  316. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  317. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  318. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  319. options: CallOptions = .defaults,
  320. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  321. ) async throws -> R where R: Sendable {
  322. try await self.client.bidirectionalStreaming(
  323. request: request,
  324. descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor,
  325. serializer: serializer,
  326. deserializer: deserializer,
  327. options: options,
  328. handler: body
  329. )
  330. }
  331. }
  332. """
  333. try self.assertClientCodeTranslation(
  334. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  335. expectedSwift: expectedSwift,
  336. accessLevel: .public
  337. )
  338. }
  339. func testClientCodeTranslatorMultipleMethod() throws {
  340. let methodA = MethodDescriptor(
  341. documentation: "/// Documentation for MethodA",
  342. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  343. isInputStreaming: true,
  344. isOutputStreaming: false,
  345. inputType: "NamespaceA_ServiceARequest",
  346. outputType: "NamespaceA_ServiceAResponse"
  347. )
  348. let methodB = MethodDescriptor(
  349. documentation: "/// Documentation for MethodB",
  350. name: Name(base: "MethodB", generatedUpperCase: "MethodB", generatedLowerCase: "methodB"),
  351. isInputStreaming: false,
  352. isOutputStreaming: true,
  353. inputType: "NamespaceA_ServiceARequest",
  354. outputType: "NamespaceA_ServiceAResponse"
  355. )
  356. let service = ServiceDescriptor(
  357. documentation: "/// Documentation for ServiceA",
  358. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  359. namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
  360. methods: [methodA, methodB]
  361. )
  362. let expectedSwift =
  363. """
  364. /// Documentation for ServiceA
  365. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  366. package protocol NamespaceA_ServiceAClientProtocol: Sendable {
  367. /// Documentation for MethodA
  368. func methodA<R>(
  369. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  370. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  371. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  372. options: CallOptions,
  373. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R
  374. ) async throws -> R where R: Sendable
  375. /// Documentation for MethodB
  376. func methodB<R>(
  377. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  378. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  379. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  380. options: CallOptions,
  381. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  382. ) async throws -> R where R: Sendable
  383. }
  384. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  385. extension NamespaceA_ServiceA.ClientProtocol {
  386. package func methodA<R>(
  387. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  388. options: CallOptions = .defaults,
  389. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  390. try $0.message
  391. }
  392. ) async throws -> R where R: Sendable {
  393. try await self.methodA(
  394. request: request,
  395. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  396. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  397. options: options,
  398. body
  399. )
  400. }
  401. package func methodB<R>(
  402. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  403. options: CallOptions = .defaults,
  404. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  405. ) async throws -> R where R: Sendable {
  406. try await self.methodB(
  407. request: request,
  408. serializer: ProtobufSerializer<NamespaceA_ServiceARequest>(),
  409. deserializer: ProtobufDeserializer<NamespaceA_ServiceAResponse>(),
  410. options: options,
  411. body
  412. )
  413. }
  414. }
  415. /// Documentation for ServiceA
  416. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  417. package struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  418. private let client: GRPCCore.GRPCClient
  419. package init(wrapping client: GRPCCore.GRPCClient) {
  420. self.client = client
  421. }
  422. /// Documentation for MethodA
  423. package func methodA<R>(
  424. request: ClientRequest.Stream<NamespaceA_ServiceARequest>,
  425. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  426. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  427. options: CallOptions = .defaults,
  428. _ body: @Sendable @escaping (ClientResponse.Single<NamespaceA_ServiceAResponse>) async throws -> R = {
  429. try $0.message
  430. }
  431. ) async throws -> R where R: Sendable {
  432. try await self.client.clientStreaming(
  433. request: request,
  434. descriptor: NamespaceA_ServiceA.Method.MethodA.descriptor,
  435. serializer: serializer,
  436. deserializer: deserializer,
  437. options: options,
  438. handler: body
  439. )
  440. }
  441. /// Documentation for MethodB
  442. package func methodB<R>(
  443. request: ClientRequest.Single<NamespaceA_ServiceARequest>,
  444. serializer: some MessageSerializer<NamespaceA_ServiceARequest>,
  445. deserializer: some MessageDeserializer<NamespaceA_ServiceAResponse>,
  446. options: CallOptions = .defaults,
  447. _ body: @Sendable @escaping (ClientResponse.Stream<NamespaceA_ServiceAResponse>) async throws -> R
  448. ) async throws -> R where R: Sendable {
  449. try await self.client.serverStreaming(
  450. request: request,
  451. descriptor: NamespaceA_ServiceA.Method.MethodB.descriptor,
  452. serializer: serializer,
  453. deserializer: deserializer,
  454. options: options,
  455. handler: body
  456. )
  457. }
  458. }
  459. """
  460. try self.assertClientCodeTranslation(
  461. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  462. expectedSwift: expectedSwift,
  463. accessLevel: .package
  464. )
  465. }
  466. func testClientCodeTranslatorNoNamespaceService() throws {
  467. let method = MethodDescriptor(
  468. documentation: "/// Documentation for MethodA",
  469. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  470. isInputStreaming: false,
  471. isOutputStreaming: false,
  472. inputType: "ServiceARequest",
  473. outputType: "ServiceAResponse"
  474. )
  475. let service = ServiceDescriptor(
  476. documentation: "/// Documentation for ServiceA",
  477. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  478. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  479. methods: [method]
  480. )
  481. let expectedSwift =
  482. """
  483. /// Documentation for ServiceA
  484. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  485. internal protocol ServiceAClientProtocol: Sendable {
  486. /// Documentation for MethodA
  487. func methodA<R>(
  488. request: ClientRequest.Single<ServiceARequest>,
  489. serializer: some MessageSerializer<ServiceARequest>,
  490. deserializer: some MessageDeserializer<ServiceAResponse>,
  491. options: CallOptions,
  492. _ body: @Sendable @escaping (ClientResponse.Single<ServiceAResponse>) async throws -> R
  493. ) async throws -> R where R: Sendable
  494. }
  495. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  496. extension ServiceA.ClientProtocol {
  497. internal func methodA<R>(
  498. request: ClientRequest.Single<ServiceARequest>,
  499. options: CallOptions = .defaults,
  500. _ body: @Sendable @escaping (ClientResponse.Single<ServiceAResponse>) async throws -> R = {
  501. try $0.message
  502. }
  503. ) async throws -> R where R: Sendable {
  504. try await self.methodA(
  505. request: request,
  506. serializer: ProtobufSerializer<ServiceARequest>(),
  507. deserializer: ProtobufDeserializer<ServiceAResponse>(),
  508. options: options,
  509. body
  510. )
  511. }
  512. }
  513. /// Documentation for ServiceA
  514. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  515. internal struct ServiceAClient: ServiceA.ClientProtocol {
  516. private let client: GRPCCore.GRPCClient
  517. internal init(wrapping client: GRPCCore.GRPCClient) {
  518. self.client = client
  519. }
  520. /// Documentation for MethodA
  521. internal func methodA<R>(
  522. request: ClientRequest.Single<ServiceARequest>,
  523. serializer: some MessageSerializer<ServiceARequest>,
  524. deserializer: some MessageDeserializer<ServiceAResponse>,
  525. options: CallOptions = .defaults,
  526. _ body: @Sendable @escaping (ClientResponse.Single<ServiceAResponse>) async throws -> R = {
  527. try $0.message
  528. }
  529. ) async throws -> R where R: Sendable {
  530. try await self.client.unary(
  531. request: request,
  532. descriptor: ServiceA.Method.MethodA.descriptor,
  533. serializer: serializer,
  534. deserializer: deserializer,
  535. options: options,
  536. handler: body
  537. )
  538. }
  539. }
  540. """
  541. try self.assertClientCodeTranslation(
  542. codeGenerationRequest: makeCodeGenerationRequest(services: [service]),
  543. expectedSwift: expectedSwift,
  544. accessLevel: .internal
  545. )
  546. }
  547. func testClientCodeTranslatorMultipleServices() throws {
  548. let serviceA = ServiceDescriptor(
  549. documentation: "/// Documentation for ServiceA",
  550. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
  551. namespace: Name(
  552. base: "nammespaceA",
  553. generatedUpperCase: "NamespaceA",
  554. generatedLowerCase: ""
  555. ),
  556. methods: []
  557. )
  558. let serviceB = ServiceDescriptor(
  559. documentation: """
  560. /// Documentation for ServiceB
  561. ///
  562. /// Line 2
  563. """,
  564. name: Name(base: "ServiceB", generatedUpperCase: "ServiceB", generatedLowerCase: ""),
  565. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  566. methods: []
  567. )
  568. let expectedSwift =
  569. """
  570. /// Documentation for ServiceA
  571. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  572. public protocol NamespaceA_ServiceAClientProtocol: Sendable {}
  573. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  574. extension NamespaceA_ServiceA.ClientProtocol {
  575. }
  576. /// Documentation for ServiceA
  577. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  578. public struct NamespaceA_ServiceAClient: NamespaceA_ServiceA.ClientProtocol {
  579. private let client: GRPCCore.GRPCClient
  580. public init(wrapping client: GRPCCore.GRPCClient) {
  581. self.client = client
  582. }
  583. }
  584. /// Documentation for ServiceB
  585. ///
  586. /// Line 2
  587. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  588. public protocol ServiceBClientProtocol: Sendable {}
  589. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  590. extension ServiceB.ClientProtocol {
  591. }
  592. /// Documentation for ServiceB
  593. ///
  594. /// Line 2
  595. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
  596. public struct ServiceBClient: ServiceB.ClientProtocol {
  597. private let client: GRPCCore.GRPCClient
  598. public init(wrapping client: GRPCCore.GRPCClient) {
  599. self.client = client
  600. }
  601. }
  602. """
  603. try self.assertClientCodeTranslation(
  604. codeGenerationRequest: makeCodeGenerationRequest(services: [serviceA, serviceB]),
  605. expectedSwift: expectedSwift,
  606. accessLevel: .public
  607. )
  608. }
  609. private func assertClientCodeTranslation(
  610. codeGenerationRequest: CodeGenerationRequest,
  611. expectedSwift: String,
  612. accessLevel: SourceGenerator.Configuration.AccessLevel
  613. ) throws {
  614. let translator = ClientCodeTranslator(accessLevel: accessLevel)
  615. let codeBlocks = try translator.translate(from: codeGenerationRequest)
  616. let renderer = TextBasedRenderer.default
  617. renderer.renderCodeBlocks(codeBlocks)
  618. let contents = renderer.renderedContents()
  619. try XCTAssertEqualWithDiff(contents, expectedSwift)
  620. }
  621. }
  622. #endif // os(macOS) || os(Linux)