1212//
1313//===----------------------------------------------------------------------===//
1414
15- import XCTest
1615@_spi ( Testing) import SwiftDriver
16+ import Testing
1717
1818// Yes, these are meta-tests! `assertDiagnostics(do:)` and friends are
1919// complicated enough to warrant a few tests of their own. To test that they
20- // fail when they're supposed to, this test class has access to an
21- // `assertFails(times:during:)` helper; see `FailableTestCase` below for the
22- // implementation.
20+ // fail when they're supposed to, we use `withKnownIssue` to catch expected
21+ // failures.
2322
24- class AssertDiagnosticsTests : FailableTestCase {
25- func testAssertNoDiagnostics( ) {
23+ @Suite ( . suppressKnownIssues( ) )
24+ struct AssertDiagnosticsTests {
25+ @Test func noDiagnostics( ) async throws {
2626 assertNoDiagnostics { _ in }
2727
28- assertFails {
28+ try await expectedIssue {
2929 assertNoDiagnostics { diags in
3030 diags. emit ( error: " something happened " )
3131 }
3232 }
33- assertFails {
33+ try await expectedIssue {
3434 assertNoDiagnostics { diags in
3535 diags. emit ( warning: " hello " )
3636 }
@@ -45,7 +45,7 @@ class AssertDiagnosticsTests: FailableTestCase {
4545 }
4646 }
4747
48- func testAssertDiagnostics ( ) {
48+ @ Test func diagnostics ( ) async throws {
4949 assertDiagnostics { diags, match in
5050 diags. emit ( error: " yankees won again " )
5151 match. expect ( . error( " won " ) )
@@ -55,21 +55,21 @@ class AssertDiagnosticsTests: FailableTestCase {
5555 diags. emit ( error: " yankees won again " )
5656 }
5757
58- assertFails ( times : 2 ) {
58+ try await expectedIssue ( count : 2 ) {
5959 assertDiagnostics { diags, match in
6060 match. expect ( . error( " lost " ) )
6161 diags. emit ( error: " yankees won again " )
6262 }
6363 }
6464
65- assertFails ( times : 2 ) {
65+ try await expectedIssue ( count : 2 ) {
6666 assertDiagnostics { diags, match in
6767 diags. emit ( error: " yankees won again " )
6868 diags. emit ( error: " yankees won yet again " )
6969 }
7070 }
7171
72- assertFails ( times : 2 ) {
72+ try await expectedIssue ( count : 2 ) {
7373 assertDiagnostics { diags, match in
7474 match. expect ( . error( " won " ) )
7575 match. expect ( . error( " won " ) )
@@ -78,7 +78,7 @@ class AssertDiagnosticsTests: FailableTestCase {
7878
7979 // We should get two assertion failures: one for expecting the warning, one
8080 // for emitting the error.
81- assertFails ( times : 2 ) {
81+ try await expectedIssue ( count : 2 ) {
8282 assertDiagnostics { diags, match in
8383 match. expect ( . warning( " won " ) )
8484 diags. emit ( . error( " yankees won again " ) )
@@ -87,15 +87,15 @@ class AssertDiagnosticsTests: FailableTestCase {
8787
8888 // We should get one assertion failure for the unexpected error. An
8989 // unexpected note is okay.
90- assertFails ( times : 1 ) {
90+ try await expectedIssue ( count : 1 ) {
9191 assertDiagnostics { diags, match in
9292 diags. emit ( error: " yankees won again " )
9393 diags. emit ( note: " investigate their star's doctor " )
9494 }
9595 }
9696
9797 // ...unless we tighten things up.
98- assertFails ( times : 2 ) {
98+ try await expectedIssue ( count : 2 ) {
9999 assertDiagnostics { diags, match in
100100 diags. emit ( error: " yankees won again " )
101101 diags. emit ( note: " investigate their star's doctor " )
@@ -111,27 +111,27 @@ class AssertDiagnosticsTests: FailableTestCase {
111111 }
112112 }
113113
114- func testAssertDriverDiagosotics ( ) throws {
114+ @ Test func driverDiagnostics ( ) async throws {
115115 try assertNoDriverDiagnostics ( args: " swiftc " , " test.swift " )
116116
117117 try assertDriverDiagnostics ( args: " swiftc " , " test.swift " ) { driver, verify in
118118 driver. diagnosticEngine. emit ( . error( " this mode does not support emitting modules " ) )
119119 verify. expect ( . error( " this mode does not support emitting modules " ) )
120120 }
121121
122- try assertFails {
122+ try await expectedIssue {
123123 try assertDriverDiagnostics ( args: " swiftc " , " test.swift " ) { driver, verify in
124124 verify. expect ( . error( " this mode does not support emitting modules " ) )
125125 }
126126 }
127127
128- try assertFails {
128+ try await expectedIssue {
129129 try assertDriverDiagnostics ( args: " swiftc " , " test.swift " ) { driver, verify in
130130 driver. diagnosticEngine. emit ( . error( " this mode does not support emitting modules " ) )
131131 }
132132 }
133133
134- try assertFails ( times : 2 ) {
134+ try await expectedIssue ( count : 2 ) {
135135 try assertDriverDiagnostics ( args: " swiftc " , " test.swift " ) { driver, verify in
136136 driver. diagnosticEngine. emit ( . error( " this mode does not support emitting modules " ) )
137137 verify. expect ( . error( " -static may not be used with -emit-executable " ) )
@@ -140,67 +140,45 @@ class AssertDiagnosticsTests: FailableTestCase {
140140 }
141141}
142142
143- // MARK: - Failure testing
144-
145- /// Subclasses are considered to pass if exactly the right number of test assertions
146- /// fail in each `assertFails(times:during:)` block. Failures are recorded
147- /// if they fail too often or not often enough.
148- class FailableTestCase : XCTestCase {
149- fileprivate var anticipatedFailures = 0
150-
151- func assertFails(
152- times: Int = 1 ,
153- _ message: String = " " ,
154- file: String = #file,
155- line: Int = #line,
156- during body: ( ) throws -> Void
157- ) rethrows {
158- let outer = anticipatedFailures
159- anticipatedFailures = times
160-
161- defer {
162- if anticipatedFailures > 0 {
163- recordFailure (
164- withDescription: " \( anticipatedFailures) failure(s) were supposed to occur, but did not: \( message) " ,
165- inFile: file, atLine: line,
166- expected: false
167- )
168- }
169- anticipatedFailures = outer
143+ /// Invoke a function that is expected to record a specific number of issues.
144+ ///
145+ /// - Parameters:
146+ /// - expectedCount: The exact number of issues expected.
147+ /// - comment: An optional comment describing the known issues.
148+ /// - file: The source file to attribute issues to.
149+ /// - line: The source line to attribute issues to.
150+ /// - body: The function to invoke.
151+ private func expectedIssue(
152+ count: Int = 1 ,
153+ _ comment: Comment ? = nil ,
154+ file: String = #file, line: UInt = #line,
155+ _ body: ( ) async throws -> Void
156+ ) async throws {
157+ let sourceLocation = SourceLocation (
158+ fileID: file, filePath: file,
159+ line: Int ( line) , column: 1
160+ )
161+ try await confirmation (
162+ comment,
163+ expectedCount: count,
164+ sourceLocation: sourceLocation
165+ ) { issueConfirmation in
166+ try await withKnownIssue (
167+ comment,
168+ isIntermittent: false ,
169+ sourceLocation: sourceLocation
170+ ) {
171+ try await body ( )
172+ } matching: { issue in
173+ issueConfirmation. confirm ( )
174+ return true
170175 }
171-
172- try body ( )
173- }
174-
175- override func setUp( ) {
176- super. setUp ( )
177- anticipatedFailures = 0
178176 }
177+ }
179178
180- override func recordFailure(
181- withDescription description: String ,
182- inFile filePath: String , atLine lineNumber: Int ,
183- expected: Bool
184- ) {
185- guard anticipatedFailures == 0 else {
186- anticipatedFailures -= 1
187- return
188- }
189-
190- if #available( macOS 10 . 13 , * ) {
191-
192- #if canImport(Darwin)
193- super. record ( XCTIssue ( type: . assertionFailure,
194- compactDescription: description,
195- sourceCodeContext: XCTSourceCodeContext ( location: XCTSourceCodeLocation ( filePath: filePath,
196- lineNumber: lineNumber) ) ) )
197- #else
198- super. recordFailure ( withDescription: description,
199- inFile: filePath, atLine: lineNumber,
200- expected: expected)
201- #endif
202- } else {
203- fatalError ( description, line: UInt ( lineNumber) )
204- }
179+ private extension Trait where Self == IssueHandlingTrait {
180+ /// Filter out known issues, keeping only real failures (like confirmation miscounts).
181+ static func suppressKnownIssues( ) -> Self {
182+ . filterIssues { $0. isFailure }
205183 }
206184}
0 commit comments