Two navigation helpers in the iOS runner accept CGRect.infinite as a valid frame. One of them turns it into a tap point at roughly -9e307; the other classifies it as sitting in the navigation header band.
Found by the first execution of the dark XCTest set (#1781 A7, PR #1789). Both tests live outside ios.yml's hand-written -only-testing: list, so nothing had ever run them.
Failures
Reproduced on Xcode 26.2 / iOS 26.2 Simulator (iPhone 16), full suite, 154 executed / 152 passed / 2 failed:
testTopLeadingNavigationFallbackPointRejectsInvalidFrame()
XCTAssertNil failed: "(-8.988465674311579e+307, -8.988465674311579e+307)"
testTopNavigationControlFrameAcceptsOnlyHeaderBand()
XCTAssertFalse failed
Both in apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift (tests at lines 185 and 205).
Root cause: the guards validate the size, never the origin
CGRect.infinite is { {-CGFLOAT_MAX/2, -CGFLOAT_MAX/2}, {CGFLOAT_MAX, CGFLOAT_MAX} }. Its width and height are CGFLOAT_MAX — finite and positive — so a size-only validity check waves it straight through. The absurd part of the rect is its origin, which neither helper looks at.
topLeadingNavigationFallbackPoint (line 102):
guard frame.width.isFinite, frame.height.isFinite, frame.width > 0, frame.height > 0 else {
return nil
}
let xOffset = min(max(frame.width * 0.08, 28), 44)
return CGPoint(x: frame.minX + xOffset, y: frame.minY + yOffset)
With .infinite: the guard passes, xOffset clamps to 44, and frame.minX + 44 is -8.988465674311579e+307 — the 44 is entirely lost to floating-point precision. That is the exact value in the assertion output.
isTopNavigationControlFrame (line 84) has the same shape of guard, then:
let maxY = window.minY + min(max(window.height * 0.22, 96), 180) // = 180 for a 430x932 window
return candidate.midY >= window.minY && candidate.midY <= maxY
CGRect.infinite.midY is -CGFLOAT_MAX/2 + CGFLOAT_MAX/2 = exactly 0.0, which lands inside [0, 180] → returns true.
The .zero half of the first test passes, because width > 0 does reject it. .infinite is the only invalid rect that slips through.
Impact
tapTopLeadingNavigationFallback feeds onScreenWindowFrame(app:) straight into topLeadingNavigationFallbackPoint. If that frame ever comes back as .infinite — an unresolved or not-yet-laid-out window — the runner synthesizes a tap at approximately (-9e307, -9e307) instead of declining to act. isTopNavigationControlFrame fails in the same direction: an unresolved element frame is treated as a top-navigation control.
Both are silent wrong-decision paths, not crashes.
Suggested fix
There is already a correct precedent in the same target — TapPointPolicy.isAllowed in apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift:
if windowFrame.isNull || windowFrame.isEmpty || windowFrame.isInfinite {
isInfinite is the API that detects exactly this rect. The navigation helpers should apply the same three-way check rather than a fourth hand-rolled spelling of "is this rect usable".
Worth extracting one shared validity predicate so there is a single definition, then checking whether any sibling geometry helper in the runner has the same size-only gap. Fix the helpers, not the tests — the assertions describe the intended contract, and the test names (RejectsInvalidFrame, AcceptsOnlyHeaderBand) say so.
Verifying
Needs a full-suite run; the PR lane's -only-testing: list does not include either test.
AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 \
AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH=<scratch> \
AGENT_DEVICE_XCUITEST_DESTINATION='generic/platform=iOS Simulator' \
pnpm gate swift-runner-ios
xcodebuild test-without-building \
-xctestrun <scratch>/Build/Products/*.xctestrun \
-destination "platform=iOS Simulator,id=<udid>" \
-skip-testing:AgentDeviceRunnerUITests/RunnerTests/testCommand \
-resultBundlePath <scratch>/RunnerTests.xcresult
-skip-testing: on testCommand is required — it is the runner's server entry point and blocks for 24 hours.
Once #1789 lands, the XCTest Nightly lane runs this set every night and will report both failures until they are fixed.
Two navigation helpers in the iOS runner accept
CGRect.infiniteas a valid frame. One of them turns it into a tap point at roughly-9e307; the other classifies it as sitting in the navigation header band.Found by the first execution of the dark XCTest set (#1781 A7, PR #1789). Both tests live outside
ios.yml's hand-written-only-testing:list, so nothing had ever run them.Failures
Reproduced on Xcode 26.2 / iOS 26.2 Simulator (iPhone 16), full suite, 154 executed / 152 passed / 2 failed:
Both in
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift(tests at lines 185 and 205).Root cause: the guards validate the size, never the origin
CGRect.infiniteis{ {-CGFLOAT_MAX/2, -CGFLOAT_MAX/2}, {CGFLOAT_MAX, CGFLOAT_MAX} }. Its width and height areCGFLOAT_MAX— finite and positive — so a size-only validity check waves it straight through. The absurd part of the rect is its origin, which neither helper looks at.topLeadingNavigationFallbackPoint(line 102):With
.infinite: the guard passes,xOffsetclamps to 44, andframe.minX + 44is-8.988465674311579e+307— the 44 is entirely lost to floating-point precision. That is the exact value in the assertion output.isTopNavigationControlFrame(line 84) has the same shape of guard, then:CGRect.infinite.midYis-CGFLOAT_MAX/2 + CGFLOAT_MAX/2= exactly 0.0, which lands inside[0, 180]→ returnstrue.The
.zerohalf of the first test passes, becausewidth > 0does reject it..infiniteis the only invalid rect that slips through.Impact
tapTopLeadingNavigationFallbackfeedsonScreenWindowFrame(app:)straight intotopLeadingNavigationFallbackPoint. If that frame ever comes back as.infinite— an unresolved or not-yet-laid-out window — the runner synthesizes a tap at approximately(-9e307, -9e307)instead of declining to act.isTopNavigationControlFramefails in the same direction: an unresolved element frame is treated as a top-navigation control.Both are silent wrong-decision paths, not crashes.
Suggested fix
There is already a correct precedent in the same target —
TapPointPolicy.isAllowedinapple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift:isInfiniteis the API that detects exactly this rect. The navigation helpers should apply the same three-way check rather than a fourth hand-rolled spelling of "is this rect usable".Worth extracting one shared validity predicate so there is a single definition, then checking whether any sibling geometry helper in the runner has the same size-only gap. Fix the helpers, not the tests — the assertions describe the intended contract, and the test names (
RejectsInvalidFrame,AcceptsOnlyHeaderBand) say so.Verifying
Needs a full-suite run; the PR lane's
-only-testing:list does not include either test.-skip-testing:ontestCommandis required — it is the runner's server entry point and blocks for 24 hours.Once #1789 lands, the XCTest Nightly lane runs this set every night and will report both failures until they are fixed.