ProtobufCodeGeneratorTests.swift 19 KB

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