-
Notifications
You must be signed in to change notification settings - Fork 412
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
222 additions
and
0 deletions.
There are no files selected for viewing
222 changes: 222 additions & 0 deletions
222
Tests/SwiftDiagnosticsTest/NewDiagnosticsFormatterTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import SwiftDiagnostics | ||
import XCTest | ||
import SwiftSyntax | ||
import SwiftSyntaxBuilder | ||
import _SwiftSyntaxTestSupport | ||
import SwiftParser | ||
|
||
struct DiagnosticDescriptor { | ||
let locationMarker: String | ||
let id: MessageID | ||
let message: String | ||
let severity: DiagnosticSeverity | ||
let highlight: [Syntax] // TODO: How to create an abstract model for this? | ||
let noteDescriptors: [NoteDescriptor] | ||
let fixIts: [FixIt] // TODO: How to create an abstract model for this? | ||
|
||
init( | ||
locationMarker: String, | ||
id: MessageID, | ||
message: String, | ||
severity: DiagnosticSeverity = .error, | ||
highlight: [Syntax] = [], | ||
noteDescriptors: [NoteDescriptor] = [], | ||
fixIts: [FixIt] = [] | ||
) { | ||
self.locationMarker = locationMarker | ||
self.id = id | ||
self.message = message | ||
self.severity = severity | ||
self.highlight = highlight | ||
self.noteDescriptors = noteDescriptors | ||
self.fixIts = fixIts | ||
} | ||
} | ||
|
||
struct NoteDescriptor { | ||
let locationMarker: String | ||
let id: MessageID | ||
let message: String | ||
} | ||
|
||
func assertAnnotated( | ||
markedSource: String, | ||
withDiagnostics diagnosticDescriptors: [DiagnosticDescriptor], | ||
matches expectedAnnotatedSource: String, | ||
file: StaticString = #file, | ||
line: UInt = #line | ||
) { | ||
let (markers, source) = extractMarkers(markedSource) | ||
let tree = Parser.parse(source: source) | ||
|
||
var diagnostics = [Diagnostic]() | ||
for diagnosticDescriptor in diagnosticDescriptors { | ||
guard let markerOffset = markers[diagnosticDescriptor.locationMarker] else { | ||
XCTFail("Marker \(diagnosticDescriptor.locationMarker) not found in the marked source", file: file, line: line) | ||
continue | ||
} | ||
let markerPosition = AbsolutePosition(utf8Offset: markerOffset) | ||
guard let token = tree.token(at: markerPosition), let node = token.parent else { | ||
XCTFail("Node not found at marker \(diagnosticDescriptor.locationMarker)", file: file, line: line) | ||
continue | ||
} | ||
|
||
var notes = [Note]() | ||
for noteDescriptor in diagnosticDescriptor.noteDescriptors { | ||
guard let markerOffset = markers[noteDescriptor.locationMarker] else { | ||
XCTFail("Marker \(noteDescriptor.locationMarker) not found in the marked source", file: file, line: line) | ||
continue | ||
} | ||
let markerPosition = AbsolutePosition(utf8Offset: markerOffset) | ||
guard let token = tree.token(at: markerPosition), let node = token.parent else { | ||
XCTFail("Node not found at marker \(noteDescriptor.locationMarker)", file: file, line: line) | ||
continue | ||
} | ||
|
||
let note = Note( | ||
node: node, | ||
message: SimpleNoteMessage(message: noteDescriptor.message, fixItID: noteDescriptor.id) | ||
) | ||
notes.append(note) | ||
} | ||
|
||
let diagnostic = Diagnostic( | ||
node: node, | ||
message: SimpleDiagnosticMessage( | ||
message: diagnosticDescriptor.message, | ||
diagnosticID: diagnosticDescriptor.id, | ||
severity: diagnosticDescriptor.severity | ||
), | ||
highlights: diagnosticDescriptor.highlight, | ||
notes: notes, | ||
fixIts: diagnosticDescriptor.fixIts | ||
) | ||
diagnostics.append(diagnostic) | ||
} | ||
|
||
let annotatedSource = DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics) | ||
|
||
assertStringsEqualWithDiff( | ||
annotatedSource, | ||
expectedAnnotatedSource, | ||
file: file, | ||
line: line | ||
) | ||
} | ||
|
||
final class DiagnosticsFormatterTests: XCTestCase { | ||
func test_1() { | ||
assertAnnotated( | ||
markedSource: """ | ||
func foo() -> Int { | ||
if 1️⃣1 != 0 { | ||
return 0 | ||
} | ||
return 1 | ||
} | ||
""", | ||
withDiagnostics: [ | ||
DiagnosticDescriptor( | ||
locationMarker: "1️⃣", | ||
id: .test, | ||
message: "My message goes here!", | ||
severity: .warning | ||
), | ||
], | ||
matches: """ | ||
1 │ func foo() -> Int { | ||
2 │ if 1 != 0 { | ||
│ ╰─ warning: My message goes here! | ||
3 │ return 0 | ||
4 │ } | ||
""" | ||
) | ||
} | ||
|
||
func test_2() { | ||
assertAnnotated( | ||
markedSource: """ | ||
func foo() -> Int { | ||
if 1️⃣1 != 0 2️⃣{ | ||
return 0 | ||
} | ||
return 1 | ||
} | ||
""", | ||
withDiagnostics: [ | ||
DiagnosticDescriptor( | ||
locationMarker: "1️⃣", | ||
id: .test, | ||
message: "My message goes here!", | ||
noteDescriptors: [ | ||
NoteDescriptor(locationMarker: "1️⃣", id: .test, message: "First message"), | ||
NoteDescriptor(locationMarker: "1️⃣", id: .test, message: "Second message"), | ||
NoteDescriptor(locationMarker: "2️⃣", id: .test, message: "Other message") | ||
] | ||
), | ||
], | ||
matches: """ | ||
1 │ func foo() -> Int { | ||
2 │ if 1 != 0 { | ||
│ │ ╰─ note: Other message | ||
│ ├─ error: My message goes here! | ||
│ ├─ note: First message | ||
│ ╰─ note: Second message | ||
3 │ return 0 | ||
4 │ } | ||
""" | ||
) | ||
} | ||
|
||
func test_3() { | ||
assertAnnotated( | ||
markedSource: """ | ||
func 2️⃣foo() -> Int { | ||
if 1️⃣1 != 0 { | ||
return 0 | ||
} | ||
return 1 | ||
} | ||
""", | ||
withDiagnostics: [ | ||
DiagnosticDescriptor( | ||
locationMarker: "1️⃣", | ||
id: .test, | ||
message: "My message goes here!", | ||
noteDescriptors: [ | ||
NoteDescriptor(locationMarker: "1️⃣", id: .test, message: "First message"), | ||
NoteDescriptor(locationMarker: "2️⃣", id: .test, message: "Second message"), | ||
] | ||
), | ||
], | ||
matches: """ | ||
1 │ func foo() -> Int { | ||
| ╰─ note: Second message // This note comes from from diagnostic, how to present it? | ||
2 │ if 1 != 0 { | ||
│ ├─ error: My message goes here! | ||
│ ╰─ note: First message | ||
3 │ return 0 | ||
4 │ } | ||
""" | ||
) | ||
} | ||
} | ||
|
||
fileprivate extension MessageID { | ||
static let test = Self(domain: "test", id: "conjured") | ||
} |