ProtobufCodeGeneratorTests.swift 19 KB

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