-
Notifications
You must be signed in to change notification settings - Fork 78
/
Copy pathReportIssue.swift
155 lines (152 loc) · 5.47 KB
/
ReportIssue.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/// Report an issue.
///
/// Invoking this function has two different behaviors depending on the context:
///
/// * When running your code in a non-testing context, this method will loop over the
/// collection of issue reports registered and invoke them. The default issue reporter for the
/// library is ``IssueReporter/runtimeWarning``, which emits a purple, runtime warning in Xcode:
///
/// 
///
/// But you can there are also [other issue reports](<doc:GettingStarted#Issue-reporters>) you
/// can use, and you can create your own.
///
/// * When running your app in tests (both XCTest and Swift's native Testing framework), it will
/// emit a test failure. This allows you to get test coverage on your reported issues, both expected
/// and unexpected ones.
///
/// 
///
/// [Issue.record]: https://developer.apple.com/documentation/testing/issue/record(_:sourcelocation:)
/// [XCTFail]: https://developer.apple.com/documentation/xctest/1500970-xctfail/
///
/// - Parameters:
/// - message: A message describing the issue.
/// - fileID: The source `#fileID` associated with the issue.
/// - filePath: The source `#filePath` associated with the issue.
/// - line: The source `#line` associated with the issue.
/// - column: The source `#column` associated with the issue.
@_transparent
public func reportIssue(
_ message: @autoclosure () -> String? = nil,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column
) {
guard let context = TestContext.current else {
guard !isTesting else { return }
if let observer = FailureObserver.current {
observer.withLock { $0 += 1 }
for reporter in IssueReporters.current {
reporter.expectIssue(
message(),
fileID: IssueContext.current?.fileID ?? fileID,
filePath: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line,
column: IssueContext.current?.column ?? column
)
}
} else {
for reporter in IssueReporters.current {
reporter.reportIssue(
message(),
fileID: IssueContext.current?.fileID ?? fileID,
filePath: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line,
column: IssueContext.current?.column ?? column
)
}
}
return
}
if TestContext.emitsFailureOnReportIssue {
switch context {
case .swiftTesting:
_recordIssue(
message: message(),
fileID: "\(IssueContext.current?.fileID ?? fileID)",
filePath: "\(IssueContext.current?.filePath ?? filePath)",
line: Int(IssueContext.current?.line ?? line),
column: Int(IssueContext.current?.column ?? column)
)
case .xcTest:
_XCTFail(
message().withAppHostWarningIfNeeded() ?? "",
file: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line
)
@unknown default: break
}
}
}
/// Report a caught error.
///
/// This function behaves similarly to ``reportIssue(_:fileID:filePath:line:column:)``, but for
/// reporting errors.
///
/// - Parameters:
/// - error: The error that caused the issue.
/// - message: A message describing the expectation.
/// - fileID: The source `#fileID` associated with the issue.
/// - filePath: The source `#filePath` associated with the issue.
/// - line: The source `#line` associated with the issue.
/// - column: The source `#column` associated with the issue.
@_transparent
public func reportIssue(
_ error: any Error,
_ message: @autoclosure () -> String? = nil,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column
) {
guard let context = TestContext.current else {
guard !isTesting else { return }
if let observer = FailureObserver.current {
observer.withLock { $0 += 1 }
for reporter in IssueReporters.current {
reporter.expectIssue(
error,
message(),
fileID: IssueContext.current?.fileID ?? fileID,
filePath: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line,
column: IssueContext.current?.column ?? column
)
}
} else {
for reporter in IssueReporters.current {
reporter.reportIssue(
error,
message(),
fileID: IssueContext.current?.fileID ?? fileID,
filePath: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line,
column: IssueContext.current?.column ?? column
)
}
}
return
}
if TestContext.emitsFailureOnReportIssue {
switch context {
case .swiftTesting:
_recordError(
error: error,
message: message(),
fileID: "\(IssueContext.current?.fileID ?? fileID)",
filePath: "\(IssueContext.current?.filePath ?? filePath)",
line: Int(IssueContext.current?.line ?? line),
column: Int(IssueContext.current?.column ?? column)
)
case .xcTest:
_XCTFail(
"Caught error: \(error)\(message().map { ": \($0)" } ?? "")".withAppHostWarningIfNeeded(),
file: IssueContext.current?.filePath ?? filePath,
line: IssueContext.current?.line ?? line
)
@unknown default: break
}
}
}