ProtobufCodeGeneratorTests.swift 20 KB

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