ReflectionServiceUnitTests.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*
  2. * Copyright 2023, 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 Foundation
  17. import GRPC
  18. import GRPCReflectionService
  19. import SwiftProtobuf
  20. import XCTest
  21. @testable import GRPCReflectionService
  22. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  23. final class ReflectionServiceUnitTests: GRPCTestCase {
  24. /// Testing the fileDescriptorDataByFilename dictionary of the ReflectionServiceData object.
  25. func testFileDescriptorDataByFilename() throws {
  26. var protos = makeProtosWithDependencies()
  27. let registry = try ReflectionServiceData(fileDescriptors: protos)
  28. let registryFileDescriptorData = registry.fileDescriptorDataByFilename
  29. for (fileName, protoData) in registryFileDescriptorData {
  30. let serializedFiledescriptorData = protoData.serializedFileDescriptorProto
  31. let dependencyFileNames = protoData.dependencyFileNames
  32. guard let index = protos.firstIndex(where: { $0.name == fileName }) else {
  33. return XCTFail(
  34. """
  35. Could not find the file descriptor proto of \(fileName) \
  36. in the original file descriptor protos list.
  37. """
  38. )
  39. }
  40. let originalProto = protos[index]
  41. XCTAssertEqual(originalProto.name, fileName)
  42. XCTAssertEqual(try originalProto.serializedData(), serializedFiledescriptorData)
  43. XCTAssertEqual(originalProto.dependency, dependencyFileNames)
  44. protos.remove(at: index)
  45. }
  46. XCTAssert(protos.isEmpty)
  47. }
  48. /// Testing the serviceNames array of the ReflectionServiceData object.
  49. func testServiceNames() throws {
  50. let protos = makeProtosWithDependencies()
  51. let servicesNames = protos.flatMap { $0.qualifiedServiceNames }.sorted()
  52. let registry = try ReflectionServiceData(fileDescriptors: protos)
  53. let registryServices = registry.serviceNames.sorted()
  54. XCTAssertEqual(registryServices, servicesNames)
  55. }
  56. /// Testing the fileNameBySymbol dictionary of the ReflectionServiceData object.
  57. func testFileNameBySymbol() throws {
  58. let protos = makeProtosWithDependencies()
  59. let registry = try ReflectionServiceData(fileDescriptors: protos)
  60. let registryFileNameBySymbol = registry.fileNameBySymbol
  61. var symbolsCount = 0
  62. for proto in protos {
  63. let qualifiedSymbolNames = proto.qualifiedSymbolNames
  64. symbolsCount += qualifiedSymbolNames.count
  65. for qualifiedSymbolName in qualifiedSymbolNames {
  66. XCTAssertEqual(registryFileNameBySymbol[qualifiedSymbolName], proto.name)
  67. }
  68. }
  69. XCTAssertEqual(symbolsCount, registryFileNameBySymbol.count)
  70. }
  71. func testFileNameBySymbolDuplicatedSymbol() throws {
  72. var protos = makeProtosWithDependencies()
  73. protos[1].messageType.append(
  74. Google_Protobuf_DescriptorProto.with {
  75. $0.name = "inputMessage2"
  76. $0.field = [
  77. Google_Protobuf_FieldDescriptorProto.with {
  78. $0.name = "inputField"
  79. $0.type = .bool
  80. }
  81. ]
  82. }
  83. )
  84. XCTAssertThrowsError(
  85. try ReflectionServiceData(fileDescriptors: protos)
  86. ) { error in
  87. XCTAssertEqual(
  88. error as? GRPCStatus,
  89. GRPCStatus(
  90. code: .alreadyExists,
  91. message:
  92. """
  93. The packagebar2.inputMessage2 symbol from bar2.proto \
  94. already exists in bar2.proto.
  95. """
  96. )
  97. )
  98. }
  99. }
  100. // Testing the nameOfFileContainingSymbol method for different types of symbols.
  101. func testNameOfFileContainingSymbolEnum() throws {
  102. let protos = makeProtosWithDependencies()
  103. let registry = try ReflectionServiceData(fileDescriptors: protos)
  104. let nameOfFileContainingSymbolResult = registry.nameOfFileContainingSymbol(
  105. named: "packagebar2.enumType2"
  106. )
  107. XCTAssertEqual(try nameOfFileContainingSymbolResult.get(), "bar2.proto")
  108. }
  109. func testNameOfFileContainingSymbolMessage() throws {
  110. let protos = makeProtosWithDependencies()
  111. let registry = try ReflectionServiceData(fileDescriptors: protos)
  112. let nameOfFileContainingSymbolResult = registry.nameOfFileContainingSymbol(
  113. named: "packagebar1.inputMessage1"
  114. )
  115. XCTAssertEqual(try nameOfFileContainingSymbolResult.get(), "bar1.proto")
  116. }
  117. func testNameOfFileContainingSymbolService() throws {
  118. let protos = makeProtosWithDependencies()
  119. let registry = try ReflectionServiceData(fileDescriptors: protos)
  120. let nameOfFileContainingSymbolResult = registry.nameOfFileContainingSymbol(
  121. named: "packagebar3.service3"
  122. )
  123. XCTAssertEqual(try nameOfFileContainingSymbolResult.get(), "bar3.proto")
  124. }
  125. func testNameOfFileContainingSymbolMethod() throws {
  126. let protos = makeProtosWithDependencies()
  127. let registry = try ReflectionServiceData(fileDescriptors: protos)
  128. let nameOfFileContainingSymbolResult = registry.nameOfFileContainingSymbol(
  129. named: "packagebar4.service4.testMethod4"
  130. )
  131. XCTAssertEqual(try nameOfFileContainingSymbolResult.get(), "bar4.proto")
  132. }
  133. func testNameOfFileContainingSymbolNonExistentSymbol() throws {
  134. let protos = makeProtosWithDependencies()
  135. let registry = try ReflectionServiceData(fileDescriptors: protos)
  136. let nameOfFileContainingSymbolResult = registry.nameOfFileContainingSymbol(
  137. named: "packagebar2.enumType3"
  138. )
  139. XCTAssertThrowsGRPCStatus(try nameOfFileContainingSymbolResult.get()) {
  140. status in
  141. XCTAssertEqual(
  142. status,
  143. GRPCStatus(code: .notFound, message: "The provided symbol could not be found.")
  144. )
  145. }
  146. }
  147. // Testing the serializedFileDescriptorProto method in different cases.
  148. func testSerialisedFileDescriptorProtosForDependenciesOfFile() throws {
  149. var protos = makeProtosWithDependencies()
  150. let registry = try ReflectionServiceData(fileDescriptors: protos)
  151. let serializedFileDescriptorProtosResult =
  152. registry
  153. .serialisedFileDescriptorProtosForDependenciesOfFile(named: "bar1.proto")
  154. switch serializedFileDescriptorProtosResult {
  155. case .success(let serializedFileDescriptorProtos):
  156. let fileDescriptorProtos = try serializedFileDescriptorProtos.map {
  157. try Google_Protobuf_FileDescriptorProto(serializedData: $0)
  158. }
  159. // Tests that the functions returns all the transitive dependencies, with their services and
  160. // methods, together with the initial proto, as serialized data.
  161. XCTAssertEqual(fileDescriptorProtos.count, 4)
  162. for fileDescriptorProto in fileDescriptorProtos {
  163. guard let protoIndex = protos.firstIndex(of: fileDescriptorProto) else {
  164. return XCTFail(
  165. """
  166. Could not find the file descriptor proto of \(fileDescriptorProto.name) \
  167. in the original file descriptor protos list.
  168. """
  169. )
  170. }
  171. for service in fileDescriptorProto.service {
  172. guard let serviceIndex = protos[protoIndex].service.firstIndex(of: service) else {
  173. return XCTFail(
  174. """
  175. Could not find the \(service.name) in the service \
  176. list of the \(fileDescriptorProto.name) file descriptor proto.
  177. """
  178. )
  179. }
  180. let originalMethods = protos[protoIndex].service[serviceIndex].method
  181. for method in service.method {
  182. XCTAssert(originalMethods.contains(method))
  183. }
  184. for messageType in fileDescriptorProto.messageType {
  185. XCTAssert(protos[protoIndex].messageType.contains(messageType))
  186. }
  187. }
  188. protos.removeAll { $0 == fileDescriptorProto }
  189. }
  190. XCTAssert(protos.isEmpty)
  191. case .failure(let status):
  192. XCTFail(
  193. "Faild with GRPCStatus code: " + String(status.code.rawValue) + " and message: "
  194. + (status.message ?? "empty") + "."
  195. )
  196. }
  197. }
  198. func testSerialisedFileDescriptorProtosForDependenciesOfFileComplexDependencyGraph() throws {
  199. var protos = makeProtosWithComplexDependencies()
  200. let registry = try ReflectionServiceData(fileDescriptors: protos)
  201. let serializedFileDescriptorProtosResult =
  202. registry
  203. .serialisedFileDescriptorProtosForDependenciesOfFile(named: "foo0.proto")
  204. switch serializedFileDescriptorProtosResult {
  205. case .success(let serializedFileDescriptorProtos):
  206. let fileDescriptorProtos = try serializedFileDescriptorProtos.map {
  207. try Google_Protobuf_FileDescriptorProto(serializedData: $0)
  208. }
  209. // Tests that the functions returns all the tranzitive dependencies, with their services and
  210. // methods, together with the initial proto, as serialized data.
  211. XCTAssertEqual(fileDescriptorProtos.count, 21)
  212. for fileDescriptorProto in fileDescriptorProtos {
  213. guard let protoIndex = protos.firstIndex(of: fileDescriptorProto) else {
  214. return XCTFail(
  215. """
  216. Could not find the file descriptor proto of \(fileDescriptorProto.name) \
  217. in the original file descriptor protos list.
  218. """
  219. )
  220. }
  221. for service in fileDescriptorProto.service {
  222. guard let serviceIndex = protos[protoIndex].service.firstIndex(of: service) else {
  223. return XCTFail(
  224. """
  225. Could not find the \(service.name) in the service \
  226. list of the \(fileDescriptorProto.name) file descriptor proto.
  227. """
  228. )
  229. }
  230. let originalMethods = protos[protoIndex].service[serviceIndex].method
  231. for method in service.method {
  232. XCTAssert(originalMethods.contains(method))
  233. }
  234. for messageType in fileDescriptorProto.messageType {
  235. XCTAssert(protos[protoIndex].messageType.contains(messageType))
  236. }
  237. }
  238. protos.removeAll { $0 == fileDescriptorProto }
  239. }
  240. XCTAssert(protos.isEmpty)
  241. case .failure(let status):
  242. XCTFail(
  243. "Faild with GRPCStatus code: " + String(status.code.rawValue) + " and message: "
  244. + (status.message ?? "empty") + "."
  245. )
  246. }
  247. }
  248. func testSerialisedFileDescriptorProtosForDependenciesOfFileDependencyLoops() throws {
  249. var protos = makeProtosWithDependencies()
  250. // Making dependencies of the "bar1.proto" to depend on "bar1.proto".
  251. protos[1].dependency.append("bar1.proto")
  252. protos[2].dependency.append("bar1.proto")
  253. protos[3].dependency.append("bar1.proto")
  254. let registry = try ReflectionServiceData(fileDescriptors: protos)
  255. let serializedFileDescriptorProtosResult =
  256. registry
  257. .serialisedFileDescriptorProtosForDependenciesOfFile(named: "bar1.proto")
  258. switch serializedFileDescriptorProtosResult {
  259. case .success(let serializedFileDescriptorProtos):
  260. let fileDescriptorProtos = try serializedFileDescriptorProtos.map {
  261. try Google_Protobuf_FileDescriptorProto(serializedData: $0)
  262. }
  263. // Test that we get only 4 serialized File Descriptor Protos as response.
  264. XCTAssertEqual(fileDescriptorProtos.count, 4)
  265. for fileDescriptorProto in fileDescriptorProtos {
  266. guard let protoIndex = protos.firstIndex(of: fileDescriptorProto) else {
  267. return XCTFail(
  268. """
  269. Could not find the file descriptor proto of \(fileDescriptorProto.name) \
  270. in the original file descriptor protos list.
  271. """
  272. )
  273. }
  274. for service in fileDescriptorProto.service {
  275. guard let serviceIndex = protos[protoIndex].service.firstIndex(of: service) else {
  276. return XCTFail(
  277. """
  278. Could not find the \(service.name) in the service \
  279. list of the \(fileDescriptorProto.name) file descriptor proto.
  280. """
  281. )
  282. }
  283. let originalMethods = protos[protoIndex].service[serviceIndex].method
  284. for method in service.method {
  285. XCTAssert(originalMethods.contains(method))
  286. }
  287. for messageType in fileDescriptorProto.messageType {
  288. XCTAssert(protos[protoIndex].messageType.contains(messageType))
  289. }
  290. }
  291. protos.removeAll { $0 == fileDescriptorProto }
  292. }
  293. XCTAssert(protos.isEmpty)
  294. case .failure(let status):
  295. XCTFail(
  296. "Faild with GRPCStatus code: " + String(status.code.rawValue) + " and message: "
  297. + (status.message ?? "empty") + "."
  298. )
  299. }
  300. }
  301. func testSerialisedFileDescriptorProtosForDependenciesOfFileInvalidFile() throws {
  302. let protos = makeProtosWithDependencies()
  303. let registry = try ReflectionServiceData(fileDescriptors: protos)
  304. let serializedFileDescriptorProtosForDependenciesOfFileResult =
  305. registry.serialisedFileDescriptorProtosForDependenciesOfFile(named: "invalid.proto")
  306. XCTAssertThrowsGRPCStatus(try serializedFileDescriptorProtosForDependenciesOfFileResult.get()) {
  307. status in
  308. XCTAssertEqual(
  309. status,
  310. GRPCStatus(
  311. code: .notFound,
  312. message: "The provided file or a dependency of the provided file could not be found."
  313. )
  314. )
  315. }
  316. }
  317. func testSerialisedFileDescriptorProtosForDependenciesOfFileDependencyNotProto() throws {
  318. var protos = makeProtosWithDependencies()
  319. protos[0].dependency.append("invalidDependency")
  320. let registry = try ReflectionServiceData(fileDescriptors: protos)
  321. let serializedFileDescriptorProtosForDependenciesOfFileResult =
  322. registry.serialisedFileDescriptorProtosForDependenciesOfFile(named: "bar1.proto")
  323. XCTAssertThrowsGRPCStatus(try serializedFileDescriptorProtosForDependenciesOfFileResult.get()) {
  324. status in
  325. XCTAssertEqual(
  326. status,
  327. GRPCStatus(
  328. code: .notFound,
  329. message: "The provided file or a dependency of the provided file could not be found."
  330. )
  331. )
  332. }
  333. }
  334. // Testing the nameOfFileContainingExtension() method.
  335. func testNameOfFileContainingExtensions() throws {
  336. let protos = makeProtosWithDependencies()
  337. let registry = try ReflectionServiceData(fileDescriptors: protos)
  338. for proto in protos {
  339. for `extension` in proto.extension {
  340. let typeName = String(`extension`.extendee.drop(while: { $0 == "." }))
  341. let registryFileNameResult = registry.nameOfFileContainingExtension(
  342. extendeeName: typeName,
  343. fieldNumber: `extension`.number
  344. )
  345. XCTAssertEqual(try registryFileNameResult.get(), proto.name)
  346. }
  347. }
  348. }
  349. func testNameOfFileContainingExtensionsInvalidTypeName() throws {
  350. let protos = makeProtosWithDependencies()
  351. let registry = try ReflectionServiceData(fileDescriptors: protos)
  352. let registryFileNameResult = registry.nameOfFileContainingExtension(
  353. extendeeName: "InvalidType",
  354. fieldNumber: 2
  355. )
  356. XCTAssertThrowsGRPCStatus(try registryFileNameResult.get()) {
  357. status in
  358. XCTAssertEqual(
  359. status,
  360. GRPCStatus(code: .notFound, message: "The provided extension could not be found.")
  361. )
  362. }
  363. }
  364. func testNameOfFileContainingExtensionsInvalidFieldNumber() throws {
  365. let protos = makeProtosWithDependencies()
  366. let registry = try ReflectionServiceData(fileDescriptors: protos)
  367. let registryFileNameResult = registry.nameOfFileContainingExtension(
  368. extendeeName: protos[0].extension[0].extendee,
  369. fieldNumber: 9
  370. )
  371. XCTAssertThrowsGRPCStatus(try registryFileNameResult.get()) {
  372. status in
  373. XCTAssertEqual(
  374. status,
  375. GRPCStatus(code: .notFound, message: "The provided extension could not be found.")
  376. )
  377. }
  378. }
  379. func testNameOfFileContainingExtensionsDuplicatedExtensions() throws {
  380. var protos = makeProtosWithDependencies()
  381. protos[0].extension.append(
  382. .with {
  383. $0.extendee = ".packagebar1.inputMessage1"
  384. $0.number = 2
  385. }
  386. )
  387. XCTAssertThrowsError(
  388. try ReflectionServiceData(fileDescriptors: protos)
  389. ) { error in
  390. XCTAssertEqual(
  391. error as? GRPCStatus,
  392. GRPCStatus(
  393. code: .alreadyExists,
  394. message:
  395. """
  396. The extension of the packagebar1.inputMessage1 type with the field number equal to \
  397. 2 from \(protos[0].name) already exists in \(protos[0].name).
  398. """
  399. )
  400. )
  401. }
  402. }
  403. // Testing the extensionsFieldNumbersOfType() method.
  404. func testExtensionsFieldNumbersOfType() throws {
  405. var protos = makeProtosWithDependencies()
  406. protos[0].extension.append(
  407. .with {
  408. $0.extendee = ".packagebar1.inputMessage1"
  409. $0.number = 120
  410. }
  411. )
  412. let registry = try ReflectionServiceData(fileDescriptors: protos)
  413. let extensionsFieldNumbersOfTypeResult = registry.extensionsFieldNumbersOfType(
  414. named: "packagebar1.inputMessage1"
  415. )
  416. XCTAssertEqual(try extensionsFieldNumbersOfTypeResult.get(), [1, 2, 3, 4, 5, 120])
  417. }
  418. func testExtensionsFieldNumbersOfTypeNoExtensionsType() throws {
  419. var protos = makeProtosWithDependencies()
  420. protos[0].messageType.append(
  421. Google_Protobuf_DescriptorProto.with {
  422. $0.name = "noExtensionMessage"
  423. $0.field = [
  424. Google_Protobuf_FieldDescriptorProto.with {
  425. $0.name = "noExtensionField"
  426. $0.type = .bool
  427. }
  428. ]
  429. }
  430. )
  431. let registry = try ReflectionServiceData(fileDescriptors: protos)
  432. let extensionsFieldNumbersOfTypeResult = registry.extensionsFieldNumbersOfType(
  433. named: "packagebar1.noExtensionMessage"
  434. )
  435. XCTAssertEqual(try extensionsFieldNumbersOfTypeResult.get(), [])
  436. }
  437. func testExtensionsFieldNumbersOfTypeInvalidTypeName() throws {
  438. let protos = makeProtosWithDependencies()
  439. let registry = try ReflectionServiceData(fileDescriptors: protos)
  440. let extensionsFieldNumbersOfTypeResult = registry.extensionsFieldNumbersOfType(
  441. named: "packagebar1.invalidTypeMessage"
  442. )
  443. XCTAssertThrowsGRPCStatus(try extensionsFieldNumbersOfTypeResult.get()) {
  444. status in
  445. XCTAssertEqual(
  446. status,
  447. GRPCStatus(code: .invalidArgument, message: "The provided type is invalid.")
  448. )
  449. }
  450. }
  451. func testExtensionsFieldNumbersOfTypeExtensionsInDifferentProtoFiles() throws {
  452. var protos = makeProtosWithDependencies()
  453. protos[2].extension.append(
  454. .with {
  455. $0.extendee = ".packagebar1.inputMessage1"
  456. $0.number = 130
  457. }
  458. )
  459. let registry = try ReflectionServiceData(fileDescriptors: protos)
  460. let extensionsFieldNumbersOfTypeResult = registry.extensionsFieldNumbersOfType(
  461. named: "packagebar1.inputMessage1"
  462. )
  463. XCTAssertEqual(try extensionsFieldNumbersOfTypeResult.get(), [1, 2, 3, 4, 5, 130])
  464. }
  465. func testReadSerializedFileDescriptorProto() throws {
  466. let initialFileDescriptorProto = generateFileDescriptorProto(fileName: "test", suffix: "1")
  467. let data = try initialFileDescriptorProto.serializedData().base64EncodedData()
  468. let temporaryDirectory: String
  469. #if os(Linux)
  470. temporaryDirectory = "/tmp/"
  471. #else
  472. if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
  473. temporaryDirectory = FileManager.default.temporaryDirectory.path()
  474. } else {
  475. temporaryDirectory = "/tmp/"
  476. }
  477. #endif
  478. let filePath = "\(temporaryDirectory)test\(UUID()).grpc.reflection"
  479. FileManager.default.createFile(atPath: filePath, contents: data)
  480. defer {
  481. XCTAssertNoThrow(try FileManager.default.removeItem(atPath: filePath))
  482. }
  483. let reflectionServiceFileDescriptorProto =
  484. try ReflectionService.readSerializedFileDescriptorProto(atPath: filePath)
  485. XCTAssertEqual(reflectionServiceFileDescriptorProto, initialFileDescriptorProto)
  486. }
  487. func testReadSerializedFileDescriptorProtoInvalidFileContents() throws {
  488. let invalidData = "%%%%%££££".data(using: .utf8)
  489. let temporaryDirectory: String
  490. #if os(Linux)
  491. temporaryDirectory = "/tmp/"
  492. #else
  493. if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
  494. temporaryDirectory = FileManager.default.temporaryDirectory.path()
  495. } else {
  496. temporaryDirectory = "/tmp/"
  497. }
  498. #endif
  499. let filePath = "\(temporaryDirectory)test\(UUID()).grpc.reflection"
  500. FileManager.default.createFile(atPath: filePath, contents: invalidData)
  501. defer {
  502. XCTAssertNoThrow(try FileManager.default.removeItem(atPath: filePath))
  503. }
  504. XCTAssertThrowsGRPCStatus(
  505. try ReflectionService.readSerializedFileDescriptorProto(atPath: filePath)
  506. ) {
  507. status in
  508. XCTAssertEqual(
  509. status,
  510. GRPCStatus(
  511. code: .invalidArgument,
  512. message:
  513. """
  514. The \(filePath) file contents could not be transformed \
  515. into serialized data representing a file descriptor proto.
  516. """
  517. )
  518. )
  519. }
  520. }
  521. func testReadSerializedFileDescriptorProtos() throws {
  522. let initialFileDescriptorProtos = makeProtosWithDependencies()
  523. var filePaths: [String] = []
  524. for initialFileDescriptorProto in initialFileDescriptorProtos {
  525. let data = try initialFileDescriptorProto.serializedData()
  526. .base64EncodedData()
  527. let temporaryDirectory: String
  528. #if os(Linux)
  529. temporaryDirectory = "/tmp/"
  530. #else
  531. if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
  532. temporaryDirectory = FileManager.default.temporaryDirectory.path()
  533. } else {
  534. temporaryDirectory = "/tmp/"
  535. }
  536. #endif
  537. let filePath = "\(temporaryDirectory)test\(UUID()).grpc.reflection"
  538. FileManager.default.createFile(atPath: filePath, contents: data)
  539. filePaths.append(filePath)
  540. }
  541. defer {
  542. for filePath in filePaths {
  543. XCTAssertNoThrow(try FileManager.default.removeItem(atPath: filePath))
  544. }
  545. }
  546. let reflectionServiceFileDescriptorProtos =
  547. try ReflectionService.readSerializedFileDescriptorProtos(atPaths: filePaths)
  548. XCTAssertEqual(reflectionServiceFileDescriptorProtos, initialFileDescriptorProtos)
  549. }
  550. }