ProtobufCodeGeneratorTests.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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_Greeter {
  56. internal enum Method {
  57. internal enum SayHello {
  58. internal typealias Input = Helloworld_HelloRequest
  59. internal typealias Output = Helloworld_HelloReply
  60. internal static let descriptor = MethodDescriptor(
  61. service: "helloworld.Greeter",
  62. method: "SayHello"
  63. )
  64. }
  65. internal static let descriptors: [MethodDescriptor] = [
  66. SayHello.descriptor
  67. ]
  68. }
  69. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  70. internal typealias ClientProtocol = Helloworld_GreeterClientProtocol
  71. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  72. internal typealias Client = Helloworld_GreeterClient
  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. import DifferentModule
  154. import ExtraModule
  155. public enum Helloworld_Greeter {
  156. public enum Method {
  157. public enum SayHello {
  158. public typealias Input = Helloworld_HelloRequest
  159. public typealias Output = Helloworld_HelloReply
  160. public static let descriptor = MethodDescriptor(
  161. service: "helloworld.Greeter",
  162. method: "SayHello"
  163. )
  164. }
  165. public static let descriptors: [MethodDescriptor] = [
  166. SayHello.descriptor
  167. ]
  168. }
  169. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  170. public typealias StreamingServiceProtocol = Helloworld_GreeterStreamingServiceProtocol
  171. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  172. public typealias ServiceProtocol = Helloworld_GreeterServiceProtocol
  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. import DifferentModule
  241. import ExtraModule
  242. package enum Helloworld_Greeter {
  243. package enum Method {
  244. package enum SayHello {
  245. package typealias Input = Helloworld_HelloRequest
  246. package typealias Output = Helloworld_HelloReply
  247. package static let descriptor = MethodDescriptor(
  248. service: "helloworld.Greeter",
  249. method: "SayHello"
  250. )
  251. }
  252. package static let descriptors: [MethodDescriptor] = [
  253. SayHello.descriptor
  254. ]
  255. }
  256. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  257. package typealias StreamingServiceProtocol = Helloworld_GreeterStreamingServiceProtocol
  258. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  259. package typealias ServiceProtocol = Helloworld_GreeterServiceProtocol
  260. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  261. package typealias ClientProtocol = Helloworld_GreeterClientProtocol
  262. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  263. package typealias Client = Helloworld_GreeterClient
  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(
  365. protos: [
  366. Google_Protobuf_FileDescriptorProto(name: "same-module.proto", package: "same-package"),
  367. Google_Protobuf_FileDescriptorProto(
  368. name: "different-module.proto",
  369. package: "different-package"
  370. ),
  371. Google_Protobuf_FileDescriptorProto.helloWorld,
  372. ])
  373. guard let fileDescriptor = descriptorSet.fileDescriptor(named: "helloworld.proto") else {
  374. return XCTFail(
  375. """
  376. Could not find the file descriptor of "helloworld.proto".
  377. """
  378. )
  379. }
  380. let moduleMappings = SwiftProtobuf_GenSwift_ModuleMappings.with {
  381. $0.mapping = [
  382. SwiftProtobuf_GenSwift_ModuleMappings.Entry.with {
  383. $0.protoFilePath = ["different-module.proto"]
  384. $0.moduleName = "DifferentModule"
  385. }
  386. ]
  387. }
  388. let generator = ProtobufCodeGenerator(configuration: configs)
  389. try XCTAssertEqualWithDiff(
  390. try generator.generateCode(
  391. from: fileDescriptor,
  392. protoFileModuleMappings: ProtoFileToModuleMappings(moduleMappingsProto: moduleMappings),
  393. extraModuleImports: ["ExtraModule"]
  394. ),
  395. expectedCode
  396. )
  397. }
  398. }
  399. private func diff(expected: String, actual: String) throws -> String {
  400. let process = Process()
  401. process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
  402. process.arguments = [
  403. "bash", "-c",
  404. "diff -U5 --label=expected <(echo '\(expected)') --label=actual <(echo '\(actual)')",
  405. ]
  406. let pipe = Pipe()
  407. process.standardOutput = pipe
  408. try process.run()
  409. process.waitUntilExit()
  410. let pipeData = try XCTUnwrap(
  411. pipe.fileHandleForReading.readToEnd(),
  412. """
  413. No output from command:
  414. \(process.executableURL!.path) \(process.arguments!.joined(separator: " "))
  415. """
  416. )
  417. return String(decoding: pipeData, as: UTF8.self)
  418. }
  419. internal func XCTAssertEqualWithDiff(
  420. _ actual: String,
  421. _ expected: String,
  422. file: StaticString = #filePath,
  423. line: UInt = #line
  424. ) throws {
  425. if actual == expected { return }
  426. XCTFail(
  427. """
  428. XCTAssertEqualWithDiff failed (click for diff)
  429. \(try diff(expected: expected, actual: actual))
  430. """,
  431. file: file,
  432. line: line
  433. )
  434. }
  435. #endif // os(macOS) || os(Linux)