Skip to content

Commit f4764ad

Browse files
committed
Add .default reporter, deprecate .runtimeWarning
This patch moves the test reporting logic into a `default` reporter so that changing the reporters can influence tests. This is an alternate solution to #146.
1 parent f1c0bb0 commit f4764ad

File tree

5 files changed

+138
-106
lines changed

5 files changed

+138
-106
lines changed

Sources/IssueReporting/Internal/Deprecations.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
// NB: Deprecated after 1.7.0
2+
3+
extension IssueReporter where Self == _DefaultReporter {
4+
@available(*, deprecated, renamed: "default")
5+
#if canImport(Darwin)
6+
@_transparent
7+
#endif
8+
public static var runtimeWarning: Self { Self() }
9+
}
10+
111
// NB: Deprecated after 1.2.2
212

313
#if canImport(Darwin)
@@ -9,4 +19,4 @@
919
public typealias FatalErrorReporter = _FatalErrorReporter
1020

1121
@available(*, unavailable, renamed: "_RuntimeWarningReporter")
12-
public typealias RuntimeWarningReporter = _RuntimeWarningReporter
22+
public typealias RuntimeWarningReporter = _DefaultReporter

Sources/IssueReporting/IsTesting.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
extension ProcessInfo {
2929
fileprivate var isTesting: Bool {
3030
if environment.keys.contains("XCTestBundlePath") { return true }
31+
if environment.keys.contains("XCTestBundleInjectPath") { return true }
3132
if environment.keys.contains("XCTestConfigurationFilePath") { return true }
3233
if environment.keys.contains("XCTestSessionIdentifier") { return true }
3334

Sources/IssueReporting/IssueReporter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public enum IssueReporters {
152152
set { _current.withLock { $0 = newValue } }
153153
}
154154

155-
@TaskLocal fileprivate static var _current = LockIsolated<[any IssueReporter]>([.runtimeWarning])
155+
@TaskLocal fileprivate static var _current = LockIsolated<[any IssueReporter]>([.default])
156156
}
157157

158158
/// Overrides the task's issue reporters for the duration of the synchronous operation.

Sources/IssueReporting/IssueReporters/RuntimeWarningReporter.swift renamed to Sources/IssueReporting/IssueReporters/DefaultReporter.swift

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ import Foundation
44
import os
55
#endif
66

7-
extension IssueReporter where Self == _RuntimeWarningReporter {
7+
extension IssueReporter where Self == _DefaultReporter {
88
/// An issue reporter that emits "purple" runtime warnings to Xcode and logs fault-level messages
99
/// to the console.
1010
///
1111
/// This is the default issue reporter. On non-Apple platforms it logs messages to `stderr`.
12+
/// During test runs it emits test failures, instead.
1213
///
1314
/// If this issue reporter receives an expected issue, it will log an info-level message to the
1415
/// console, instead.
1516
#if canImport(Darwin)
1617
@_transparent
1718
#endif
18-
public static var runtimeWarning: Self { Self() }
19+
public static var `default`: Self { Self() }
1920
}
2021

21-
/// A type representing an issue reporter that emits "purple" runtime warnings to Xcode and logs
22-
/// fault-level messages to the console.
22+
/// A type representing an issue reporter that emits "purple" runtime warnings and test failures.
2323
///
2424
/// Use ``IssueReporter/runtimeWarning`` to create one of these values.
25-
public struct _RuntimeWarningReporter: IssueReporter {
25+
public struct _DefaultReporter: IssueReporter {
2626
#if canImport(os)
2727
@UncheckedSendable
2828
#if canImport(Darwin)
@@ -64,55 +64,113 @@ public struct _RuntimeWarningReporter: IssueReporter {
6464
filePath: StaticString,
6565
line: UInt,
6666
column: UInt
67+
) {
68+
guard !isTesting else {
69+
_recordIssue(
70+
message: message(),
71+
fileID: "\(fileID)",
72+
filePath: "\(filePath)",
73+
line: Int(line),
74+
column: Int(column)
75+
)
76+
_XCTFail(
77+
message().withAppHostWarningIfNeeded() ?? "",
78+
file: filePath,
79+
line: line
80+
)
81+
return
82+
}
83+
runtimeWarn(message(), fileID: fileID, line: line)
84+
}
85+
86+
@_transparent
87+
public func reportIssue(
88+
_ error: any Error,
89+
_ message: @autoclosure () -> String?,
90+
fileID: StaticString,
91+
filePath: StaticString,
92+
line: UInt,
93+
column: UInt
94+
) {
95+
guard !isTesting else {
96+
_recordError(
97+
error: error,
98+
message: message(),
99+
fileID: "\(fileID)",
100+
filePath: "\(filePath)",
101+
line: Int(line),
102+
column: Int(column)
103+
)
104+
_XCTFail(
105+
"Caught error: \(error)\(message().map { ": \($0)" } ?? "")".withAppHostWarningIfNeeded(),
106+
file: filePath,
107+
line: line
108+
)
109+
return
110+
}
111+
runtimeWarn(
112+
"Caught error: \(error)\(message().map { ": \($0)" } ?? "")",
113+
fileID: fileID,
114+
line: line
115+
)
116+
}
117+
118+
public func expectIssue(
119+
_ message: @autoclosure () -> String?,
120+
fileID: StaticString,
121+
filePath: StaticString,
122+
line: UInt,
123+
column: UInt
67124
) {
68125
#if canImport(os)
69-
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1"
70-
else {
71-
print("🟣 \(fileID):\(line): \(message() ?? "")")
72-
return
73-
}
74126
let moduleName = String(
75127
Substring("\(fileID)".utf8.prefix(while: { $0 != UTF8.CodeUnit(ascii: "/") }))
76128
)
77129
var message = message() ?? ""
78130
if message.isEmpty {
79-
message = "Issue reported"
131+
message = "Issue expected"
80132
}
81133
os_log(
82-
.fault,
83-
dso: dso,
84-
log: OSLog(subsystem: "com.apple.runtime-issues", category: moduleName),
134+
.info,
135+
log: OSLog(subsystem: "co.pointfree.expected-issues", category: moduleName),
85136
"%@",
86137
"\(isTesting ? "\(fileID):\(line): " : "")\(message)"
87138
)
88139
#else
89-
printError("\(fileID):\(line): \(message() ?? "")")
140+
print("\(fileID):\(line): \(message() ?? "")")
90141
#endif
91142
}
92143

93-
public func expectIssue(
144+
@_transparent
145+
@inlinable
146+
func runtimeWarn(
94147
_ message: @autoclosure () -> String?,
95148
fileID: StaticString,
96-
filePath: StaticString,
97-
line: UInt,
98-
column: UInt
149+
line: UInt
99150
) {
100151
#if canImport(os)
152+
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1"
153+
else {
154+
print("🟣 \(fileID):\(line): \(message() ?? "")")
155+
return
156+
}
101157
let moduleName = String(
102158
Substring("\(fileID)".utf8.prefix(while: { $0 != UTF8.CodeUnit(ascii: "/") }))
103159
)
104160
var message = message() ?? ""
105161
if message.isEmpty {
106-
message = "Issue expected"
162+
message = "Issue reported"
107163
}
108164
os_log(
109-
.info,
110-
log: OSLog(subsystem: "co.pointfree.expected-issues", category: moduleName),
165+
.fault,
166+
dso: dso,
167+
log: OSLog(subsystem: "com.apple.runtime-issues", category: moduleName),
111168
"%@",
112169
"\(isTesting ? "\(fileID):\(line): " : "")\(message)"
113170
)
114171
#else
115-
print("\(fileID):\(line): \(message() ?? "")")
172+
printError("\(fileID):\(line): \(message() ?? "")")
116173
#endif
174+
117175
}
118176
}

Sources/IssueReporting/ReportIssue.swift

Lines changed: 44 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -34,50 +34,35 @@ public func reportIssue(
3434
line: UInt = #line,
3535
column: UInt = #column
3636
) {
37+
guard !IssueReporters.current.isEmpty else { return }
3738
let (fileID, filePath, line, column) = (
3839
IssueContext.current?.fileID ?? fileID,
3940
IssueContext.current?.filePath ?? filePath,
4041
IssueContext.current?.line ?? line,
4142
IssueContext.current?.column ?? column
4243
)
43-
guard TestContext.current != nil else {
44-
guard !isTesting else { return }
45-
if let observer = FailureObserver.current {
46-
observer.withLock { $0 += 1 }
47-
for reporter in IssueReporters.current {
48-
reporter.expectIssue(
49-
message(),
50-
fileID: fileID,
51-
filePath: filePath,
52-
line: line,
53-
column: column
54-
)
55-
}
56-
} else {
57-
for reporter in IssueReporters.current {
58-
reporter.reportIssue(
59-
message(),
60-
fileID: fileID,
61-
filePath: filePath,
62-
line: line,
63-
column: column
64-
)
65-
}
44+
if let observer = FailureObserver.current {
45+
observer.withLock { $0 += 1 }
46+
for reporter in IssueReporters.current {
47+
reporter.expectIssue(
48+
message(),
49+
fileID: fileID,
50+
filePath: filePath,
51+
line: line,
52+
column: column
53+
)
54+
}
55+
} else {
56+
for reporter in IssueReporters.current {
57+
reporter.reportIssue(
58+
message(),
59+
fileID: fileID,
60+
filePath: filePath,
61+
line: line,
62+
column: column
63+
)
6664
}
67-
return
6865
}
69-
_recordIssue(
70-
message: message(),
71-
fileID: "\(fileID)",
72-
filePath: "\(filePath)",
73-
line: Int(line),
74-
column: Int(column)
75-
)
76-
_XCTFail(
77-
message().withAppHostWarningIfNeeded() ?? "",
78-
file: filePath,
79-
line: line
80-
)
8166
}
8267

8368
/// Report a caught error.
@@ -101,57 +86,35 @@ public func reportIssue(
10186
line: UInt = #line,
10287
column: UInt = #column
10388
) {
89+
guard !IssueReporters.current.isEmpty else { return }
10490
let (fileID, filePath, line, column) = (
10591
IssueContext.current?.fileID ?? fileID,
10692
IssueContext.current?.filePath ?? filePath,
10793
IssueContext.current?.line ?? line,
10894
IssueContext.current?.column ?? column
10995
)
110-
guard let context = TestContext.current else {
111-
guard !isTesting else { return }
112-
if let observer = FailureObserver.current {
113-
observer.withLock { $0 += 1 }
114-
for reporter in IssueReporters.current {
115-
reporter.expectIssue(
116-
error,
117-
message(),
118-
fileID: fileID,
119-
filePath: filePath,
120-
line: line,
121-
column: column
122-
)
123-
}
124-
} else {
125-
for reporter in IssueReporters.current {
126-
reporter.reportIssue(
127-
error,
128-
message(),
129-
fileID: fileID,
130-
filePath: filePath,
131-
line: line,
132-
column: column
133-
)
134-
}
96+
if let observer = FailureObserver.current {
97+
observer.withLock { $0 += 1 }
98+
for reporter in IssueReporters.current {
99+
reporter.expectIssue(
100+
error,
101+
message(),
102+
fileID: fileID,
103+
filePath: filePath,
104+
line: line,
105+
column: column
106+
)
107+
}
108+
} else {
109+
for reporter in IssueReporters.current {
110+
reporter.reportIssue(
111+
error,
112+
message(),
113+
fileID: fileID,
114+
filePath: filePath,
115+
line: line,
116+
column: column
117+
)
135118
}
136-
return
137-
}
138-
139-
switch context {
140-
case .swiftTesting:
141-
_recordError(
142-
error: error,
143-
message: message(),
144-
fileID: "\(fileID)",
145-
filePath: "\(filePath)",
146-
line: Int(line),
147-
column: Int(column)
148-
)
149-
case .xcTest:
150-
_XCTFail(
151-
"Caught error: \(error)\(message().map { ": \($0)" } ?? "")".withAppHostWarningIfNeeded(),
152-
file: filePath,
153-
line: line
154-
)
155-
@unknown default: break
156119
}
157120
}

0 commit comments

Comments
 (0)