TextBasedRendererTests.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  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: { $0.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: { $0.renderComment(_:) },
  55. rendersAs: #"""
  56. /// Generated by foo
  57. ///
  58. /// Also, bar
  59. """#
  60. )
  61. try _test(
  62. .mark("Lorem ipsum", sectionBreak: false),
  63. renderedBy: { $0.renderComment(_:) },
  64. rendersAs: #"""
  65. // MARK: Lorem ipsum
  66. """#
  67. )
  68. try _test(
  69. .mark("Lorem ipsum", sectionBreak: true),
  70. renderedBy: { $0.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: { $0.renderComment(_:) },
  82. rendersAs: #"""
  83. // Generated by foo
  84. // Also, bar
  85. """#
  86. )
  87. try _test(
  88. .preFormatted("/// Lorem ipsum\n"),
  89. renderedBy: { $0.renderComment(_:) },
  90. rendersAs: """
  91. /// Lorem ipsum
  92. """
  93. )
  94. try _test(
  95. .preFormatted("/// Lorem ipsum\n\n/// Lorem ipsum\n"),
  96. renderedBy: { $0.renderComment(_:) },
  97. rendersAs: """
  98. /// Lorem ipsum
  99. /// Lorem ipsum
  100. """
  101. )
  102. }
  103. func testImports() throws {
  104. try _test(nil, renderedBy: { $0.renderImports(_:) }, rendersAs: "")
  105. try _test(
  106. [ImportDescription(moduleName: "Foo"), ImportDescription(moduleName: "Bar")],
  107. renderedBy: { $0.renderImports(_:) },
  108. rendersAs: #"""
  109. import Foo
  110. import Bar
  111. """#
  112. )
  113. try _test(
  114. [ImportDescription(moduleName: "Foo", spi: "Secret")],
  115. renderedBy: { $0.renderImports(_:) },
  116. rendersAs: #"""
  117. @_spi(Secret) import Foo
  118. """#
  119. )
  120. try _test(
  121. [ImportDescription(moduleName: "Foo", preconcurrency: .onOS(["Bar", "Baz"]))],
  122. renderedBy: { $0.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: { $0.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: { $0.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: { $0.renderedAccessModifier(_:) },
  200. rendersAs: #"""
  201. public
  202. """#
  203. )
  204. try _test(
  205. .internal,
  206. renderedBy: { $0.renderedAccessModifier(_:) },
  207. rendersAs: #"""
  208. internal
  209. """#
  210. )
  211. try _test(
  212. .fileprivate,
  213. renderedBy: { $0.renderedAccessModifier(_:) },
  214. rendersAs: #"""
  215. fileprivate
  216. """#
  217. )
  218. try _test(
  219. .private,
  220. renderedBy: { $0.renderedAccessModifier(_:) },
  221. rendersAs: #"""
  222. private
  223. """#
  224. )
  225. }
  226. func testLiterals() throws {
  227. try _test(
  228. .string("hi"),
  229. renderedBy: { $0.renderLiteral(_:) },
  230. rendersAs: #"""
  231. "hi"
  232. """#
  233. )
  234. try _test(
  235. .string("this string: \"foo\""),
  236. renderedBy: { $0.renderLiteral(_:) },
  237. rendersAs: #"""
  238. #"this string: "foo""#
  239. """#
  240. )
  241. try _test(
  242. .nil,
  243. renderedBy: { $0.renderLiteral(_:) },
  244. rendersAs: #"""
  245. nil
  246. """#
  247. )
  248. try _test(
  249. .array([]),
  250. renderedBy: { $0.renderLiteral(_:) },
  251. rendersAs: #"""
  252. []
  253. """#
  254. )
  255. try _test(
  256. .array([.literal(.nil)]),
  257. renderedBy: { $0.renderLiteral(_:) },
  258. rendersAs: #"""
  259. [
  260. nil
  261. ]
  262. """#
  263. )
  264. try _test(
  265. .array([.literal(.nil), .literal(.nil)]),
  266. renderedBy: { $0.renderLiteral(_:) },
  267. rendersAs: #"""
  268. [
  269. nil,
  270. nil
  271. ]
  272. """#
  273. )
  274. }
  275. func testExpression() throws {
  276. try _test(
  277. .literal(.nil),
  278. renderedBy: { $0.renderExpression(_:) },
  279. rendersAs: #"""
  280. nil
  281. """#
  282. )
  283. try _test(
  284. .identifierPattern("foo"),
  285. renderedBy: { $0.renderExpression(_:) },
  286. rendersAs: #"""
  287. foo
  288. """#
  289. )
  290. try _test(
  291. .memberAccess(.init(left: .identifierPattern("foo"), right: "bar")),
  292. renderedBy: { $0.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: { $0.renderExpression(_:) },
  305. rendersAs: #"""
  306. callee(foo)
  307. """#
  308. )
  309. }
  310. func testDeclaration() throws {
  311. try _test(
  312. .variable(kind: .let, left: "foo"),
  313. renderedBy: { $0.renderDeclaration(_:) },
  314. rendersAs: #"""
  315. let foo
  316. """#
  317. )
  318. try _test(
  319. .extension(.init(onType: "String", declarations: [])),
  320. renderedBy: { $0.renderDeclaration(_:) },
  321. rendersAs: #"""
  322. extension String {
  323. }
  324. """#
  325. )
  326. try _test(
  327. .struct(.init(name: "Foo")),
  328. renderedBy: { $0.renderDeclaration(_:) },
  329. rendersAs: #"""
  330. struct Foo {}
  331. """#
  332. )
  333. try _test(
  334. .protocol(.init(name: "Foo")),
  335. renderedBy: { $0.renderDeclaration(_:) },
  336. rendersAs: #"""
  337. protocol Foo {}
  338. """#
  339. )
  340. try _test(
  341. .enum(.init(name: "Foo")),
  342. renderedBy: { $0.renderDeclaration(_:) },
  343. rendersAs: #"""
  344. enum Foo {}
  345. """#
  346. )
  347. try _test(
  348. .typealias(.init(name: "foo", existingType: .member(["Foo", "Bar"]))),
  349. renderedBy: { $0.renderDeclaration(_:) },
  350. rendersAs: #"""
  351. typealias foo = Foo.Bar
  352. """#
  353. )
  354. try _test(
  355. .function(FunctionDescription.init(kind: .function(name: "foo"), body: [])),
  356. renderedBy: { $0.renderDeclaration(_:) },
  357. rendersAs: #"""
  358. func foo() {}
  359. """#
  360. )
  361. }
  362. func testFunctionKind() throws {
  363. try _test(
  364. .initializer,
  365. renderedBy: { $0.renderedFunctionKind(_:) },
  366. rendersAs: #"""
  367. init
  368. """#
  369. )
  370. try _test(
  371. .function(name: "funky"),
  372. renderedBy: { $0.renderedFunctionKind(_:) },
  373. rendersAs: #"""
  374. func funky
  375. """#
  376. )
  377. try _test(
  378. .function(name: "funky", isStatic: true),
  379. renderedBy: { $0.renderedFunctionKind(_:) },
  380. rendersAs: #"""
  381. static func funky
  382. """#
  383. )
  384. }
  385. func testFunctionKeyword() throws {
  386. try _test(
  387. .throws,
  388. renderedBy: { $0.renderedFunctionKeyword(_:) },
  389. rendersAs: #"""
  390. throws
  391. """#
  392. )
  393. try _test(
  394. .async,
  395. renderedBy: { $0.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: { $0.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: { $0.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: { $0.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: { $0.renderParameter(_:) },
  426. rendersAs: #"""
  427. _: T = nil
  428. """#
  429. )
  430. try _test(
  431. .init(label: nil, name: nil, type: .member("T"), defaultValue: nil),
  432. renderedBy: { $0.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: { $0.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: { $0.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: { $0.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: { $0.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: { $0.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: { $0.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: { $0.renderIdentifier(_:) },
  525. rendersAs: #"""
  526. foo
  527. """#
  528. )
  529. }
  530. func testMemberAccess() throws {
  531. try _test(
  532. .init(left: .identifierPattern("foo"), right: "bar"),
  533. renderedBy: { $0.renderMemberAccess(_:) },
  534. rendersAs: #"""
  535. foo.bar
  536. """#
  537. )
  538. try _test(
  539. .init(left: nil, right: "bar"),
  540. renderedBy: { $0.renderMemberAccess(_:) },
  541. rendersAs: #"""
  542. .bar
  543. """#
  544. )
  545. }
  546. func testFunctionCallArgument() throws {
  547. try _test(
  548. .init(label: "foo", expression: .identifierPattern("bar")),
  549. renderedBy: { $0.renderFunctionCallArgument(_:) },
  550. rendersAs: #"""
  551. foo: bar
  552. """#
  553. )
  554. try _test(
  555. .init(label: nil, expression: .identifierPattern("bar")),
  556. renderedBy: { $0.renderFunctionCallArgument(_:) },
  557. rendersAs: #"""
  558. bar
  559. """#
  560. )
  561. }
  562. func testFunctionCall() throws {
  563. try _test(
  564. .functionCall(.init(calledExpression: .identifierPattern("callee"))),
  565. renderedBy: { $0.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: { $0.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: { $0.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: { $0.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: { $0.renderDeprecation(_:) },
  620. rendersAs: #"""
  621. @available(*, deprecated)
  622. """#
  623. )
  624. try _test(
  625. .init(message: "some message"),
  626. renderedBy: { $0.renderDeprecation(_:) },
  627. rendersAs: #"""
  628. @available(*, deprecated, message: "some message")
  629. """#
  630. )
  631. try _test(
  632. .init(renamed: "newSymbol(param:)"),
  633. renderedBy: { $0.renderDeprecation(_:) },
  634. rendersAs: #"""
  635. @available(*, deprecated, renamed: "newSymbol(param:)")
  636. """#
  637. )
  638. try _test(
  639. .init(message: "some message", renamed: "newSymbol(param:)"),
  640. renderedBy: { $0.renderDeprecation(_:) },
  641. rendersAs: #"""
  642. @available(*, deprecated, message: "some message", renamed: "newSymbol(param:)")
  643. """#
  644. )
  645. }
  646. func testAvailability() throws {
  647. try _test(
  648. .init(osVersions: [
  649. .init(os: .macOS, version: "12.0"),
  650. .init(os: .iOS, version: "13.1.2"),
  651. .init(os: .watchOS, version: "8.1.2"),
  652. .init(os: .tvOS, version: "15.0.2"),
  653. ]),
  654. renderedBy: { $0.renderAvailability(_:) },
  655. rendersAs: #"""
  656. @available(macOS 12.0, iOS 13.1.2, watchOS 8.1.2, tvOS 15.0.2, *)
  657. """#
  658. )
  659. }
  660. func testBindingKind() throws {
  661. try _test(
  662. .var,
  663. renderedBy: { $0.renderedBindingKind(_:) },
  664. rendersAs: #"""
  665. var
  666. """#
  667. )
  668. try _test(
  669. .let,
  670. renderedBy: { $0.renderedBindingKind(_:) },
  671. rendersAs: #"""
  672. let
  673. """#
  674. )
  675. }
  676. func testVariable() throws {
  677. try _test(
  678. .init(
  679. accessModifier: .public,
  680. isStatic: true,
  681. kind: .let,
  682. left: .identifierPattern("foo"),
  683. type: .init(TypeName.string),
  684. right: .literal(.string("bar"))
  685. ),
  686. renderedBy: { $0.renderVariable(_:) },
  687. rendersAs: #"""
  688. public static let foo: Swift.String = "bar"
  689. """#
  690. )
  691. try _test(
  692. .init(
  693. accessModifier: .internal,
  694. isStatic: false,
  695. kind: .var,
  696. left: .identifierPattern("foo"),
  697. type: nil,
  698. right: nil
  699. ),
  700. renderedBy: { $0.renderVariable(_:) },
  701. rendersAs: #"""
  702. internal var foo
  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. ),
  712. renderedBy: { $0.renderVariable(_:) },
  713. rendersAs: #"""
  714. var foo: Swift.Int {
  715. 42
  716. }
  717. """#
  718. )
  719. try _test(
  720. .init(
  721. kind: .var,
  722. left: .identifierPattern("foo"),
  723. type: .init(TypeName.int),
  724. getter: [CodeBlock.expression(.literal(.int(42)))],
  725. getterEffects: [.throws]
  726. ),
  727. renderedBy: { $0.renderVariable(_:) },
  728. rendersAs: #"""
  729. var foo: Swift.Int {
  730. get throws {
  731. 42
  732. }
  733. }
  734. """#
  735. )
  736. }
  737. func testStruct() throws {
  738. try _test(
  739. .init(name: "Structy"),
  740. renderedBy: { $0.renderStruct(_:) },
  741. rendersAs: #"""
  742. struct Structy {}
  743. """#
  744. )
  745. }
  746. func testProtocol() throws {
  747. try _test(
  748. .init(name: "Protocoly"),
  749. renderedBy: { $0.renderProtocol(_:) },
  750. rendersAs: #"""
  751. protocol Protocoly {}
  752. """#
  753. )
  754. }
  755. func testEnum() throws {
  756. try _test(
  757. .init(name: "Enumy"),
  758. renderedBy: { $0.renderEnum(_:) },
  759. rendersAs: #"""
  760. enum Enumy {}
  761. """#
  762. )
  763. }
  764. func testCodeBlockItem() throws {
  765. try _test(
  766. .declaration(.variable(kind: .let, left: "foo")),
  767. renderedBy: { $0.renderCodeBlockItem(_:) },
  768. rendersAs: #"""
  769. let foo
  770. """#
  771. )
  772. try _test(
  773. .expression(.literal(.nil)),
  774. renderedBy: { $0.renderCodeBlockItem(_:) },
  775. rendersAs: #"""
  776. nil
  777. """#
  778. )
  779. }
  780. func testCodeBlock() throws {
  781. try _test(
  782. .init(
  783. comment: .inline("- MARK: Section"),
  784. item: .declaration(.variable(kind: .let, left: "foo"))
  785. ),
  786. renderedBy: { $0.renderCodeBlock(_:) },
  787. rendersAs: #"""
  788. // - MARK: Section
  789. let foo
  790. """#
  791. )
  792. try _test(
  793. .init(comment: nil, item: .declaration(.variable(kind: .let, left: "foo"))),
  794. renderedBy: { $0.renderCodeBlock(_:) },
  795. rendersAs: #"""
  796. let foo
  797. """#
  798. )
  799. }
  800. func testTypealias() throws {
  801. try _test(
  802. .init(name: "inty", existingType: .member("Int")),
  803. renderedBy: { $0.renderTypealias(_:) },
  804. rendersAs: #"""
  805. typealias inty = Int
  806. """#
  807. )
  808. try _test(
  809. .init(accessModifier: .private, name: "inty", existingType: .member("Int")),
  810. renderedBy: { $0.renderTypealias(_:) },
  811. rendersAs: #"""
  812. private typealias inty = Int
  813. """#
  814. )
  815. }
  816. func testFile() throws {
  817. try _test(
  818. .init(
  819. topComment: .inline("hi"),
  820. imports: [.init(moduleName: "Foo")],
  821. codeBlocks: [.init(comment: nil, item: .declaration(.struct(.init(name: "Bar"))))]
  822. ),
  823. renderedBy: { $0.renderFile(_:) },
  824. rendersAs: #"""
  825. // hi
  826. import Foo
  827. struct Bar {}
  828. """#
  829. )
  830. }
  831. func testIndentation() throws {
  832. try _test(
  833. .init(
  834. topComment: .inline("hi"),
  835. imports: [.init(moduleName: "Foo")],
  836. codeBlocks: [
  837. .init(
  838. comment: nil,
  839. item: .declaration(.struct(.init(name: "Bar", members: [.struct(.init(name: "Baz"))])))
  840. )
  841. ]
  842. ),
  843. renderedBy: { $0.renderFile(_:) },
  844. rendersAs: #"""
  845. // hi
  846. import Foo
  847. struct Bar {
  848. struct Baz {}
  849. }
  850. """#,
  851. indentation: 2
  852. )
  853. try _test(
  854. .array([.literal(.nil), .literal(.nil)]),
  855. renderedBy: { $0.renderLiteral(_:) },
  856. rendersAs: #"""
  857. [
  858. nil,
  859. nil
  860. ]
  861. """#,
  862. indentation: 3
  863. )
  864. try _test(
  865. .init(
  866. kind: .var,
  867. left: .identifierPattern("foo"),
  868. type: .init(TypeName.int),
  869. getter: [CodeBlock.expression(.literal(.int(42)))],
  870. getterEffects: [.throws]
  871. ),
  872. renderedBy: { $0.renderVariable(_:) },
  873. rendersAs: #"""
  874. var foo: Swift.Int {
  875. get throws {
  876. 42
  877. }
  878. }
  879. """#,
  880. indentation: 5
  881. )
  882. }
  883. }
  884. extension Test_TextBasedRenderer {
  885. func _test<Input>(
  886. _ input: Input,
  887. renderedBy renderClosure: (TextBasedRenderer) -> ((Input) -> String),
  888. rendersAs output: String,
  889. file: StaticString = #filePath,
  890. line: UInt = #line,
  891. indentation: Int = 4
  892. ) throws {
  893. let renderer = TextBasedRenderer(indentation: indentation)
  894. XCTAssertEqual(renderClosure(renderer)(input), output, file: file, line: line)
  895. }
  896. func _test<Input>(
  897. _ input: Input,
  898. renderedBy renderClosure: (TextBasedRenderer) -> ((Input) -> Void),
  899. rendersAs output: String,
  900. file: StaticString = #filePath,
  901. line: UInt = #line,
  902. indentation: Int = 4
  903. ) throws {
  904. try _test(
  905. input,
  906. renderedBy: { renderer in
  907. let closure = renderClosure(renderer)
  908. return { input in
  909. closure(input)
  910. return renderer.renderedContents()
  911. }
  912. },
  913. rendersAs: output,
  914. indentation: indentation
  915. )
  916. }
  917. }