Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions Sources/Testing/ExitTests/ExitStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,52 @@ public enum ExitStatus: Sendable {
extension ExitStatus: Equatable {}

// MARK: - CustomStringConvertible
@_spi(Experimental)

#if os(Linux)
/// Get the short name of a signal constant.
///
/// This function declaration is provided because `sigabbrev_np()` is only
/// declared if `_GNU_SOURCE` is set, but setting it causes build errors due to
/// conflicts with Swift's Glibc module.
private let _sigabbrev_np = symbol(named: "sigabbrev_np").map {
castCFunction(at: $0, to: (@convention(c) (CInt) -> UnsafePointer<CChar>?).self)
}
#endif

#if SWT_NO_PROCESS_SPAWNING
@available(*, unavailable, message: "Exit tests are not available on this platform.")
#endif
extension ExitStatus: CustomStringConvertible {
public var description: String {
switch self {
case let .exitCode(exitCode):
".exitCode(\(exitCode))"
return ".exitCode(\(exitCode))"
case let .signal(signal):
".signal(\(signal))"
var signalName: String?
#if SWT_TARGET_OS_APPLE || os(FreeBSD) || os(OpenBSD) || os(Android)
// These platforms define sys_signame with a size, which is imported
// into Swift as a tuple.
withUnsafeBytes(of: sys_signame) { sys_signame in
sys_signame.withMemoryRebound(to: UnsafePointer<CChar>.self) { sys_signame in
if signal > 0 && signal < sys_signame.count {
signalName = String(validatingCString: sys_signame[Int(signal)])
}
}
}
#elseif os(Linux)
signalName = _sigabbrev_np?(signal)
.flatMap(String.init(validatingCString:))
#elseif os(Windows) || os(WASI)
// These platforms do not have API to get the programmatic name of a
// signal constant.
#else
#warning("Platform-specific implementation missing: signal names unavailable")
#endif

if let signalName {
return ".signal(SIG\(signalName.uppercased()))"
}
return ".signal(\(signal))"
}
}
}
9 changes: 9 additions & 0 deletions Tests/TestingTests/ExitTestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ private import _TestingInternals

#if !SWT_NO_EXIT_TESTS
@Suite("Exit test tests") struct ExitTestTests {
@Test("Signal names are reported (where supported)") func signalName() {
let exitStatus = ExitStatus.signal(SIGABRT)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android)
#expect(String(describing: exitStatus) == ".signal(SIGABRT)")
#else
#expect(String(describing: exitStatus) == ".signal(\(SIGABRT))")
#endif
}

@Test("Exit tests (passing)") func passing() async {
await #expect(processExitsWith: .failure) {
exit(EXIT_FAILURE)
Expand Down