ProtobufCodeGeneratorTests.swift 21 KB

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