TextBasedRendererTests.swift 23 KB

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