IDLToStructuredSwiftTranslatorSnippetBasedTests.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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 XCTest
  18. @testable import GRPCCodeGen
  19. final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
  20. func testImports() throws {
  21. var dependencies = [Dependency]()
  22. dependencies.append(Dependency(module: "Foo", accessLevel: .public))
  23. dependencies.append(
  24. Dependency(
  25. item: .init(kind: .typealias, name: "Bar"),
  26. module: "Foo",
  27. accessLevel: .internal
  28. )
  29. )
  30. dependencies.append(
  31. Dependency(
  32. item: .init(kind: .struct, name: "Baz"),
  33. module: "Foo",
  34. accessLevel: .package
  35. )
  36. )
  37. dependencies.append(
  38. Dependency(
  39. item: .init(kind: .class, name: "Bac"),
  40. module: "Foo",
  41. accessLevel: .package
  42. )
  43. )
  44. dependencies.append(
  45. Dependency(
  46. item: .init(kind: .enum, name: "Bap"),
  47. module: "Foo",
  48. accessLevel: .package
  49. )
  50. )
  51. dependencies.append(
  52. Dependency(
  53. item: .init(kind: .protocol, name: "Bat"),
  54. module: "Foo",
  55. accessLevel: .package
  56. )
  57. )
  58. dependencies.append(
  59. Dependency(
  60. item: .init(kind: .let, name: "Baq"),
  61. module: "Foo",
  62. accessLevel: .package
  63. )
  64. )
  65. dependencies.append(
  66. Dependency(
  67. item: .init(kind: .var, name: "Bag"),
  68. module: "Foo",
  69. accessLevel: .package
  70. )
  71. )
  72. dependencies.append(
  73. Dependency(
  74. item: .init(kind: .func, name: "Bak"),
  75. module: "Foo",
  76. accessLevel: .package
  77. )
  78. )
  79. let expectedSwift =
  80. """
  81. /// Some really exciting license header 2023.
  82. public import GRPCCore
  83. public import Foo
  84. internal import typealias Foo.Bar
  85. package import struct Foo.Baz
  86. package import class Foo.Bac
  87. package import enum Foo.Bap
  88. package import protocol Foo.Bat
  89. package import let Foo.Baq
  90. package import var Foo.Bag
  91. package import func Foo.Bak
  92. """
  93. try self.assertIDLToStructuredSwiftTranslation(
  94. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  95. expectedSwift: expectedSwift,
  96. accessLevel: .public
  97. )
  98. }
  99. func testPreconcurrencyImports() throws {
  100. var dependencies = [Dependency]()
  101. dependencies.append(
  102. Dependency(
  103. module: "Foo",
  104. preconcurrency: .required,
  105. accessLevel: .internal
  106. )
  107. )
  108. dependencies.append(
  109. Dependency(
  110. item: .init(kind: .enum, name: "Bar"),
  111. module: "Foo",
  112. preconcurrency: .required,
  113. accessLevel: .internal
  114. )
  115. )
  116. dependencies.append(
  117. Dependency(
  118. module: "Baz",
  119. preconcurrency: .requiredOnOS(["Deq", "Der"]),
  120. accessLevel: .internal
  121. )
  122. )
  123. let expectedSwift =
  124. """
  125. /// Some really exciting license header 2023.
  126. public import GRPCCore
  127. @preconcurrency internal import Foo
  128. @preconcurrency internal import enum Foo.Bar
  129. #if os(Deq) || os(Der)
  130. @preconcurrency internal import Baz
  131. #else
  132. internal import Baz
  133. #endif
  134. """
  135. try self.assertIDLToStructuredSwiftTranslation(
  136. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  137. expectedSwift: expectedSwift,
  138. accessLevel: .public
  139. )
  140. }
  141. func testSPIImports() throws {
  142. var dependencies = [Dependency]()
  143. dependencies.append(
  144. Dependency(module: "Foo", spi: "Secret", accessLevel: .internal)
  145. )
  146. dependencies.append(
  147. Dependency(
  148. item: .init(kind: .enum, name: "Bar"),
  149. module: "Foo",
  150. spi: "Secret",
  151. accessLevel: .internal
  152. )
  153. )
  154. let expectedSwift =
  155. """
  156. /// Some really exciting license header 2023.
  157. public import GRPCCore
  158. @_spi(Secret) internal import Foo
  159. @_spi(Secret) internal import enum Foo.Bar
  160. """
  161. try self.assertIDLToStructuredSwiftTranslation(
  162. codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies),
  163. expectedSwift: expectedSwift,
  164. accessLevel: .public
  165. )
  166. }
  167. func testGeneration() throws {
  168. var dependencies = [Dependency]()
  169. dependencies.append(
  170. Dependency(module: "Foo", spi: "Secret", accessLevel: .internal)
  171. )
  172. dependencies.append(
  173. Dependency(
  174. item: .init(kind: .enum, name: "Bar"),
  175. module: "Foo",
  176. spi: "Secret",
  177. accessLevel: .internal
  178. )
  179. )
  180. let serviceA = ServiceDescriptor(
  181. documentation: "/// Documentation for AService\n",
  182. name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"),
  183. namespace: Name(
  184. base: "namespaceA",
  185. generatedUpperCase: "NamespaceA",
  186. generatedLowerCase: "namespaceA"
  187. ),
  188. methods: []
  189. )
  190. let expectedSwift =
  191. """
  192. /// Some really exciting license header 2023.
  193. public import GRPCCore
  194. @_spi(Secret) internal import Foo
  195. @_spi(Secret) internal import enum Foo.Bar
  196. // MARK: - namespaceA.ServiceA
  197. /// Namespace containing generated types for the "namespaceA.ServiceA" service.
  198. public enum NamespaceA_ServiceA {
  199. /// Service descriptor for the "namespaceA.ServiceA" service.
  200. public static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA")
  201. /// Namespace for method metadata.
  202. public enum Method {
  203. /// Descriptors for all methods in the "namespaceA.ServiceA" service.
  204. public static let descriptors: [GRPCCore.MethodDescriptor] = []
  205. }
  206. }
  207. extension GRPCCore.ServiceDescriptor {
  208. /// Service descriptor for the "namespaceA.ServiceA" service.
  209. public static let namespaceA_ServiceA = GRPCCore.ServiceDescriptor(fullyQualifiedService: "namespaceA.ServiceA")
  210. }
  211. // MARK: namespaceA.ServiceA (server)
  212. extension NamespaceA_ServiceA {
  213. /// Streaming variant of the service protocol for the "namespaceA.ServiceA" service.
  214. ///
  215. /// This protocol is the lowest-level of the service protocols generated for this service
  216. /// giving you the most flexibility over the implementation of your service. This comes at
  217. /// the cost of more verbose and less strict APIs. Each RPC requires you to implement it in
  218. /// terms of a request stream and response stream. Where only a single request or response
  219. /// message is expected, you are responsible for enforcing this invariant is maintained.
  220. ///
  221. /// Where possible, prefer using the stricter, less-verbose ``ServiceProtocol``
  222. /// or ``SimpleServiceProtocol`` instead.
  223. ///
  224. /// > Source IDL Documentation:
  225. /// >
  226. /// > Documentation for AService
  227. public protocol StreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
  228. /// Service protocol for the "namespaceA.ServiceA" service.
  229. ///
  230. /// This protocol is higher level than ``StreamingServiceProtocol`` but lower level than
  231. /// the ``SimpleServiceProtocol``, it provides access to request and response metadata and
  232. /// trailing response metadata. If you don't need these then consider using
  233. /// the ``SimpleServiceProtocol``. If you need fine grained control over your RPCs then
  234. /// use ``StreamingServiceProtocol``.
  235. ///
  236. /// > Source IDL Documentation:
  237. /// >
  238. /// > Documentation for AService
  239. public protocol ServiceProtocol: NamespaceA_ServiceA.StreamingServiceProtocol {}
  240. /// Simple service protocol for the "namespaceA.ServiceA" service.
  241. ///
  242. /// This is the highest level protocol for the service. The API is the easiest to use but
  243. /// doesn't provide access to request or response metadata. If you need access to these
  244. /// then use ``ServiceProtocol`` instead.
  245. ///
  246. /// > Source IDL Documentation:
  247. /// >
  248. /// > Documentation for AService
  249. public protocol SimpleServiceProtocol: NamespaceA_ServiceA.ServiceProtocol {}
  250. }
  251. // Default implementation of 'registerMethods(with:)'.
  252. extension NamespaceA_ServiceA.StreamingServiceProtocol {
  253. public func registerMethods(with router: inout GRPCCore.RPCRouter) {}
  254. }
  255. // Default implementation of streaming methods from 'StreamingServiceProtocol'.
  256. extension NamespaceA_ServiceA.ServiceProtocol {
  257. }
  258. // Default implementation of methods from 'ServiceProtocol'.
  259. extension NamespaceA_ServiceA.SimpleServiceProtocol {
  260. }
  261. """
  262. try self.assertIDLToStructuredSwiftTranslation(
  263. codeGenerationRequest: makeCodeGenerationRequest(
  264. services: [serviceA],
  265. dependencies: dependencies
  266. ),
  267. expectedSwift: expectedSwift,
  268. accessLevel: .public,
  269. server: true
  270. )
  271. }
  272. private func assertIDLToStructuredSwiftTranslation(
  273. codeGenerationRequest: CodeGenerationRequest,
  274. expectedSwift: String,
  275. accessLevel: SourceGenerator.Config.AccessLevel,
  276. server: Bool = false
  277. ) throws {
  278. let translator = IDLToStructuredSwiftTranslator()
  279. let structuredSwift = try translator.translate(
  280. codeGenerationRequest: codeGenerationRequest,
  281. accessLevel: accessLevel,
  282. accessLevelOnImports: true,
  283. client: false,
  284. server: server
  285. )
  286. let renderer = TextBasedRenderer.default
  287. let sourceFile = try renderer.render(structured: structuredSwift)
  288. let contents = sourceFile.contents
  289. try XCTAssertEqualWithDiff(contents, expectedSwift)
  290. }
  291. func testSameNameServicesNoNamespaceError() throws {
  292. let serviceA = ServiceDescriptor(
  293. documentation: "Documentation for AService",
  294. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  295. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  296. methods: []
  297. )
  298. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceA])
  299. let translator = IDLToStructuredSwiftTranslator()
  300. XCTAssertThrowsError(
  301. ofType: CodeGenError.self,
  302. try translator.translate(
  303. codeGenerationRequest: codeGenerationRequest,
  304. accessLevel: .public,
  305. accessLevelOnImports: true,
  306. client: true,
  307. server: true
  308. )
  309. ) {
  310. error in
  311. XCTAssertEqual(
  312. error as CodeGenError,
  313. CodeGenError(
  314. code: .nonUniqueServiceName,
  315. message: """
  316. Services must have unique descriptors. \
  317. AService is the descriptor of at least two different services.
  318. """
  319. )
  320. )
  321. }
  322. }
  323. func testSameDescriptorsServicesNoNamespaceError() throws {
  324. let serviceA = ServiceDescriptor(
  325. documentation: "Documentation for AService",
  326. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  327. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  328. methods: []
  329. )
  330. let serviceB = ServiceDescriptor(
  331. documentation: "Documentation for BService",
  332. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  333. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  334. methods: []
  335. )
  336. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceB])
  337. let translator = IDLToStructuredSwiftTranslator()
  338. XCTAssertThrowsError(
  339. ofType: CodeGenError.self,
  340. try translator.translate(
  341. codeGenerationRequest: codeGenerationRequest,
  342. accessLevel: .public,
  343. accessLevelOnImports: true,
  344. client: true,
  345. server: true
  346. )
  347. ) {
  348. error in
  349. XCTAssertEqual(
  350. error as CodeGenError,
  351. CodeGenError(
  352. code: .nonUniqueServiceName,
  353. message: """
  354. Services must have unique descriptors. AService is the descriptor of at least two different services.
  355. """
  356. )
  357. )
  358. }
  359. }
  360. func testSameDescriptorsSameNamespaceError() throws {
  361. let serviceA = ServiceDescriptor(
  362. documentation: "Documentation for AService",
  363. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  364. namespace: Name(
  365. base: "namespacea",
  366. generatedUpperCase: "NamespaceA",
  367. generatedLowerCase: "namespacea"
  368. ),
  369. methods: []
  370. )
  371. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceA])
  372. let translator = IDLToStructuredSwiftTranslator()
  373. XCTAssertThrowsError(
  374. ofType: CodeGenError.self,
  375. try translator.translate(
  376. codeGenerationRequest: codeGenerationRequest,
  377. accessLevel: .public,
  378. accessLevelOnImports: true,
  379. client: true,
  380. server: true
  381. )
  382. ) {
  383. error in
  384. XCTAssertEqual(
  385. error as CodeGenError,
  386. CodeGenError(
  387. code: .nonUniqueServiceName,
  388. message: """
  389. Services must have unique descriptors. \
  390. namespacea.AService is the descriptor of at least two different services.
  391. """
  392. )
  393. )
  394. }
  395. }
  396. func testSameGeneratedNameServicesSameNamespaceError() throws {
  397. let serviceA = ServiceDescriptor(
  398. documentation: "/// Documentation for AService\n",
  399. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  400. namespace: Name(
  401. base: "namespacea",
  402. generatedUpperCase: "NamespaceA",
  403. generatedLowerCase: "namespacea"
  404. ),
  405. methods: []
  406. )
  407. let serviceB = ServiceDescriptor(
  408. documentation: "/// Documentation for BService\n",
  409. name: Name(base: "BService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  410. namespace: Name(
  411. base: "namespacea",
  412. generatedUpperCase: "NamespaceA",
  413. generatedLowerCase: "namespacea"
  414. ),
  415. methods: []
  416. )
  417. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceB])
  418. let translator = IDLToStructuredSwiftTranslator()
  419. XCTAssertThrowsError(
  420. ofType: CodeGenError.self,
  421. try translator.translate(
  422. codeGenerationRequest: codeGenerationRequest,
  423. accessLevel: .internal,
  424. accessLevelOnImports: true,
  425. client: true,
  426. server: true
  427. )
  428. ) {
  429. error in
  430. XCTAssertEqual(
  431. error as CodeGenError,
  432. CodeGenError(
  433. code: .nonUniqueServiceName,
  434. message: """
  435. There must be a unique (namespace, service_name) pair for each service. \
  436. NamespaceA_AService is used as a <namespace>_<service_name> construction for multiple services.
  437. """
  438. )
  439. )
  440. }
  441. }
  442. func testSameBaseNameMethodsSameServiceError() throws {
  443. let methodA = MethodDescriptor(
  444. documentation: "Documentation for MethodA",
  445. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  446. isInputStreaming: false,
  447. isOutputStreaming: false,
  448. inputType: "NamespaceA_ServiceARequest",
  449. outputType: "NamespaceA_ServiceAResponse"
  450. )
  451. let service = ServiceDescriptor(
  452. documentation: "Documentation for AService",
  453. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  454. namespace: Name(
  455. base: "namespacea",
  456. generatedUpperCase: "NamespaceA",
  457. generatedLowerCase: "namespacea"
  458. ),
  459. methods: [methodA, methodA]
  460. )
  461. let codeGenerationRequest = makeCodeGenerationRequest(services: [service])
  462. let translator = IDLToStructuredSwiftTranslator()
  463. XCTAssertThrowsError(
  464. ofType: CodeGenError.self,
  465. try translator.translate(
  466. codeGenerationRequest: codeGenerationRequest,
  467. accessLevel: .public,
  468. accessLevelOnImports: true,
  469. client: true,
  470. server: true
  471. )
  472. ) {
  473. error in
  474. XCTAssertEqual(
  475. error as CodeGenError,
  476. CodeGenError(
  477. code: .nonUniqueMethodName,
  478. message: """
  479. Methods of a service must have unique base names. \
  480. MethodA is used as a base name for multiple methods of the AService service.
  481. """
  482. )
  483. )
  484. }
  485. }
  486. func testSameGeneratedUpperCaseNameMethodsSameServiceError() throws {
  487. let methodA = MethodDescriptor(
  488. documentation: "Documentation for MethodA",
  489. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  490. isInputStreaming: false,
  491. isOutputStreaming: false,
  492. inputType: "NamespaceA_ServiceARequest",
  493. outputType: "NamespaceA_ServiceAResponse"
  494. )
  495. let methodB = MethodDescriptor(
  496. documentation: "Documentation for MethodA",
  497. name: Name(base: "MethodB", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  498. isInputStreaming: false,
  499. isOutputStreaming: false,
  500. inputType: "NamespaceA_ServiceARequest",
  501. outputType: "NamespaceA_ServiceAResponse"
  502. )
  503. let service = ServiceDescriptor(
  504. documentation: "Documentation for AService",
  505. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  506. namespace: Name(
  507. base: "namespacea",
  508. generatedUpperCase: "NamespaceA",
  509. generatedLowerCase: "namespacea"
  510. ),
  511. methods: [methodA, methodB]
  512. )
  513. let codeGenerationRequest = makeCodeGenerationRequest(services: [service])
  514. let translator = IDLToStructuredSwiftTranslator()
  515. XCTAssertThrowsError(
  516. ofType: CodeGenError.self,
  517. try translator.translate(
  518. codeGenerationRequest: codeGenerationRequest,
  519. accessLevel: .public,
  520. accessLevelOnImports: true,
  521. client: true,
  522. server: true
  523. )
  524. ) {
  525. error in
  526. XCTAssertEqual(
  527. error as CodeGenError,
  528. CodeGenError(
  529. code: .nonUniqueMethodName,
  530. message: """
  531. Methods of a service must have unique generated upper case names. \
  532. MethodA is used as a generated upper case name for multiple methods of the AService service.
  533. """
  534. )
  535. )
  536. }
  537. }
  538. func testSameLowerCaseNameMethodsSameServiceError() throws {
  539. let methodA = MethodDescriptor(
  540. documentation: "Documentation for MethodA",
  541. name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
  542. isInputStreaming: false,
  543. isOutputStreaming: false,
  544. inputType: "NamespaceA_ServiceARequest",
  545. outputType: "NamespaceA_ServiceAResponse"
  546. )
  547. let methodB = MethodDescriptor(
  548. documentation: "Documentation for MethodA",
  549. name: Name(base: "MethodB", generatedUpperCase: "MethodB", generatedLowerCase: "methodA"),
  550. isInputStreaming: false,
  551. isOutputStreaming: false,
  552. inputType: "NamespaceA_ServiceARequest",
  553. outputType: "NamespaceA_ServiceAResponse"
  554. )
  555. let service = ServiceDescriptor(
  556. documentation: "Documentation for AService",
  557. name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"),
  558. namespace: Name(
  559. base: "namespacea",
  560. generatedUpperCase: "NamespaceA",
  561. generatedLowerCase: "namespacea"
  562. ),
  563. methods: [methodA, methodB]
  564. )
  565. let codeGenerationRequest = makeCodeGenerationRequest(services: [service])
  566. let translator = IDLToStructuredSwiftTranslator()
  567. XCTAssertThrowsError(
  568. ofType: CodeGenError.self,
  569. try translator.translate(
  570. codeGenerationRequest: codeGenerationRequest,
  571. accessLevel: .public,
  572. accessLevelOnImports: true,
  573. client: true,
  574. server: true
  575. )
  576. ) {
  577. error in
  578. XCTAssertEqual(
  579. error as CodeGenError,
  580. CodeGenError(
  581. code: .nonUniqueMethodName,
  582. message: """
  583. Methods of a service must have unique lower case names. \
  584. methodA is used as a signature name for multiple methods of the AService service.
  585. """
  586. )
  587. )
  588. }
  589. }
  590. func testSameGeneratedNameNoNamespaceServiceAndNamespaceError() throws {
  591. let serviceA = ServiceDescriptor(
  592. documentation: "Documentation for SameName service with no namespace",
  593. name: Name(
  594. base: "SameName",
  595. generatedUpperCase: "SameName_BService",
  596. generatedLowerCase: "sameName"
  597. ),
  598. namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
  599. methods: []
  600. )
  601. let serviceB = ServiceDescriptor(
  602. documentation: "Documentation for BService",
  603. name: Name(base: "BService", generatedUpperCase: "BService", generatedLowerCase: "bService"),
  604. namespace: Name(
  605. base: "sameName",
  606. generatedUpperCase: "SameName",
  607. generatedLowerCase: "sameName"
  608. ),
  609. methods: []
  610. )
  611. let codeGenerationRequest = makeCodeGenerationRequest(services: [serviceA, serviceB])
  612. let translator = IDLToStructuredSwiftTranslator()
  613. XCTAssertThrowsError(
  614. ofType: CodeGenError.self,
  615. try translator.translate(
  616. codeGenerationRequest: codeGenerationRequest,
  617. accessLevel: .public,
  618. accessLevelOnImports: true,
  619. client: true,
  620. server: true
  621. )
  622. ) {
  623. error in
  624. XCTAssertEqual(
  625. error as CodeGenError,
  626. CodeGenError(
  627. code: .nonUniqueServiceName,
  628. message: """
  629. There must be a unique (namespace, service_name) pair for each service. \
  630. SameName_BService is used as a <namespace>_<service_name> construction for multiple services.
  631. """
  632. )
  633. )
  634. }
  635. }
  636. }
  637. #endif // os(macOS) || os(Linux)