TextBasedRendererTests.swift 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  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. //===----------------------------------------------------------------------===//
  17. //
  18. // This source file is part of the SwiftOpenAPIGenerator open source project
  19. //
  20. // Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
  21. // Licensed under Apache License v2.0
  22. //
  23. // See LICENSE.txt for license information
  24. // See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
  25. //
  26. // SPDX-License-Identifier: Apache-2.0
  27. //
  28. //===----------------------------------------------------------------------===//
  29. import XCTest
  30. @testable import GRPCCodeGen
  31. @available(gRPCSwift 2.0, *)
  32. final class Test_TextBasedRenderer: XCTestCase {
  33. func testComment() throws {
  34. try _test(
  35. .inline(
  36. #"""
  37. Generated by foo
  38. Also, bar
  39. """#
  40. ),
  41. renderedBy: { $0.renderComment(_:) },
  42. rendersAs: #"""
  43. // Generated by foo
  44. //
  45. // Also, bar
  46. """#
  47. )
  48. try _test(
  49. .doc(
  50. #"""
  51. Generated by foo
  52. Also, bar
  53. """#
  54. ),
  55. renderedBy: { $0.renderComment(_:) },
  56. rendersAs: #"""
  57. /// Generated by foo
  58. ///
  59. /// Also, bar
  60. """#
  61. )
  62. try _test(
  63. .mark("Lorem ipsum", sectionBreak: false),
  64. renderedBy: { $0.renderComment(_:) },
  65. rendersAs: #"""
  66. // MARK: Lorem ipsum
  67. """#
  68. )
  69. try _test(
  70. .mark("Lorem ipsum", sectionBreak: true),
  71. renderedBy: { $0.renderComment(_:) },
  72. rendersAs: #"""
  73. // MARK: - Lorem ipsum
  74. """#
  75. )
  76. try _test(
  77. .inline(
  78. """
  79. Generated by foo\r\nAlso, bar
  80. """
  81. ),
  82. renderedBy: { $0.renderComment(_:) },
  83. rendersAs: #"""
  84. // Generated by foo
  85. // Also, bar
  86. """#
  87. )
  88. try _test(
  89. .preFormatted("/// Lorem ipsum\n"),
  90. renderedBy: { $0.renderComment(_:) },
  91. rendersAs: """
  92. /// Lorem ipsum
  93. """
  94. )
  95. try _test(
  96. .preFormatted("/// Lorem ipsum\n\n/// Lorem ipsum\n"),
  97. renderedBy: { $0.renderComment(_:) },
  98. rendersAs: """
  99. /// Lorem ipsum
  100. /// Lorem ipsum
  101. """
  102. )
  103. }
  104. func testImports() throws {
  105. try _test(nil, renderedBy: { $0.renderImports(_:) }, rendersAs: "")
  106. try _test(
  107. [
  108. ImportDescription(moduleName: "Foo"),
  109. ImportDescription(moduleName: "Bar"),
  110. ImportDescription(accessLevel: .fileprivate, moduleName: "BazFileprivate"),
  111. ImportDescription(accessLevel: .private, moduleName: "BazPrivate"),
  112. ImportDescription(accessLevel: .internal, moduleName: "BazInternal"),
  113. ImportDescription(accessLevel: .package, moduleName: "BazPackage"),
  114. ImportDescription(accessLevel: .public, moduleName: "BazPublic"),
  115. ],
  116. renderedBy: { $0.renderImports(_:) },
  117. rendersAs: #"""
  118. import Foo
  119. import Bar
  120. fileprivate import BazFileprivate
  121. private import BazPrivate
  122. internal import BazInternal
  123. package import BazPackage
  124. public import BazPublic
  125. """#
  126. )
  127. try _test(
  128. [ImportDescription(moduleName: "Foo", spi: "Secret")],
  129. renderedBy: { $0.renderImports(_:) },
  130. rendersAs: #"""
  131. @_spi(Secret) import Foo
  132. """#
  133. )
  134. try _test(
  135. [
  136. ImportDescription(
  137. moduleName: "Foo",
  138. preconcurrency: .onOS(["Bar", "Baz"])
  139. )
  140. ],
  141. renderedBy: { $0.renderImports(_:) },
  142. rendersAs: #"""
  143. #if os(Bar) || os(Baz)
  144. @preconcurrency import Foo
  145. #else
  146. import Foo
  147. #endif
  148. """#
  149. )
  150. try _test(
  151. [
  152. ImportDescription(moduleName: "Foo", preconcurrency: .always),
  153. ImportDescription(
  154. moduleName: "Bar",
  155. spi: "Secret",
  156. preconcurrency: .always
  157. ),
  158. ],
  159. renderedBy: { $0.renderImports(_:) },
  160. rendersAs: #"""
  161. @preconcurrency import Foo
  162. @preconcurrency @_spi(Secret) import Bar
  163. """#
  164. )
  165. try _test(
  166. [
  167. ImportDescription(
  168. moduleName: "Foo",
  169. item: ImportDescription.Item(kind: .typealias, name: "Bar")
  170. ),
  171. ImportDescription(
  172. moduleName: "Foo",
  173. item: ImportDescription.Item(kind: .struct, name: "Baz")
  174. ),
  175. ImportDescription(
  176. moduleName: "Foo",
  177. item: ImportDescription.Item(kind: .class, name: "Bac")
  178. ),
  179. ImportDescription(
  180. moduleName: "Foo",
  181. item: ImportDescription.Item(kind: .enum, name: "Bap")
  182. ),
  183. ImportDescription(
  184. moduleName: "Foo",
  185. item: ImportDescription.Item(kind: .protocol, name: "Bat")
  186. ),
  187. ImportDescription(
  188. moduleName: "Foo",
  189. item: ImportDescription.Item(kind: .let, name: "Bam")
  190. ),
  191. ImportDescription(
  192. moduleName: "Foo",
  193. item: ImportDescription.Item(kind: .var, name: "Bag")
  194. ),
  195. ImportDescription(
  196. moduleName: "Foo",
  197. item: ImportDescription.Item(kind: .func, name: "Bak")
  198. ),
  199. ImportDescription(
  200. moduleName: "Foo",
  201. spi: "Secret",
  202. item: ImportDescription.Item(kind: .func, name: "SecretBar")
  203. ),
  204. ImportDescription(
  205. moduleName: "Foo",
  206. preconcurrency: .always,
  207. item: ImportDescription.Item(kind: .func, name: "PreconcurrencyBar")
  208. ),
  209. ],
  210. renderedBy: { $0.renderImports(_:) },
  211. rendersAs: #"""
  212. import typealias Foo.Bar
  213. import struct Foo.Baz
  214. import class Foo.Bac
  215. import enum Foo.Bap
  216. import protocol Foo.Bat
  217. import let Foo.Bam
  218. import var Foo.Bag
  219. import func Foo.Bak
  220. @_spi(Secret) import func Foo.SecretBar
  221. @preconcurrency import func Foo.PreconcurrencyBar
  222. """#
  223. )
  224. }
  225. func testAccessModifiers() throws {
  226. try _test(
  227. .public,
  228. renderedBy: { $0.renderedAccessModifier(_:) },
  229. rendersAs: #"""
  230. public
  231. """#
  232. )
  233. try _test(
  234. .internal,
  235. renderedBy: { $0.renderedAccessModifier(_:) },
  236. rendersAs: #"""
  237. internal
  238. """#
  239. )
  240. try _test(
  241. .fileprivate,
  242. renderedBy: { $0.renderedAccessModifier(_:) },
  243. rendersAs: #"""
  244. fileprivate
  245. """#
  246. )
  247. try _test(
  248. .private,
  249. renderedBy: { $0.renderedAccessModifier(_:) },
  250. rendersAs: #"""
  251. private
  252. """#
  253. )
  254. }
  255. func testLiterals() throws {
  256. try _test(
  257. .string("hi"),
  258. renderedBy: { $0.renderLiteral(_:) },
  259. rendersAs: #"""
  260. "hi"
  261. """#
  262. )
  263. try _test(
  264. .string("this string: \"foo\""),
  265. renderedBy: { $0.renderLiteral(_:) },
  266. rendersAs: #"""
  267. #"this string: "foo""#
  268. """#
  269. )
  270. try _test(
  271. .nil,
  272. renderedBy: { $0.renderLiteral(_:) },
  273. rendersAs: #"""
  274. nil
  275. """#
  276. )
  277. try _test(
  278. .array([]),
  279. renderedBy: { $0.renderLiteral(_:) },
  280. rendersAs: #"""
  281. []
  282. """#
  283. )
  284. try _test(
  285. .array([.literal(.nil)]),
  286. renderedBy: { $0.renderLiteral(_:) },
  287. rendersAs: #"""
  288. [
  289. nil
  290. ]
  291. """#
  292. )
  293. try _test(
  294. .array([.literal(.nil), .literal(.nil)]),
  295. renderedBy: { $0.renderLiteral(_:) },
  296. rendersAs: #"""
  297. [
  298. nil,
  299. nil
  300. ]
  301. """#
  302. )
  303. try _test(
  304. .dictionary([]),
  305. renderedBy: { $0.renderLiteral(_:) },
  306. rendersAs: #"""
  307. [:]
  308. """#
  309. )
  310. try _test(
  311. .dictionary([.init(key: .literal("foo"), value: .literal("bar"))]),
  312. renderedBy: { $0.renderLiteral(_:) },
  313. rendersAs: #"""
  314. [
  315. "foo": "bar"
  316. ]
  317. """#
  318. )
  319. try _test(
  320. .dictionary([
  321. .init(key: .literal("foo"), value: .literal("bar")),
  322. .init(key: .literal("bar"), value: .literal("baz")),
  323. ]),
  324. renderedBy: { $0.renderLiteral(_:) },
  325. rendersAs: #"""
  326. [
  327. "foo": "bar",
  328. "bar": "baz"
  329. ]
  330. """#
  331. )
  332. }
  333. func testExpression() throws {
  334. try _test(
  335. .literal(.nil),
  336. renderedBy: { $0.renderExpression(_:) },
  337. rendersAs: #"""
  338. nil
  339. """#
  340. )
  341. try _test(
  342. .identifierPattern("foo"),
  343. renderedBy: { $0.renderExpression(_:) },
  344. rendersAs: #"""
  345. foo
  346. """#
  347. )
  348. try _test(
  349. .memberAccess(.init(left: .identifierPattern("foo"), right: "bar")),
  350. renderedBy: { $0.renderExpression(_:) },
  351. rendersAs: #"""
  352. foo.bar
  353. """#
  354. )
  355. try _test(
  356. .functionCall(
  357. .init(
  358. calledExpression: .identifierPattern("callee"),
  359. arguments: [.init(label: nil, expression: .identifierPattern("foo"))]
  360. )
  361. ),
  362. renderedBy: { $0.renderExpression(_:) },
  363. rendersAs: #"""
  364. callee(foo)
  365. """#
  366. )
  367. }
  368. func testDeclaration() throws {
  369. try _test(
  370. .variable(kind: .let, left: "foo"),
  371. renderedBy: { $0.renderDeclaration(_:) },
  372. rendersAs: #"""
  373. let foo
  374. """#
  375. )
  376. try _test(
  377. .extension(.init(onType: "String", declarations: [])),
  378. renderedBy: { $0.renderDeclaration(_:) },
  379. rendersAs: #"""
  380. extension String {
  381. }
  382. """#
  383. )
  384. try _test(
  385. .struct(.init(name: "Foo")),
  386. renderedBy: { $0.renderDeclaration(_:) },
  387. rendersAs: #"""
  388. struct Foo {}
  389. """#
  390. )
  391. try _test(
  392. .protocol(.init(name: "Foo")),
  393. renderedBy: { $0.renderDeclaration(_:) },
  394. rendersAs: #"""
  395. protocol Foo {}
  396. """#
  397. )
  398. try _test(
  399. .enum(.init(name: "Foo")),
  400. renderedBy: { $0.renderDeclaration(_:) },
  401. rendersAs: #"""
  402. enum Foo {}
  403. """#
  404. )
  405. try _test(
  406. .typealias(.init(name: "foo", existingType: .member(["Foo", "Bar"]))),
  407. renderedBy: { $0.renderDeclaration(_:) },
  408. rendersAs: #"""
  409. typealias foo = Foo.Bar
  410. """#
  411. )
  412. try _test(
  413. .function(FunctionDescription.init(kind: .function(name: "foo"), body: [])),
  414. renderedBy: { $0.renderDeclaration(_:) },
  415. rendersAs: #"""
  416. func foo() {}
  417. """#
  418. )
  419. }
  420. func testFunctionKind() throws {
  421. try _test(
  422. .initializer,
  423. renderedBy: { $0.renderedFunctionKind(_:) },
  424. rendersAs: #"""
  425. init
  426. """#
  427. )
  428. try _test(
  429. .function(name: "funky"),
  430. renderedBy: { $0.renderedFunctionKind(_:) },
  431. rendersAs: #"""
  432. func funky
  433. """#
  434. )
  435. try _test(
  436. .function(name: "funky", isStatic: true),
  437. renderedBy: { $0.renderedFunctionKind(_:) },
  438. rendersAs: #"""
  439. static func funky
  440. """#
  441. )
  442. }
  443. func testFunctionKeyword() throws {
  444. try _test(
  445. .throws,
  446. renderedBy: { $0.renderedFunctionKeyword(_:) },
  447. rendersAs: #"""
  448. throws
  449. """#
  450. )
  451. try _test(
  452. .async,
  453. renderedBy: { $0.renderedFunctionKeyword(_:) },
  454. rendersAs: #"""
  455. async
  456. """#
  457. )
  458. }
  459. func testParameter() throws {
  460. try _test(
  461. .init(label: "l", name: "n", type: .member("T"), defaultValue: .literal(.nil)),
  462. renderedBy: { $0.renderParameter(_:) },
  463. rendersAs: #"""
  464. l n: T = nil
  465. """#
  466. )
  467. try _test(
  468. .init(label: nil, name: "n", type: .member("T"), defaultValue: .literal(.nil)),
  469. renderedBy: { $0.renderParameter(_:) },
  470. rendersAs: #"""
  471. _ n: T = nil
  472. """#
  473. )
  474. try _test(
  475. .init(label: "l", name: nil, type: .member("T"), defaultValue: .literal(.nil)),
  476. renderedBy: { $0.renderParameter(_:) },
  477. rendersAs: #"""
  478. l: T = nil
  479. """#
  480. )
  481. try _test(
  482. .init(label: nil, name: nil, type: .member("T"), defaultValue: .literal(.nil)),
  483. renderedBy: { $0.renderParameter(_:) },
  484. rendersAs: #"""
  485. _: T = nil
  486. """#
  487. )
  488. try _test(
  489. .init(label: nil, name: nil, type: .member("T"), defaultValue: nil),
  490. renderedBy: { $0.renderParameter(_:) },
  491. rendersAs: #"""
  492. _: T
  493. """#
  494. )
  495. }
  496. func testGenericFunction() throws {
  497. try _test(
  498. .init(
  499. accessModifier: .public,
  500. kind: .function(name: "f"),
  501. generics: [.member("R")],
  502. parameters: [],
  503. whereClause: WhereClause(requirements: [.conformance("R", "Sendable")]),
  504. body: []
  505. ),
  506. renderedBy: { $0.renderFunction(_:) },
  507. rendersAs: #"""
  508. public func f<R>() where R: Sendable {}
  509. """#
  510. )
  511. try _test(
  512. .init(
  513. accessModifier: .public,
  514. kind: .function(name: "f"),
  515. generics: [.member("R"), .member("T")],
  516. parameters: [],
  517. whereClause: WhereClause(requirements: [
  518. .conformance("R", "Sendable"), .conformance("T", "Encodable"),
  519. ]),
  520. body: []
  521. ),
  522. renderedBy: { $0.renderFunction(_:) },
  523. rendersAs: #"""
  524. public func f<R, T>() where R: Sendable, T: Encodable {}
  525. """#
  526. )
  527. }
  528. func testFunction() throws {
  529. try _test(
  530. .init(accessModifier: .public, kind: .function(name: "f"), parameters: [], body: []),
  531. renderedBy: { $0.renderFunction(_:) },
  532. rendersAs: #"""
  533. public func f() {}
  534. """#
  535. )
  536. try _test(
  537. .init(
  538. accessModifier: .public,
  539. kind: .function(name: "f"),
  540. parameters: [.init(label: "a", name: "b", type: .member("C"), defaultValue: nil)],
  541. body: []
  542. ),
  543. renderedBy: { $0.renderFunction(_:) },
  544. rendersAs: #"""
  545. public func f(a b: C) {}
  546. """#
  547. )
  548. try _test(
  549. .init(
  550. accessModifier: .public,
  551. kind: .function(name: "f"),
  552. parameters: [
  553. .init(label: "a", name: "b", type: .member("C"), defaultValue: nil),
  554. .init(label: nil, name: "d", type: .member("E"), defaultValue: .literal(.string("f"))),
  555. ],
  556. body: []
  557. ),
  558. renderedBy: { $0.renderFunction(_:) },
  559. rendersAs: #"""
  560. public func f(
  561. a b: C,
  562. _ d: E = "f"
  563. ) {}
  564. """#
  565. )
  566. try _test(
  567. .init(
  568. kind: .function(name: "f"),
  569. parameters: [],
  570. keywords: [.async, .throws],
  571. returnType: .identifierType(TypeName.string)
  572. ),
  573. renderedBy: { $0.renderFunction(_:) },
  574. rendersAs: #"""
  575. func f() async throws -> Swift.String
  576. """#
  577. )
  578. }
  579. func testIdentifiers() throws {
  580. try _test(
  581. .pattern("foo"),
  582. renderedBy: { $0.renderIdentifier(_:) },
  583. rendersAs: #"""
  584. foo
  585. """#
  586. )
  587. }
  588. func testMemberAccess() throws {
  589. try _test(
  590. .init(left: .identifierPattern("foo"), right: "bar"),
  591. renderedBy: { $0.renderMemberAccess(_:) },
  592. rendersAs: #"""
  593. foo.bar
  594. """#
  595. )
  596. try _test(
  597. .init(left: nil, right: "bar"),
  598. renderedBy: { $0.renderMemberAccess(_:) },
  599. rendersAs: #"""
  600. .bar
  601. """#
  602. )
  603. }
  604. func testFunctionCallArgument() throws {
  605. try _test(
  606. .init(label: "foo", expression: .identifierPattern("bar")),
  607. renderedBy: { $0.renderFunctionCallArgument(_:) },
  608. rendersAs: #"""
  609. foo: bar
  610. """#
  611. )
  612. try _test(
  613. .init(label: nil, expression: .identifierPattern("bar")),
  614. renderedBy: { $0.renderFunctionCallArgument(_:) },
  615. rendersAs: #"""
  616. bar
  617. """#
  618. )
  619. }
  620. func testFunctionCall() throws {
  621. try _test(
  622. .functionCall(.init(calledExpression: .identifierPattern("callee"))),
  623. renderedBy: { $0.renderExpression(_:) },
  624. rendersAs: #"""
  625. callee()
  626. """#
  627. )
  628. try _test(
  629. .functionCall(
  630. .init(
  631. calledExpression: .identifierPattern("callee"),
  632. arguments: [.init(label: "foo", expression: .identifierPattern("bar"))]
  633. )
  634. ),
  635. renderedBy: { $0.renderExpression(_:) },
  636. rendersAs: #"""
  637. callee(foo: bar)
  638. """#
  639. )
  640. try _test(
  641. .functionCall(
  642. .init(
  643. calledExpression: .identifierPattern("callee"),
  644. arguments: [
  645. .init(label: "foo", expression: .identifierPattern("bar")),
  646. .init(label: "baz", expression: .identifierPattern("boo")),
  647. ]
  648. )
  649. ),
  650. renderedBy: { $0.renderExpression(_:) },
  651. rendersAs: #"""
  652. callee(
  653. foo: bar,
  654. baz: boo
  655. )
  656. """#
  657. )
  658. }
  659. func testExtension() throws {
  660. try _test(
  661. .init(
  662. accessModifier: .public,
  663. onType: "Info",
  664. declarations: [.variable(kind: .let, left: "foo", type: .member("Int"))]
  665. ),
  666. renderedBy: { $0.renderExtension(_:) },
  667. rendersAs: #"""
  668. public extension Info {
  669. let foo: Int
  670. }
  671. """#
  672. )
  673. }
  674. func testDeprecation() throws {
  675. try _test(
  676. .init(),
  677. renderedBy: { $0.renderDeprecation(_:) },
  678. rendersAs: #"""
  679. @available(*, deprecated)
  680. """#
  681. )
  682. try _test(
  683. .init(message: "some message"),
  684. renderedBy: { $0.renderDeprecation(_:) },
  685. rendersAs: #"""
  686. @available(*, deprecated, message: "some message")
  687. """#
  688. )
  689. try _test(
  690. .init(renamed: "newSymbol(param:)"),
  691. renderedBy: { $0.renderDeprecation(_:) },
  692. rendersAs: #"""
  693. @available(*, deprecated, renamed: "newSymbol(param:)")
  694. """#
  695. )
  696. try _test(
  697. .init(message: "some message", renamed: "newSymbol(param:)"),
  698. renderedBy: { $0.renderDeprecation(_:) },
  699. rendersAs: #"""
  700. @available(*, deprecated, message: "some message", renamed: "newSymbol(param:)")
  701. """#
  702. )
  703. }
  704. func testAvailability() throws {
  705. try _test(
  706. .init(osVersions: [
  707. .init(os: .macOS, version: "12.0"),
  708. .init(os: .iOS, version: "13.1.2"),
  709. .init(os: .watchOS, version: "8.1.2"),
  710. .init(os: .tvOS, version: "15.0.2"),
  711. ]),
  712. renderedBy: { $0.renderAvailability(_:) },
  713. rendersAs: #"""
  714. @available(macOS 12.0, iOS 13.1.2, watchOS 8.1.2, tvOS 15.0.2, *)
  715. """#
  716. )
  717. }
  718. func testBindingKind() throws {
  719. try _test(
  720. .var,
  721. renderedBy: { $0.renderedBindingKind(_:) },
  722. rendersAs: #"""
  723. var
  724. """#
  725. )
  726. try _test(
  727. .let,
  728. renderedBy: { $0.renderedBindingKind(_:) },
  729. rendersAs: #"""
  730. let
  731. """#
  732. )
  733. }
  734. func testVariable() throws {
  735. try _test(
  736. .init(
  737. accessModifier: .public,
  738. isStatic: true,
  739. kind: .let,
  740. left: .identifierPattern("foo"),
  741. type: .init(TypeName.string),
  742. right: .literal(.string("bar"))
  743. ),
  744. renderedBy: { $0.renderVariable(_:) },
  745. rendersAs: #"""
  746. public static let foo: Swift.String = "bar"
  747. """#
  748. )
  749. try _test(
  750. .init(
  751. accessModifier: .internal,
  752. isStatic: false,
  753. kind: .var,
  754. left: .identifierPattern("foo"),
  755. type: nil,
  756. right: nil
  757. ),
  758. renderedBy: { $0.renderVariable(_:) },
  759. rendersAs: #"""
  760. internal var foo
  761. """#
  762. )
  763. try _test(
  764. .init(
  765. kind: .var,
  766. left: .identifierPattern("foo"),
  767. type: .init(TypeName.int),
  768. getter: [CodeBlock.expression(.literal(.int(42)))]
  769. ),
  770. renderedBy: { $0.renderVariable(_:) },
  771. rendersAs: #"""
  772. var foo: Swift.Int {
  773. 42
  774. }
  775. """#
  776. )
  777. try _test(
  778. .init(
  779. kind: .var,
  780. left: .identifierPattern("foo"),
  781. type: .init(TypeName.int),
  782. getter: [CodeBlock.expression(.literal(.int(42)))],
  783. getterEffects: [.throws]
  784. ),
  785. renderedBy: { $0.renderVariable(_:) },
  786. rendersAs: #"""
  787. var foo: Swift.Int {
  788. get throws {
  789. 42
  790. }
  791. }
  792. """#
  793. )
  794. }
  795. func testStruct() throws {
  796. try _test(
  797. StructDescription(name: "Structy"),
  798. renderedBy: { $0.renderStruct(_:) },
  799. rendersAs: #"""
  800. struct Structy {}
  801. """#
  802. )
  803. try _test(
  804. StructDescription(
  805. name: "Structy",
  806. conformances: ["Foo"]
  807. ),
  808. renderedBy: { $0.renderStruct(_:) },
  809. rendersAs: #"""
  810. struct Structy: Foo {}
  811. """#
  812. )
  813. try _test(
  814. StructDescription(
  815. name: "Structy",
  816. generics: [.member("T")]
  817. ),
  818. renderedBy: { $0.renderStruct(_:) },
  819. rendersAs: #"""
  820. struct Structy<T> {}
  821. """#
  822. )
  823. try _test(
  824. StructDescription(
  825. name: "Structy",
  826. generics: [.member("T")],
  827. whereClause: WhereClause(
  828. requirements: [
  829. .conformance("T", "Foo"),
  830. .conformance("T", "Sendable"),
  831. ]
  832. )
  833. ),
  834. renderedBy: {
  835. $0.renderStruct(_:)
  836. },
  837. rendersAs: #"""
  838. struct Structy<T> where T: Foo, T: Sendable {}
  839. """#
  840. )
  841. try _test(
  842. StructDescription(
  843. name: "Structy",
  844. generics: [.member("T")],
  845. conformances: ["Hashable"],
  846. whereClause: WhereClause(requirements: [.conformance("T", "Foo")])
  847. ),
  848. renderedBy: { $0.renderStruct(_:) },
  849. rendersAs: #"""
  850. struct Structy<T>: Hashable where T: Foo {}
  851. """#
  852. )
  853. }
  854. func testProtocol() throws {
  855. try _test(
  856. .init(name: "Protocoly"),
  857. renderedBy: { $0.renderProtocol(_:) },
  858. rendersAs: #"""
  859. protocol Protocoly {}
  860. """#
  861. )
  862. }
  863. func testEnum() throws {
  864. try _test(
  865. .init(name: "Enumy"),
  866. renderedBy: { $0.renderEnum(_:) },
  867. rendersAs: #"""
  868. enum Enumy {}
  869. """#
  870. )
  871. }
  872. func testCodeBlockItem() throws {
  873. try _test(
  874. .declaration(.variable(kind: .let, left: "foo")),
  875. renderedBy: { $0.renderCodeBlockItem(_:) },
  876. rendersAs: #"""
  877. let foo
  878. """#
  879. )
  880. try _test(
  881. .expression(.literal(.nil)),
  882. renderedBy: { $0.renderCodeBlockItem(_:) },
  883. rendersAs: #"""
  884. nil
  885. """#
  886. )
  887. }
  888. func testCodeBlock() throws {
  889. try _test(
  890. .init(
  891. comment: .inline("- MARK: Section"),
  892. item: .declaration(.variable(kind: .let, left: "foo"))
  893. ),
  894. renderedBy: { $0.renderCodeBlock(_:) },
  895. rendersAs: #"""
  896. // - MARK: Section
  897. let foo
  898. """#
  899. )
  900. try _test(
  901. .init(comment: nil, item: .declaration(.variable(kind: .let, left: "foo"))),
  902. renderedBy: { $0.renderCodeBlock(_:) },
  903. rendersAs: #"""
  904. let foo
  905. """#
  906. )
  907. }
  908. func testTypealias() throws {
  909. try _test(
  910. .init(name: "inty", existingType: .member("Int")),
  911. renderedBy: { $0.renderTypealias(_:) },
  912. rendersAs: #"""
  913. typealias inty = Int
  914. """#
  915. )
  916. try _test(
  917. .init(accessModifier: .private, name: "inty", existingType: .member("Int")),
  918. renderedBy: { $0.renderTypealias(_:) },
  919. rendersAs: #"""
  920. private typealias inty = Int
  921. """#
  922. )
  923. }
  924. func testFile() throws {
  925. try _test(
  926. .init(
  927. topComment: .inline("hi"),
  928. imports: [.init(moduleName: "Foo")],
  929. codeBlocks: [.init(comment: nil, item: .declaration(.struct(.init(name: "Bar"))))]
  930. ),
  931. renderedBy: { $0.renderFile(_:) },
  932. rendersAs: #"""
  933. // hi
  934. import Foo
  935. struct Bar {}
  936. """#
  937. )
  938. }
  939. func testIndentation() throws {
  940. try _test(
  941. .init(
  942. topComment: .inline("hi"),
  943. imports: [.init(moduleName: "Foo")],
  944. codeBlocks: [
  945. .init(
  946. comment: nil,
  947. item: .declaration(.struct(.init(name: "Bar", members: [.struct(.init(name: "Baz"))])))
  948. )
  949. ]
  950. ),
  951. renderedBy: { $0.renderFile(_:) },
  952. rendersAs: #"""
  953. // hi
  954. import Foo
  955. struct Bar {
  956. struct Baz {}
  957. }
  958. """#,
  959. indentation: 2
  960. )
  961. try _test(
  962. .array([.literal(.nil), .literal(.nil)]),
  963. renderedBy: { $0.renderLiteral(_:) },
  964. rendersAs: #"""
  965. [
  966. nil,
  967. nil
  968. ]
  969. """#,
  970. indentation: 3
  971. )
  972. try _test(
  973. .init(
  974. kind: .var,
  975. left: .identifierPattern("foo"),
  976. type: .init(TypeName.int),
  977. getter: [CodeBlock.expression(.literal(.int(42)))],
  978. getterEffects: [.throws]
  979. ),
  980. renderedBy: { $0.renderVariable(_:) },
  981. rendersAs: #"""
  982. var foo: Swift.Int {
  983. get throws {
  984. 42
  985. }
  986. }
  987. """#,
  988. indentation: 5
  989. )
  990. }
  991. }
  992. @available(gRPCSwift 2.0, *)
  993. extension Test_TextBasedRenderer {
  994. func _test<Input>(
  995. _ input: Input,
  996. renderedBy renderClosure: (TextBasedRenderer) -> ((Input) -> String),
  997. rendersAs output: String,
  998. file: StaticString = #filePath,
  999. line: UInt = #line,
  1000. indentation: Int = 4
  1001. ) throws {
  1002. let renderer = TextBasedRenderer(indentation: indentation)
  1003. XCTAssertEqual(renderClosure(renderer)(input), output, file: file, line: line)
  1004. }
  1005. func _test<Input>(
  1006. _ input: Input,
  1007. renderedBy renderClosure: (TextBasedRenderer) -> ((Input) -> Void),
  1008. rendersAs output: String,
  1009. file: StaticString = #filePath,
  1010. line: UInt = #line,
  1011. indentation: Int = 4
  1012. ) throws {
  1013. try _test(
  1014. input,
  1015. renderedBy: { renderer in
  1016. let closure = renderClosure(renderer)
  1017. return { input in
  1018. closure(input)
  1019. return renderer.renderedContents()
  1020. }
  1021. },
  1022. rendersAs: output,
  1023. indentation: indentation
  1024. )
  1025. }
  1026. }