fix(ios): the runner must not answer ok for a launch that failed

XCTest records a refused launch as a test issue that never throws, so the
companion returned ok for an app that never started. Check the state the
app actually reached and report the refusal instead.
This commit is contained in:
pj committed 2026-08-15 20:49:31 +05:30
1 parent 0929ae54bf
commit 73736ab26f
1 file changed
+27
+27
View File
@@ -9,6 +9,7 @@ enum AppLifecycle {
} }
static func launch(bundleIdentifier: String, foregroundIfRunning: Bool) throws { static func launch(bundleIdentifier: String, foregroundIfRunning: Bool) throws {
var reached = XCUIApplication.State.unknown
try onMainCatching { try onMainCatching {
let application = XCUIApplication(bundleIdentifier: bundleIdentifier) let application = XCUIApplication(bundleIdentifier: bundleIdentifier)
if foregroundIfRunning { if foregroundIfRunning {
@@ -18,6 +19,17 @@ enum AppLifecycle {
} else { } else {
application.launch() application.launch()
} }
reached = application.state
}
// A refused launch is recorded as a test issue that never throws, so
// without this the runner answers ok for an app that is not running
// and the host learns nothing until its own bound expires.
switch reached {
case .runningForeground, .runningBackground, .runningBackgroundSuspended:
return
default:
throw LifecycleError.failed(
"\(bundleIdentifier) is \(name(of: reached)) after launch")
} }
} }
@@ -54,6 +66,21 @@ enum AppLifecycle {
return result return result
} }
private static func name(of state: XCUIApplication.State) -> String {
switch state {
case .runningForeground:
return "foreground"
case .runningBackground:
return "background"
case .runningBackgroundSuspended:
return "suspended"
case .notRunning:
return "not running"
default:
return "unknown"
}
}
// onMainCatching runs automation work on the main thread and converts a // onMainCatching runs automation work on the main thread and converts a
// framework assertion into a thrown error so the server survives it. // framework assertion into a thrown error so the server survives it.
private static func onMainCatching(_ work: @escaping () -> Void) throws { private static func onMainCatching(_ work: @escaping () -> Void) throws {