Files
2026-07-13 12:22:33 +08:00

77 lines
2.3 KiB
Swift

import Foundation
#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif
enum TestProcessCleanup {
static let codexTestStubCommandRegex = [
#"codex-(stub|fallback-stub|plan-only-stub|credits-only-stub|hung-stub)-"#,
#"[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}"#,
#"[[:space:]]+app-server([[:space:]]|$)"#,
].joined()
static func register() {
atexit(_testProcessCleanupAtExit)
}
fileprivate static func terminateLeakedCodexTestStubs() {
// Never target an installed Codex process. These UUID-named executables are created only by this test target.
let pids = Self.pids(matchingFullCommandRegex: Self.codexTestStubCommandRegex)
.filter { $0 > 0 && $0 != getpid() }
guard !pids.isEmpty else { return }
for pid in pids {
_ = kill(pid, SIGTERM)
}
let deadline = Date().addingTimeInterval(0.6)
while Date() < deadline {
let stillRunning = pids.contains(where: { kill($0, 0) == 0 })
if !stillRunning { return }
usleep(50000)
}
for pid in pids where kill(pid, 0) == 0 {
_ = kill(pid, SIGKILL)
}
}
private static func pids(matchingFullCommandRegex regex: String) -> [pid_t] {
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/usr/bin/env")
proc.arguments = ["pgrep", "-f", regex]
let stdout = Pipe()
proc.standardOutput = stdout
proc.standardError = Pipe()
proc.standardInput = nil
do {
try proc.run()
} catch {
return []
}
proc.waitUntilExit()
// Exit code 1 = "no processes matched".
if proc.terminationStatus != 0 { return [] }
let data = stdout.fileHandleForReading.readDataToEndOfFile()
guard let text = String(data: data, encoding: .utf8) else { return [] }
return text
.split(whereSeparator: \.isNewline)
.compactMap { Int32($0) }
.map { pid_t($0) }
}
}
private let _registerTestProcessCleanup: Void = TestProcessCleanup.register()
@_cdecl("codexbar_test_cleanup_atexit")
private func _testProcessCleanupAtExit() {
TestProcessCleanup.terminateLeakedCodexTestStubs()
}