ProtobufCodeGenParserTests.swift 15 KB

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