Skip to content

Multiple improvements to SwiftSyntaxComparison #562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ let package = Package(
),
.target(
name: "_SwiftSyntaxTestSupport",
dependencies: ["SwiftSyntax", "SwiftSyntaxParser"]
dependencies: ["SwiftSyntax"]
),
.executableTarget(
name: "lit-test-helper",
Expand Down
59 changes: 48 additions & 11 deletions Sources/_SwiftSyntaxTestSupport/Syntax+Assertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
//===----------------------------------------------------------------------===//

import SwiftSyntax
import SwiftSyntaxParser
import XCTest

/// Verifies that there is a next item returned by the iterator and that it
Expand All @@ -35,22 +34,55 @@ public func XCTAssertNextIsNil<Iterator: IteratorProtocol>(_ iterator: inout Ite
}

/// Verifies that the tree parsed from `actual` has the same structure as
/// `expected`, ie. it has the same structure and optionally the same trivia
/// (if `includeTrivia` is set).
public func XCTAssertSameStructure(_ actual: String, _ expected: Syntax, includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line) throws {
let actualTree = try Syntax(SyntaxParser.parse(source: actual))
/// `expected` when parsed with `parse`, ie. it has the same structure and
/// optionally the same trivia (if `includeTrivia` is set).
public func XCTAssertSameStructure(
_ actual: String,
parse: (String) throws -> Syntax,
_ expected: Syntax,
includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line
) throws {
let actualTree = try parse(actual)
XCTAssertSameStructure(actualTree, expected, includeTrivia: includeTrivia, file: file, line: line)
}

/// Verifies that two trees are equivalent, ie. they have the same structure
/// and optionally the same trivia if `includeTrivia` is set.
public func XCTAssertSameStructure(_ actual: Syntax, _ expected: Syntax, includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line) {
public func XCTAssertSameStructure(
_ actual: SyntaxProtocol,
_ expected: SyntaxProtocol,
includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line
) {
let diff = actual.findFirstDifference(baseline: expected, includeTrivia: includeTrivia)
XCTAssertNil(diff, diff!.debugDescription, file: file, line: line)
}

/// See `SubtreeMatcher.assertSameStructure`.
public func XCTAssertHasSubstructure(
_ markedText: String,
parse: (String) throws -> Syntax,
afterMarker: String? = nil,
_ expected: SyntaxProtocol,
includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line
) throws {
let subtreeMatcher = try SubtreeMatcher(markedText, parse: parse)
try subtreeMatcher.assertSameStructure(afterMarker: afterMarker, Syntax(expected), file: file, line: line)
}

/// See `SubtreeMatcher.assertSameStructure`.
public func XCTAssertHasSubstructure(
_ actualTree: SyntaxProtocol,
_ expected: SyntaxProtocol,
includeTrivia: Bool = false,
file: StaticString = #filePath, line: UInt = #line
) throws {
let subtreeMatcher = SubtreeMatcher(Syntax(actualTree))
try subtreeMatcher.assertSameStructure(Syntax(expected), file: file, line: line)
}

/// Allows matching a subtrees of the given `markedText` against
/// `baseline`/`expected` trees, where a combination of markers and the type
/// of the `expected` tree is used to first find the subtree to match. Note
Expand Down Expand Up @@ -98,7 +130,7 @@ public struct SubtreeMatcher {
/// The syntax tree from parsing source *with markers removed*.
private var actualTree: Syntax

public init(_ markedText: String) throws {
public init(_ markedText: String, parse: (String) throws -> Syntax) throws {
var text = ""
var markers = [String: Int]()
var lastIndex = markedText.startIndex
Expand All @@ -112,7 +144,12 @@ public struct SubtreeMatcher {
text += markedText[lastIndex ..< markedText.endIndex]

self.markers = markers.isEmpty ? ["DEFAULT": 0] : markers
self.actualTree = try Syntax(SyntaxParser.parse(source: text))
self.actualTree = try parse(text)
}

public init(_ actualTree: Syntax) {
self.markers = ["DEFAULT": 0]
self.actualTree = actualTree
}

/// Same as `Syntax.findFirstDifference(baseline:includeTrivia:)`, but
Expand Down Expand Up @@ -149,7 +186,7 @@ public enum SubtreeError: Error, CustomStringConvertible {
case let .invalidMarker(name):
return "Could not find marker with name '\(name)'"
case let .invalidSubtree(tree, afterUTF8Offset, type):
return "Could not find subtree after UTF8 offset \(afterUTF8Offset) with type \(type) in:\n\(tree.debugDescription)"
return "Could not find subtree after UTF8 offset \(afterUTF8Offset) with type \(type) in:\n\(tree.debugDescription(includeChildren: true))"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import SwiftSyntax
import _SwiftSyntaxTestSupport
import SwiftSyntaxParser
import XCTest

private func parse(source: String) throws -> Syntax {
return try Syntax(SyntaxParser.parse(source: source))
}

public class SyntaxComparisonTests: XCTestCase {
public func testSame() throws {
let expected = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("f")))

let actual = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("f")))
XCTAssertNil(actual.findFirstDifference(baseline: expected))

let matcher = try SubtreeMatcher("struct A { func f() { } }")
let matcher = try SubtreeMatcher("struct A { func f() { } }", parse: parse)
try XCTAssertNil(matcher.findFirstDifference(baseline: expected))
}

Expand All @@ -36,7 +41,7 @@ public class SyntaxComparisonTests: XCTestCase {
let actual = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("f")))
try expectations(actual.findFirstDifference(baseline: expected))

let matcher = try SubtreeMatcher("struct A { #^FUNC^#func f() { } }")
let matcher = try SubtreeMatcher("struct A { #^FUNC^#func f() { } }", parse: parse)
try expectations(matcher.findFirstDifference(baseline: expected))
}

Expand All @@ -52,7 +57,7 @@ public class SyntaxComparisonTests: XCTestCase {
let actual = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("g")))
try expectations(actual.findFirstDifference(baseline: expected))

let matcher = try SubtreeMatcher("struct A { #^FUNC^#func g() { } }")
let matcher = try SubtreeMatcher("struct A { #^FUNC^#func g() { } }", parse: parse)
try expectations(matcher.findFirstDifference(afterMarker: "FUNC", baseline: expected))
}

