ProtobufCodeGenParserTests.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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. import GRPCCodeGen
  17. import SwiftProtobuf
  18. import SwiftProtobufPluginLibrary
  19. import XCTest
  20. @testable import GRPCProtobufCodeGen
  21. final class ProtobufCodeGenParserTests: XCTestCase {
  22. func testParser() throws {
  23. let descriptorSet = DescriptorSet(
  24. protos: [
  25. Google_Protobuf_FileDescriptorProto(
  26. name: "same-module.proto",
  27. package: "same-package"
  28. ),
  29. Google_Protobuf_FileDescriptorProto(
  30. name: "different-module.proto",
  31. package: "different-package"
  32. ),
  33. Google_Protobuf_FileDescriptorProto.helloWorld,
  34. ]
  35. )
  36. guard let fileDescriptor = descriptorSet.fileDescriptor(named: "helloworld.proto") else {
  37. return XCTFail(
  38. """
  39. Could not find the file descriptor of "helloworld.proto".
  40. """
  41. )
  42. }
  43. let moduleMappings = SwiftProtobuf_GenSwift_ModuleMappings.with {
  44. $0.mapping = [
  45. SwiftProtobuf_GenSwift_ModuleMappings.Entry.with {
  46. $0.protoFilePath = ["different-module.proto"]
  47. $0.moduleName = "DifferentModule"
  48. }
  49. ]
  50. }
  51. let parsedCodeGenRequest = try ProtobufCodeGenParser(
  52. input: fileDescriptor,
  53. protoFileModuleMappings: ProtoFileToModuleMappings(moduleMappingsProto: moduleMappings),
  54. extraModuleImports: ["ExtraModule"]
  55. ).parse()
  56. self.testCommonHelloworldParsedRequestFields(for: parsedCodeGenRequest)
  57. let expectedMethod = CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(
  58. documentation: "/// Sends a greeting.\n",
  59. name: CodeGenerationRequest.Name(
  60. base: "SayHello",
  61. generatedUpperCase: "SayHello",
  62. generatedLowerCase: "sayHello"
  63. ),
  64. isInputStreaming: false,
  65. isOutputStreaming: false,
  66. inputType: "Helloworld_HelloRequest",
  67. outputType: "Helloworld_HelloReply"
  68. )
  69. guard let method = parsedCodeGenRequest.services.first?.methods.first else { return XCTFail() }
  70. XCTAssertEqual(method, expectedMethod)
  71. let expectedService = CodeGenerationRequest.ServiceDescriptor(
  72. documentation: "/// The greeting service definition.\n",
  73. name: CodeGenerationRequest.Name(
  74. base: "Greeter",
  75. generatedUpperCase: "Greeter",
  76. generatedLowerCase: "greeter"
  77. ),
  78. namespace: CodeGenerationRequest.Name(
  79. base: "helloworld",
  80. generatedUpperCase: "Helloworld",
  81. generatedLowerCase: "helloworld"
  82. ),
  83. methods: [expectedMethod]
  84. )
  85. guard let service = parsedCodeGenRequest.services.first else { return XCTFail() }
  86. XCTAssertEqual(service, expectedService)
  87. XCTAssertEqual(service.methods.count, 1)
  88. XCTAssertEqual(
  89. parsedCodeGenRequest.lookupSerializer("Helloworld_HelloRequest"),
  90. "ProtobufSerializer<Helloworld_HelloRequest>()"
  91. )
  92. XCTAssertEqual(
  93. parsedCodeGenRequest.lookupDeserializer("Helloworld_HelloRequest"),
  94. "ProtobufDeserializer<Helloworld_HelloRequest>()"
  95. )
  96. }
  97. func testParserNestedPackage() throws {
  98. let descriptorSet = DescriptorSet(
  99. protos: [
  100. Google_Protobuf_FileDescriptorProto(
  101. name: "same-module.proto",
  102. package: "same-package"
  103. ),
  104. Google_Protobuf_FileDescriptorProto(
  105. name: "different-module.proto",
  106. package: "different-package"
  107. ),
  108. Google_Protobuf_FileDescriptorProto.helloWorldNestedPackage,
  109. ]
  110. )
  111. guard let fileDescriptor = descriptorSet.fileDescriptor(named: "helloworld.proto") else {
  112. return XCTFail(
  113. """
  114. Could not find the file descriptor of "helloworld.proto".
  115. """
  116. )
  117. }
  118. let moduleMappings = SwiftProtobuf_GenSwift_ModuleMappings.with {
  119. $0.mapping = [
  120. SwiftProtobuf_GenSwift_ModuleMappings.Entry.with {
  121. $0.protoFilePath = ["different-module.proto"]
  122. $0.moduleName = "DifferentModule"
  123. }
  124. ]
  125. }
  126. let parsedCodeGenRequest = try ProtobufCodeGenParser(
  127. input: fileDescriptor,
  128. protoFileModuleMappings: ProtoFileToModuleMappings(moduleMappingsProto: moduleMappings),
  129. extraModuleImports: ["ExtraModule"]
  130. ).parse()
  131. self.testCommonHelloworldParsedRequestFields(for: parsedCodeGenRequest)
  132. let expectedMethod = CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(
  133. documentation: "/// Sends a greeting.\n",
  134. name: CodeGenerationRequest.Name(
  135. base: "SayHello",
  136. generatedUpperCase: "SayHello",
  137. generatedLowerCase: "sayHello"
  138. ),
  139. isInputStreaming: false,
  140. isOutputStreaming: false,
  141. inputType: "Hello_World_HelloRequest",
  142. outputType: "Hello_World_HelloReply"
  143. )
  144. guard let method = parsedCodeGenRequest.services.first?.methods.first else { return XCTFail() }
  145. XCTAssertEqual(method, expectedMethod)
  146. let expectedService = CodeGenerationRequest.ServiceDescriptor(
  147. documentation: "/// The greeting service definition.\n",
  148. name: CodeGenerationRequest.Name(
  149. base: "Greeter",
  150. generatedUpperCase: "Greeter",
  151. generatedLowerCase: "greeter"
  152. ),
  153. namespace: CodeGenerationRequest.Name(
  154. base: "hello.world",
  155. generatedUpperCase: "Hello_World",
  156. generatedLowerCase: "hello_world"
  157. ),
  158. methods: [expectedMethod]
  159. )
  160. guard let service = parsedCodeGenRequest.services.first else { return XCTFail() }
  161. XCTAssertEqual(service, expectedService)
  162. XCTAssertEqual(service.methods.count, 1)
  163. XCTAssertEqual(
  164. parsedCodeGenRequest.lookupSerializer("Hello_World_HelloRequest"),
  165. "ProtobufSerializer<Hello_World_HelloRequest>()"
  166. )
  167. XCTAssertEqual(
  168. parsedCodeGenRequest.lookupDeserializer("Hello_World_HelloRequest"),
  169. "ProtobufDeserializer<Hello_World_HelloRequest>()"
  170. )
  171. }
  172. func testParserEmptyPackage() throws {
  173. let descriptorSet = DescriptorSet(
  174. protos: [
  175. Google_Protobuf_FileDescriptorProto(
  176. name: "same-module.proto",
  177. package: "same-package"
  178. ),
  179. Google_Protobuf_FileDescriptorProto(
  180. name: "different-module.proto",
  181. package: "different-package"
  182. ),
  183. Google_Protobuf_FileDescriptorProto.helloWorldEmptyPackage,
  184. ]
  185. )
  186. guard let fileDescriptor = descriptorSet.fileDescriptor(named: "helloworld.proto") else {
  187. return XCTFail(
  188. """
  189. Could not find the file descriptor of "helloworld.proto".
  190. """
  191. )
  192. }
  193. let moduleMappings = SwiftProtobuf_GenSwift_ModuleMappings.with {
  194. $0.mapping = [
  195. SwiftProtobuf_GenSwift_ModuleMappings.Entry.with {
  196. $0.protoFilePath = ["different-module.proto"]
  197. $0.moduleName = "DifferentModule"
  198. }
  199. ]
  200. }
  201. let parsedCodeGenRequest = try ProtobufCodeGenParser(
  202. input: fileDescriptor,
  203. protoFileModuleMappings: ProtoFileToModuleMappings(moduleMappingsProto: moduleMappings),
  204. extraModuleImports: ["ExtraModule"]
  205. ).parse()
  206. self.testCommonHelloworldParsedRequestFields(for: parsedCodeGenRequest)
  207. let expectedMethod = CodeGenerationRequest.ServiceDescriptor.MethodDescriptor(
  208. documentation: "/// Sends a greeting.\n",
  209. name: CodeGenerationRequest.Name(
  210. base: "SayHello",
  211. generatedUpperCase: "SayHello",
  212. generatedLowerCase: "sayHello"
  213. ),
  214. isInputStreaming: false,
  215. isOutputStreaming: false,
  216. inputType: "HelloRequest",
  217. outputType: "HelloReply"
  218. )
  219. guard let method = parsedCodeGenRequest.services.first?.methods.first else { return XCTFail() }
  220. XCTAssertEqual(method, expectedMethod)
  221. let expectedService = CodeGenerationRequest.ServiceDescriptor(
  222. documentation: "/// The greeting service definition.\n",
  223. name: CodeGenerationRequest.Name(
  224. base: "Greeter",
  225. generatedUpperCase: "Greeter",
  226. generatedLowerCase: "greeter"
  227. ),
  228. namespace: CodeGenerationRequest.Name(
  229. base: "",
  230. generatedUpperCase: "",
  231. generatedLowerCase: ""
  232. ),
  233. methods: [expectedMethod]
  234. )
  235. guard let service = parsedCodeGenRequest.services.first else { return XCTFail() }
  236. XCTAssertEqual(service, expectedService)
  237. XCTAssertEqual(service.methods.count, 1)
  238. XCTAssertEqual(
  239. parsedCodeGenRequest.lookupSerializer("HelloRequest"),
  240. "ProtobufSerializer<HelloRequest>()"
  241. )
  242. XCTAssertEqual(
  243. parsedCodeGenRequest.lookupDeserializer("HelloRequest"),
  244. "ProtobufDeserializer<HelloRequest>()"
  245. )
  246. }
  247. }
  248. extension ProtobufCodeGenParserTests {
  249. func testCommonHelloworldParsedRequestFields(for request: CodeGenerationRequest) {
  250. XCTAssertEqual(request.fileName, "helloworld.proto")
  251. XCTAssertEqual(
  252. request.leadingTrivia,
  253. """
  254. // Copyright 2015 gRPC authors.
  255. //
  256. // Licensed under the Apache License, Version 2.0 (the "License");
  257. // you may not use this file except in compliance with the License.
  258. // You may obtain a copy of the License at
  259. //
  260. // http://www.apache.org/licenses/LICENSE-2.0
  261. //
  262. // Unless required by applicable law or agreed to in writing, software
  263. // distributed under the License is distributed on an "AS IS" BASIS,
  264. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  265. // See the License for the specific language governing permissions and
  266. // limitations under the License.
  267. // DO NOT EDIT.
  268. // swift-format-ignore-file
  269. //
  270. // Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
  271. // Source: helloworld.proto
  272. //
  273. // For information on using the generated types, please see the documentation:
  274. // https://github.com/grpc/grpc-swift
  275. """
  276. )
  277. XCTAssertEqual(request.dependencies.count, 3)
  278. let expectedDependencyNames = ["GRPCProtobuf", "DifferentModule", "ExtraModule"]
  279. let parsedDependencyNames = request.dependencies.map { $0.module }
  280. XCTAssertEqual(parsedDependencyNames, expectedDependencyNames)
  281. XCTAssertEqual(request.services.count, 1)
  282. }
  283. }
  284. extension Google_Protobuf_FileDescriptorProto {
  285. static var helloWorld: Google_Protobuf_FileDescriptorProto {
  286. let requestType = Google_Protobuf_DescriptorProto.with {
  287. $0.name = "HelloRequest"
  288. $0.field = [
  289. Google_Protobuf_FieldDescriptorProto.with {
  290. $0.name = "name"
  291. $0.number = 1
  292. $0.label = .optional
  293. $0.type = .string
  294. $0.jsonName = "name"
  295. }
  296. ]
  297. }
  298. let responseType = Google_Protobuf_DescriptorProto.with {
  299. $0.name = "HelloReply"
  300. $0.field = [
  301. Google_Protobuf_FieldDescriptorProto.with {
  302. $0.name = "message"
  303. $0.number = 1
  304. $0.label = .optional
  305. $0.type = .string
  306. $0.jsonName = "message"
  307. }
  308. ]
  309. }
  310. let service = Google_Protobuf_ServiceDescriptorProto.with {
  311. $0.name = "Greeter"
  312. $0.method = [
  313. Google_Protobuf_MethodDescriptorProto.with {
  314. $0.name = "SayHello"
  315. $0.inputType = ".helloworld.HelloRequest"
  316. $0.outputType = ".helloworld.HelloReply"
  317. $0.clientStreaming = false
  318. $0.serverStreaming = false
  319. }
  320. ]
  321. }
  322. return Google_Protobuf_FileDescriptorProto.with {
  323. $0.name = "helloworld.proto"
  324. $0.package = "helloworld"
  325. $0.dependency = ["same-module.proto", "different-module.proto"]
  326. $0.publicDependency = [0, 1]
  327. $0.messageType = [requestType, responseType]
  328. $0.service = [service]
  329. $0.sourceCodeInfo = Google_Protobuf_SourceCodeInfo.with {
  330. $0.location = [
  331. Google_Protobuf_SourceCodeInfo.Location.with {
  332. $0.path = [12]
  333. $0.span = [14, 0, 18]
  334. $0.leadingDetachedComments = [
  335. """
  336. Copyright 2015 gRPC authors.
  337. Licensed under the Apache License, Version 2.0 (the \"License\");
  338. you may not use this file except in compliance with the License.
  339. You may obtain a copy of the License at
  340. http://www.apache.org/licenses/LICENSE-2.0
  341. Unless required by applicable law or agreed to in writing, software
  342. distributed under the License is distributed on an \"AS IS\" BASIS,
  343. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  344. See the License for the specific language governing permissions and
  345. limitations under the License.
  346. """
  347. ]
  348. },
  349. Google_Protobuf_SourceCodeInfo.Location.with {
  350. $0.path = [6, 0]
  351. $0.span = [19, 0, 22, 1]
  352. $0.leadingComments = " The greeting service definition.\n"
  353. },
  354. Google_Protobuf_SourceCodeInfo.Location.with {
  355. $0.path = [6, 0, 2, 0]
  356. $0.span = [21, 2, 53]
  357. $0.leadingComments = " Sends a greeting.\n"
  358. },
  359. ]
  360. }
  361. $0.syntax = "proto3"
  362. }
  363. }
  364. static var helloWorldNestedPackage: Google_Protobuf_FileDescriptorProto {
  365. let service = Google_Protobuf_ServiceDescriptorProto.with {
  366. $0.name = "Greeter"
  367. $0.method = [
  368. Google_Protobuf_MethodDescriptorProto.with {
  369. $0.name = "SayHello"
  370. $0.inputType = ".hello.world.HelloRequest"
  371. $0.outputType = ".hello.world.HelloReply"
  372. $0.clientStreaming = false
  373. $0.serverStreaming = false
  374. }
  375. ]
  376. }
  377. var helloWorldCopy = self.helloWorld
  378. helloWorldCopy.package = "hello.world"
  379. helloWorldCopy.service = [service]
  380. return helloWorldCopy
  381. }
  382. static var helloWorldEmptyPackage: Google_Protobuf_FileDescriptorProto {
  383. let service = Google_Protobuf_ServiceDescriptorProto.with {
  384. $0.name = "Greeter"
  385. $0.method = [
  386. Google_Protobuf_MethodDescriptorProto.with {
  387. $0.name = "SayHello"
  388. $0.inputType = ".HelloRequest"
  389. $0.outputType = ".HelloReply"
  390. $0.clientStreaming = false
  391. $0.serverStreaming = false
  392. }
  393. ]
  394. }
  395. var helloWorldCopy = self.helloWorld
  396. helloWorldCopy.package = ""
  397. helloWorldCopy.service = [service]
  398. return helloWorldCopy
  399. }
  400. internal init(name: String, package: String) {
  401. self.init()
  402. self.name = name
  403. self.package = package
  404. }
  405. }