TextBasedRendererTests.swift 23 KB

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