Skip to content

Commit 03ec484

Browse files
authored
Improve ExitStatus.description for signals. (#1302)
This PR uses the `sys_signame` array present on many UNIX-like systems to derive a better description for values of type `ExitStatus` and `ExitTest.Condition`. (On Linux, the equivalent `sigabbrev_np()` is used. Windows and WASI don't have an equivalent API.) Before: ```swift let s = ExitStatus.signal(SIGABRT) print(s) // ".signal(12345)" ``` After: ```swift let s = ExitStatus.signal(SIGABRT) print(s) // ".signal(SIGABRT)" ``` ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 4fc4f3f commit 03ec484

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

Sources/Testing/ExitTests/ExitStatus.swift

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,55 @@ public enum ExitStatus: Sendable {
9696
extension ExitStatus: Equatable {}
9797

9898
// MARK: - CustomStringConvertible
99-
@_spi(Experimental)
99+
100+
#if os(Linux) && !SWT_NO_DYNAMIC_LINKING
101+
/// Get the short name of a signal constant.
102+
///
103+
/// This symbol is provided because the underlying function was added to glibc
104+
/// relatively recently and may not be available on all targets. Checking
105+
/// `__GLIBC_PREREQ()` is insufficient because `_GNU_SOURCE` may not be defined
106+
/// at the point string.h is first included.
107+
private let _sigabbrev_np = symbol(named: "sigabbrev_np").map {
108+
castCFunction(at: $0, to: (@convention(c) (CInt) -> UnsafePointer<CChar>?).self)
109+
}
110+
#endif
111+
100112
#if SWT_NO_PROCESS_SPAWNING
101113
@available(*, unavailable, message: "Exit tests are not available on this platform.")
102114
#endif
103115
extension ExitStatus: CustomStringConvertible {
104116
public var description: String {
105117
switch self {
106118
case let .exitCode(exitCode):
107-
".exitCode(\(exitCode))"
119+
return ".exitCode(\(exitCode))"
108120
case let .signal(signal):
109-
".signal(\(signal))"
121+
var signalName: String?
122+
123+
#if SWT_TARGET_OS_APPLE || os(FreeBSD) || os(OpenBSD) || os(Android)
124+
// These platforms define sys_signame with a size, which is imported
125+
// into Swift as a tuple.
126+
withUnsafeBytes(of: sys_signame) { sys_signame in
127+
sys_signame.withMemoryRebound(to: UnsafePointer<CChar>.self) { sys_signame in
128+
if signal > 0 && signal < sys_signame.count {
129+
signalName = String(validatingCString: sys_signame[Int(signal)])?.uppercased()
130+
}
131+
}
132+
}
133+
#elseif os(Linux)
134+
#if !SWT_NO_DYNAMIC_LINKING
135+
signalName = _sigabbrev_np?(signal).flatMap(String.init(validatingCString:))
136+
#endif
137+
#elseif os(Windows) || os(WASI)
138+
// These platforms do not have API to get the programmatic name of a
139+
// signal constant.
140+
#else
141+
#warning("Platform-specific implementation missing: signal names unavailable")
142+
#endif
143+
144+
if let signalName {
145+
return ".signal(SIG\(signalName)\(signal))"
146+
}
147+
return ".signal(\(signal))"
110148
}
111149
}
112150
}

Tests/TestingTests/ExitTestTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ private import _TestingInternals
1313

1414
#if !SWT_NO_EXIT_TESTS
1515
@Suite("Exit test tests") struct ExitTestTests {
16+
@Test("Signal names are reported (where supported)") func signalName() {
17+
let exitStatus = ExitStatus.signal(SIGABRT)
18+
#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)
19+
#expect(String(describing: exitStatus) == ".signal(SIGABRT → \(SIGABRT))")
20+
#else
21+
#expect(String(describing: exitStatus) == ".signal(\(SIGABRT))")
22+
#endif
23+
}
24+
1625
@Test("Exit tests (passing)") func passing() async {
1726
await #expect(processExitsWith: .failure) {
1827
exit(EXIT_FAILURE)

0 commit comments

Comments
 (0)