ProtobufCodeGeneratorTests.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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 GRPCCodeGen
  18. import GRPCProtobufCodeGen
  19. import SwiftProtobuf
  20. import SwiftProtobufPluginLibrary
  21. import XCTest
  22. final class ProtobufCodeGeneratorTests: XCTestCase {
  23. func testProtobufCodeGenerator() throws {
  24. try testCodeGeneration(
  25. indentation: 4,
  26. visibility: .internal,
  27. client: true,
  28. server: false,
  29. expectedCode: """
  30. // Copyright 2015 gRPC authors.
  31. //
  32. // Licensed under the Apache License, Version 2.0 (the "License");
  33. // you may not use this file except in compliance with the License.
  34. // You may obtain a copy of the License at
  35. //
  36. // http://www.apache.org/licenses/LICENSE-2.0
  37. //
  38. // Unless required by applicable law or agreed to in writing, software
  39. // distributed under the License is distributed on an "AS IS" BASIS,
  40. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  41. // See the License for the specific language governing permissions and
  42. // limitations under the License.
  43. // DO NOT EDIT.
  44. // swift-format-ignore-file
  45. //
  46. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  47. // Source: helloworld.proto
  48. //
  49. // For information on using the generated types, please see the documentation:
  50. // https://github.com/grpc/grpc-swift
  51. import GRPCCore
  52. import GRPCProtobuf
  53. internal enum Helloworld {
  54. internal enum Greeter {
  55. internal enum Method {
  56. internal enum SayHello {
  57. internal typealias Input = Helloworld_HelloRequest
  58. internal typealias Output = Helloworld_HelloReply
  59. internal static let descriptor = MethodDescriptor(
  60. service: "helloworld.Greeter",
  61. method: "SayHello"
  62. )
  63. }
  64. internal static let descriptors: [MethodDescriptor] = [
  65. SayHello.descriptor
  66. ]
  67. }
  68. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  69. internal typealias ClientProtocol = Helloworld_GreeterClientProtocol
  70. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  71. internal typealias Client = Helloworld_GreeterClient
  72. }
  73. }
  74. /// The greeting service definition.
  75. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  76. internal protocol Helloworld_GreeterClientProtocol: Sendable {
  77. /// Sends a greeting.
  78. func sayHello<R>(
  79. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  80. serializer: some MessageSerializer<Helloworld.Greeter.Method.SayHello.Input>,
  81. deserializer: some MessageDeserializer<Helloworld.Greeter.Method.SayHello.Output>,
  82. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  83. ) async throws -> R where R: Sendable
  84. }
  85. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  86. extension Helloworld.Greeter.ClientProtocol {
  87. internal func sayHello<R>(
  88. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  89. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  90. ) async throws -> R where R: Sendable {
  91. try await self.sayHello(
  92. request: request,
  93. serializer: ProtobufSerializer<Helloworld.Greeter.Method.SayHello.Input>(),
  94. deserializer: ProtobufDeserializer<Helloworld.Greeter.Method.SayHello.Output>(),
  95. body
  96. )
  97. }
  98. }
  99. /// The greeting service definition.
  100. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  101. internal struct Helloworld_GreeterClient: Helloworld.Greeter.ClientProtocol {
  102. private let client: GRPCCore.GRPCClient
  103. internal init(client: GRPCCore.GRPCClient) {
  104. self.client = client
  105. }
  106. /// Sends a greeting.
  107. internal func sayHello<R>(
  108. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  109. serializer: some MessageSerializer<Helloworld.Greeter.Method.SayHello.Input>,
  110. deserializer: some MessageDeserializer<Helloworld.Greeter.Method.SayHello.Output>,
  111. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  112. ) async throws -> R where R: Sendable {
  113. try await self.client.unary(
  114. request: request,
  115. descriptor: Helloworld.Greeter.Method.SayHello.descriptor,
  116. serializer: serializer,
  117. deserializer: deserializer,
  118. handler: body
  119. )
  120. }
  121. }
  122. """
  123. )
  124. try testCodeGeneration(
  125. indentation: 2,
  126. visibility: .public,
  127. client: false,
  128. server: true,
  129. expectedCode: """
  130. // Copyright 2015 gRPC authors.
  131. //
  132. // Licensed under the Apache License, Version 2.0 (the "License");
  133. // you may not use this file except in compliance with the License.
  134. // You may obtain a copy of the License at
  135. //
  136. // http://www.apache.org/licenses/LICENSE-2.0
  137. //
  138. // Unless required by applicable law or agreed to in writing, software
  139. // distributed under the License is distributed on an "AS IS" BASIS,
  140. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  141. // See the License for the specific language governing permissions and
  142. // limitations under the License.
  143. // DO NOT EDIT.
  144. // swift-format-ignore-file
  145. //
  146. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  147. // Source: helloworld.proto
  148. //
  149. // For information on using the generated types, please see the documentation:
  150. // https://github.com/grpc/grpc-swift
  151. import GRPCCore
  152. import GRPCProtobuf
  153. public enum Helloworld {
  154. public enum Greeter {
  155. public enum Method {
  156. public enum SayHello {
  157. public typealias Input = Helloworld_HelloRequest
  158. public typealias Output = Helloworld_HelloReply
  159. public static let descriptor = MethodDescriptor(
  160. service: "helloworld.Greeter",
  161. method: "SayHello"
  162. )
  163. }
  164. public static let descriptors: [MethodDescriptor] = [
  165. SayHello.descriptor
  166. ]
  167. }
  168. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  169. public typealias StreamingServiceProtocol = Helloworld_GreeterStreamingServiceProtocol
  170. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  171. public typealias ServiceProtocol = Helloworld_GreeterServiceProtocol
  172. }
  173. }
  174. /// The greeting service definition.
  175. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  176. public protocol Helloworld_GreeterStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  177. /// Sends a greeting.
  178. func sayHello(request: ServerRequest.Stream<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Stream<Helloworld.Greeter.Method.SayHello.Output>
  179. }
  180. /// Conformance to `GRPCCore.RegistrableRPCService`.
  181. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  182. extension Helloworld.Greeter.StreamingServiceProtocol {
  183. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  184. public func registerMethods(with router: inout GRPCCore.RPCRouter) {
  185. router.registerHandler(
  186. forMethod: Helloworld.Greeter.Method.SayHello.descriptor,
  187. deserializer: ProtobufDeserializer<Helloworld.Greeter.Method.SayHello.Input>(),
  188. serializer: ProtobufSerializer<Helloworld.Greeter.Method.SayHello.Output>(),
  189. handler: { request in
  190. try await self.sayHello(request: request)
  191. }
  192. )
  193. }
  194. }
  195. /// The greeting service definition.
  196. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  197. public protocol Helloworld_GreeterServiceProtocol: Helloworld.Greeter.StreamingServiceProtocol {
  198. /// Sends a greeting.
  199. func sayHello(request: ServerRequest.Single<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Single<Helloworld.Greeter.Method.SayHello.Output>
  200. }
  201. /// Partial conformance to `Helloworld_GreeterStreamingServiceProtocol`.
  202. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  203. extension Helloworld.Greeter.ServiceProtocol {
  204. public func sayHello(request: ServerRequest.Stream<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Stream<Helloworld.Greeter.Method.SayHello.Output> {
  205. let response = try await self.sayHello(request: ServerRequest.Single(stream: request))
  206. return ServerResponse.Stream(single: response)
  207. }
  208. }
  209. """
  210. )
  211. try testCodeGeneration(
  212. indentation: 2,
  213. visibility: .package,
  214. client: true,
  215. server: true,
  216. expectedCode: """
  217. // Copyright 2015 gRPC authors.
  218. //
  219. // Licensed under the Apache License, Version 2.0 (the "License");
  220. // you may not use this file except in compliance with the License.
  221. // You may obtain a copy of the License at
  222. //
  223. // http://www.apache.org/licenses/LICENSE-2.0
  224. //
  225. // Unless required by applicable law or agreed to in writing, software
  226. // distributed under the License is distributed on an "AS IS" BASIS,
  227. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  228. // See the License for the specific language governing permissions and
  229. // limitations under the License.
  230. // DO NOT EDIT.
  231. // swift-format-ignore-file
  232. //
  233. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  234. // Source: helloworld.proto
  235. //
  236. // For information on using the generated types, please see the documentation:
  237. // https://github.com/grpc/grpc-swift
  238. import GRPCCore
  239. import GRPCProtobuf
  240. package enum Helloworld {
  241. package enum Greeter {
  242. package enum Method {
  243. package enum SayHello {
  244. package typealias Input = Helloworld_HelloRequest
  245. package typealias Output = Helloworld_HelloReply
  246. package static let descriptor = MethodDescriptor(
  247. service: "helloworld.Greeter",
  248. method: "SayHello"
  249. )
  250. }
  251. package static let descriptors: [MethodDescriptor] = [
  252. SayHello.descriptor
  253. ]
  254. }
  255. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  256. package typealias StreamingServiceProtocol = Helloworld_GreeterStreamingServiceProtocol
  257. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  258. package typealias ServiceProtocol = Helloworld_GreeterServiceProtocol
  259. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  260. package typealias ClientProtocol = Helloworld_GreeterClientProtocol
  261. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  262. package typealias Client = Helloworld_GreeterClient
  263. }
  264. }
  265. /// The greeting service definition.
  266. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  267. package protocol Helloworld_GreeterStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  268. /// Sends a greeting.
  269. func sayHello(request: ServerRequest.Stream<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Stream<Helloworld.Greeter.Method.SayHello.Output>
  270. }
  271. /// Conformance to `GRPCCore.RegistrableRPCService`.
  272. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  273. extension Helloworld.Greeter.StreamingServiceProtocol {
  274. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  275. package func registerMethods(with router: inout GRPCCore.RPCRouter) {
  276. router.registerHandler(
  277. forMethod: Helloworld.Greeter.Method.SayHello.descriptor,
  278. deserializer: ProtobufDeserializer<Helloworld.Greeter.Method.SayHello.Input>(),
  279. serializer: ProtobufSerializer<Helloworld.Greeter.Method.SayHello.Output>(),
  280. handler: { request in
  281. try await self.sayHello(request: request)
  282. }
  283. )
  284. }
  285. }
  286. /// The greeting service definition.
  287. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  288. package protocol Helloworld_GreeterServiceProtocol: Helloworld.Greeter.StreamingServiceProtocol {
  289. /// Sends a greeting.
  290. func sayHello(request: ServerRequest.Single<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Single<Helloworld.Greeter.Method.SayHello.Output>
  291. }
  292. /// Partial conformance to `Helloworld_GreeterStreamingServiceProtocol`.
  293. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  294. extension Helloworld.Greeter.ServiceProtocol {
  295. package func sayHello(request: ServerRequest.Stream<Helloworld.Greeter.Method.SayHello.Input>) async throws -> ServerResponse.Stream<Helloworld.Greeter.Method.SayHello.Output> {
  296. let response = try await self.sayHello(request: ServerRequest.Single(stream: request))
  297. return ServerResponse.Stream(single: response)
  298. }
  299. }
  300. /// The greeting service definition.
  301. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  302. package protocol Helloworld_GreeterClientProtocol: Sendable {
  303. /// Sends a greeting.
  304. func sayHello<R>(
  305. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  306. serializer: some MessageSerializer<Helloworld.Greeter.Method.SayHello.Input>,
  307. deserializer: some MessageDeserializer<Helloworld.Greeter.Method.SayHello.Output>,
  308. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  309. ) async throws -> R where R: Sendable
  310. }
  311. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  312. extension Helloworld.Greeter.ClientProtocol {
  313. package func sayHello<R>(
  314. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  315. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  316. ) async throws -> R where R: Sendable {
  317. try await self.sayHello(
  318. request: request,
  319. serializer: ProtobufSerializer<Helloworld.Greeter.Method.SayHello.Input>(),
  320. deserializer: ProtobufDeserializer<Helloworld.Greeter.Method.SayHello.Output>(),
  321. body
  322. )
  323. }
  324. }
  325. /// The greeting service definition.
  326. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  327. package struct Helloworld_GreeterClient: Helloworld.Greeter.ClientProtocol {
  328. private let client: GRPCCore.GRPCClient
  329. package init(client: GRPCCore.GRPCClient) {
  330. self.client = client
  331. }
  332. /// Sends a greeting.
  333. package func sayHello<R>(
  334. request: ClientRequest.Single<Helloworld.Greeter.Method.SayHello.Input>,
  335. serializer: some MessageSerializer<Helloworld.Greeter.Method.SayHello.Input>,
  336. deserializer: some MessageDeserializer<Helloworld.Greeter.Method.SayHello.Output>,
  337. _ body: @Sendable @escaping (ClientResponse.Single<Helloworld.Greeter.Method.SayHello.Output>) async throws -> R
  338. ) async throws -> R where R: Sendable {
  339. try await self.client.unary(
  340. request: request,
  341. descriptor: Helloworld.Greeter.Method.SayHello.descriptor,
  342. serializer: serializer,
  343. deserializer: deserializer,
  344. handler: body
  345. )
  346. }
  347. }
  348. """
  349. )
  350. }
  351. func testCodeGeneration(
  352. indentation: Int,
  353. visibility: SourceGenerator.Configuration.AccessLevel,
  354. client: Bool,
  355. server: Bool,
  356. expectedCode: String
  357. ) throws {
  358. let configs = SourceGenerator.Configuration(
  359. accessLevel: visibility,
  360. client: client,
  361. server: server,
  362. indentation: indentation
  363. )
  364. let descriptorSet = DescriptorSet(protos: [Google_Protobuf_FileDescriptorProto.helloWorld])
  365. guard let fileDescriptor = descriptorSet.fileDescriptor(named: "helloworld.proto") else {
  366. return XCTFail(
  367. """
  368. Could not find the file descriptor of "helloworld.proto".
  369. """
  370. )
  371. }
  372. let generator = ProtobufCodeGenerator(configuration: configs)
  373. try XCTAssertEqualWithDiff(
  374. try generator.generateCode(
  375. from: fileDescriptor,
  376. protoFileModuleMappings: ProtoFileToModuleMappings()
  377. ),
  378. expectedCode
  379. )
  380. }
  381. }
  382. private func diff(expected: String, actual: String) throws -> String {
  383. let process = Process()
  384. process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
  385. process.arguments = [
  386. "bash", "-c",
  387. "diff -U5 --label=expected <(echo '\(expected)') --label=actual <(echo '\(actual)')",
  388. ]
  389. let pipe = Pipe()
  390. process.standardOutput = pipe
  391. try process.run()
  392. process.waitUntilExit()
  393. let pipeData = try XCTUnwrap(
  394. pipe.fileHandleForReading.readToEnd(),
  395. """
  396. No output from command:
  397. \(process.executableURL!.path) \(process.arguments!.joined(separator: " "))
  398. """
  399. )
  400. return String(decoding: pipeData, as: UTF8.self)
  401. }
  402. internal func XCTAssertEqualWithDiff(
  403. _ actual: String,
  404. _ expected: String,
  405. file: StaticString = #filePath,
  406. line: UInt = #line
  407. ) throws {
  408. if actual == expected { return }
  409. XCTFail(
  410. """
  411. XCTAssertEqualWithDiff failed (click for diff)
  412. \(try diff(expected: expected, actual: actual))
  413. """,
  414. file: file,
  415. line: line
  416. )
  417. }
  418. #endif // os(macOS) || os(Linux)