ProtobufCodeGeneratorTests.swift 20 KB

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