@@ -32,7 +32,7 @@ import Foundation
3232 /// - column: The column where the assertion occurs. The default is the line column you call
3333 /// this function.
3434 public func assertInlineSnapshot< Value> (
35- of value: @autoclosure ( ) throws -> Value ,
35+ of value: @autoclosure ( ) throws -> Value ? ,
3636 as snapshotting: Snapshotting < Value , String > ,
3737 message: @autoclosure ( ) -> String = " " ,
3838 record isRecording: Bool = isRecording,
@@ -46,41 +46,44 @@ import Foundation
4646 ) {
4747 let _: Void = installTestObserver
4848 do {
49- var actual : String !
49+ var actual : String ?
5050 let expectation = XCTestExpectation ( )
51- try snapshotting. snapshot ( value ( ) ) . run {
52- actual = $0
53- expectation. fulfill ( )
54- }
55- switch XCTWaiter . wait ( for: [ expectation] , timeout: timeout) {
56- case . completed:
57- break
58- case . timedOut:
59- XCTFail (
60- """
61- Exceeded timeout of \( timeout) seconds waiting for snapshot.
62-
63- This can happen when an asynchronously loaded value (like a network response) has not \
64- loaded. If a timeout is unavoidable, consider setting the " timeout " parameter of
65- " assertInlineSnapshot " to a higher value.
66- """ ,
67- file: file,
68- line: line
69- )
70- return
71- case . incorrectOrder, . interrupted, . invertedFulfillment:
72- XCTFail ( " Couldn't snapshot value " , file: file, line: line)
73- return
74- @unknown default :
75- XCTFail ( " Couldn't snapshot value " , file: file, line: line)
76- return
51+ if let value = try value ( ) {
52+ snapshotting. snapshot ( value) . run {
53+ actual = $0
54+ expectation. fulfill ( )
55+ }
56+ switch XCTWaiter . wait ( for: [ expectation] , timeout: timeout) {
57+ case . completed:
58+ break
59+ case . timedOut:
60+ XCTFail (
61+ """
62+ Exceeded timeout of \( timeout) seconds waiting for snapshot.
63+
64+ This can happen when an asynchronously loaded value (like a network response) has not \
65+ loaded. If a timeout is unavoidable, consider setting the " timeout " parameter of
66+ " assertInlineSnapshot " to a higher value.
67+ """ ,
68+ file: file,
69+ line: line
70+ )
71+ return
72+ case . incorrectOrder, . interrupted, . invertedFulfillment:
73+ XCTFail ( " Couldn't snapshot value " , file: file, line: line)
74+ return
75+ @unknown default :
76+ XCTFail ( " Couldn't snapshot value " , file: file, line: line)
77+ return
78+ }
7779 }
78- guard !isRecording, let expected = expected ? ( )
80+ let expected = expected ? ( )
81+ guard !isRecording, let expected
7982 else {
8083 // NB: Write snapshot state before calling `XCTFail` in case `continueAfterFailure = false`
8184 inlineSnapshotState [ File ( path: file) , default: [ ] ] . append (
8285 InlineSnapshot (
83- expected: expected ? ( ) ,
86+ expected: expected,
8487 actual: actual,
8588 wasRecording: isRecording,
8689 syntaxDescriptor: syntaxDescriptor,
@@ -100,9 +103,7 @@ import Foundation
100103 Automatically recorded a new snapshot for " \( syntaxDescriptor. trailingClosureLabel) " .
101104 """
102105 }
103- if let expected = expected ? ( ) ,
104- let difference = snapshotting. diffing. diff ( expected, actual) ? . 0
105- {
106+ if let difference = snapshotting. diffing. diff ( expected ?? " " , actual ?? " " ) ? . 0 {
106107 failure += " Difference: … \n \n \( difference. indenting ( by: 2 ) ) "
107108 }
108109 XCTFail (
@@ -116,7 +117,7 @@ import Foundation
116117 )
117118 return
118119 }
119- guard let difference = snapshotting. diffing. diff ( expected, actual) ? . 0
120+ guard let difference = snapshotting. diffing. diff ( expected, actual ?? " " ) ? . 0
120121 else { return }
121122
122123 let message = message ( )
@@ -304,7 +305,7 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
304305
305306 private struct InlineSnapshot : Hashable {
306307 var expected : String ?
307- var actual : String
308+ var actual : String ?
308309 var wasRecording : Bool
309310 var syntaxDescriptor : InlineSnapshotSyntaxDescriptor
310311 var function : String
@@ -421,40 +422,42 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
421422 . prefix ( while: { $0 == " " || $0 == " \t " } )
422423 )
423424 let delimiter = String (
424- repeating: " # " , count: snapshot. actual. hashCount ( isMultiline: true )
425+ repeating: " # " , count: ( snapshot. actual ?? " " ) . hashCount ( isMultiline: true )
425426 )
426427 let leadingIndent = leadingTrivia + self . indent
427428 let snapshotLabel = TokenSyntax (
428429 stringLiteral: snapshot. syntaxDescriptor. trailingClosureLabel
429430 )
430- let snapshotClosure = ClosureExprSyntax (
431- leftBrace: . leftBraceToken( trailingTrivia: . newline) ,
432- statements: CodeBlockItemListSyntax {
433- StringLiteralExprSyntax (
434- leadingTrivia: Trivia ( stringLiteral: leadingIndent) ,
435- openingPounds: . rawStringPoundDelimiter( delimiter) ,
436- openingQuote: . multilineStringQuoteToken( trailingTrivia: . newline) ,
437- segments: [
438- . stringSegment(
439- StringSegmentSyntax (
440- content: . stringSegment(
441- snapshot. actual
442- . replacingOccurrences ( of: " \r " , with: #"\ \#( delimiter) r"# )
443- . indenting ( with: leadingIndent)
431+ let snapshotClosure = snapshot. actual. map { actual in
432+ ClosureExprSyntax (
433+ leftBrace: . leftBraceToken( trailingTrivia: . newline) ,
434+ statements: CodeBlockItemListSyntax {
435+ StringLiteralExprSyntax (
436+ leadingTrivia: Trivia ( stringLiteral: leadingIndent) ,
437+ openingPounds: . rawStringPoundDelimiter( delimiter) ,
438+ openingQuote: . multilineStringQuoteToken( trailingTrivia: . newline) ,
439+ segments: [
440+ . stringSegment(
441+ StringSegmentSyntax (
442+ content: . stringSegment(
443+ actual
444+ . replacingOccurrences ( of: " \r " , with: #"\ \#( delimiter) r"# )
445+ . indenting ( with: leadingIndent)
446+ )
444447 )
445448 )
446- )
447- ] ,
448- closingQuote: . multilineStringQuoteToken(
449- leadingTrivia: . newline + Trivia( stringLiteral: leadingIndent)
450- ) ,
451- closingPounds: . rawStringPoundDelimiter( delimiter)
449+ ] ,
450+ closingQuote: . multilineStringQuoteToken(
451+ leadingTrivia: . newline + Trivia( stringLiteral: leadingIndent)
452+ ) ,
453+ closingPounds: . rawStringPoundDelimiter( delimiter)
454+ )
455+ } ,
456+ rightBrace: . rightBraceToken(
457+ leadingTrivia: . newline + Trivia( stringLiteral: leadingTrivia)
452458 )
453- } ,
454- rightBrace: . rightBraceToken(
455- leadingTrivia: . newline + Trivia( stringLiteral: leadingTrivia)
456459 )
457- )
460+ }
458461
459462 let arguments = functionCallExpr. arguments
460463 let firstTrailingClosureOffset =
@@ -475,23 +478,41 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
475478 switch centeredTrailingClosureOffset {
476479 case ..< 0 :
477480 let index = arguments. index ( arguments. startIndex, offsetBy: trailingClosureOffset)
478- functionCallExpr. arguments [ index] . label = snapshotLabel
479- functionCallExpr. arguments [ index] . expression = ExprSyntax ( snapshotClosure)
481+ if let snapshotClosure {
482+ functionCallExpr. arguments [ index] . label = snapshotLabel
483+ functionCallExpr. arguments [ index] . expression = ExprSyntax ( snapshotClosure)
484+ } else {
485+ functionCallExpr. arguments. remove ( at: index)
486+ }
480487
481488 case 0 :
482489 if snapshot. wasRecording || functionCallExpr. trailingClosure == nil {
483490 functionCallExpr. rightParen? . trailingTrivia = . space
484- functionCallExpr. trailingClosure = snapshotClosure
491+ if let snapshotClosure {
492+ functionCallExpr. trailingClosure = snapshotClosure // FIXME: ?? multipleTrailingClosures.removeFirst()
493+ } else if !functionCallExpr. additionalTrailingClosures. isEmpty {
494+ let additionalTrailingClosure = functionCallExpr. additionalTrailingClosures. remove (
495+ at: functionCallExpr. additionalTrailingClosures. startIndex
496+ )
497+ functionCallExpr. trailingClosure = additionalTrailingClosure. closure
498+ } else {
499+ functionCallExpr. rightParen? . trailingTrivia = " "
500+ functionCallExpr. trailingClosure = nil
501+ }
485502 } else {
486503 fatalError ( )
487504 }
488505
489506 case 1 ... :
490- var newElement : MultipleTrailingClosureElementSyntax {
491- MultipleTrailingClosureElementSyntax (
492- label: snapshotLabel,
493- closure: snapshotClosure. with ( \. leadingTrivia, snapshotClosure. leadingTrivia + . space)
494- )
507+ var newElement : MultipleTrailingClosureElementSyntax ? {
508+ snapshotClosure. map { snapshotClosure in
509+ MultipleTrailingClosureElementSyntax (
510+ label: snapshotLabel,
511+ closure: snapshotClosure. with (
512+ \. leadingTrivia, snapshotClosure. leadingTrivia + . space
513+ )
514+ )
515+ }
495516 }
496517
497518 if !functionCallExpr. additionalTrailingClosures. isEmpty,
@@ -510,16 +531,22 @@ public struct InlineSnapshotSyntaxDescriptor: Hashable {
510531 functionCallExpr. additionalTrailingClosures [ index] . label. text
511532 ) {
512533 if snapshot. wasRecording {
513- functionCallExpr. additionalTrailingClosures [ index] . label = snapshotLabel
514- functionCallExpr. additionalTrailingClosures [ index] . closure = snapshotClosure
534+ if let snapshotClosure {
535+ functionCallExpr. additionalTrailingClosures [ index] . label = snapshotLabel
536+ functionCallExpr. additionalTrailingClosures [ index] . closure = snapshotClosure
537+ } else {
538+ functionCallExpr. additionalTrailingClosures. remove ( at: index)
539+ }
515540 }
516- } else {
541+ } else if let newElement,
542+ snapshot. wasRecording || index == functionCallExpr. additionalTrailingClosures. endIndex
543+ {
517544 functionCallExpr. additionalTrailingClosures. insert (
518545 newElement. with ( \. trailingTrivia, . space) ,
519546 at: index
520547 )
521548 }
522- } else if centeredTrailingClosureOffset >= 1 {
549+ } else if centeredTrailingClosureOffset >= 1 , let newElement {
523550 if let index = functionCallExpr. additionalTrailingClosures. index (
524551 functionCallExpr. additionalTrailingClosures. endIndex,
525552 offsetBy: - 1 ,
0 commit comments