Expand All @@ -69,7 +74,7 @@ public class SyntaxComparisonTests: XCTestCase {
XCTAssertNil(actual.findFirstDifference(baseline: expected))
try expectations(actual.findFirstDifference(baseline: expected, includeTrivia: true))

let matcher = try SubtreeMatcher("struct A {func f() { }}")
let matcher = try SubtreeMatcher("struct A {func f() { }}", parse: parse)
try XCTAssertNil(matcher.findFirstDifference(baseline: expected))
try expectations(matcher.findFirstDifference(baseline: expected, includeTrivia: true))
}
Expand All @@ -86,7 +91,7 @@ public class SyntaxComparisonTests: XCTestCase {
let actual = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("f")))
try expectations(actual.findFirstDifference(baseline: expected))

let matcher = try SubtreeMatcher("struct A { func f() { } }")
let matcher = try SubtreeMatcher("struct A { func f() { } }", parse: parse)
try expectations(matcher.findFirstDifference(baseline: expected))
}

Expand All @@ -100,7 +105,7 @@ public class SyntaxComparisonTests: XCTestCase {
let actual = Syntax(makeFunc(identifier: SyntaxFactory.makeIdentifier("f")))
try expectations(actual.findFirstDifference(baseline: expected))

let matcher = try SubtreeMatcher("struct A { func f() { } }")
let matcher = try SubtreeMatcher("struct A { func f() { } }", parse: parse)
try expectations(matcher.findFirstDifference(baseline: expected))
}

Expand All @@ -120,7 +125,7 @@ public class SyntaxComparisonTests: XCTestCase {
0
}
}
""")
""", parse: parse)
try expectations(matcher.findFirstDifference(baseline: expected))
}

Expand All @@ -137,7 +142,7 @@ public class SyntaxComparisonTests: XCTestCase {
0
}
}
""")
""", parse: parse)
let funcDiff = try XCTUnwrap(matcher.findFirstDifference(afterMarker: "FUNC", baseline: expectedFunc))
XCTAssertEqual(funcDiff.reason, .additionalNode)

Expand Down