IDLToStructuredSwiftTranslatorSnippetBasedTests.swift 21 KB

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