Repository navigation
Conversation
…bject The runner process hosts no scene, so UIKit's UIScreen answers a placeholder panel there — 960x1440 @3x (320x480 points) on the nightly's iPhone 17 Pro — while the route gate and the capture path read the real XCUIScreen. A reference frame derived from UIScreen.main therefore aimed the window-less back fallback at (28, 56): the status bar, above the 62-106pt bar band the fallback exists to hit. The tap changed nothing, both callstack#3894 nightly tests failed, and the windows route was skipped because the placeholder sole screen 'answered'. Probe evidence on this simulator (logged in the PR description): UIScreen.main.bounds = 320x480, XCUIScreen KVC bounds = 1206x2622 with KVC scale 3, and a public XCUIScreen screenshot at {402,874} @3x. The bridge grows RunnerResolveSoleScreenPanelGeometry: the same sole-screen rule as the routing resolver, fail-closed through the exception catcher with the shared APP_SCREEN_UNRESOLVED log, so a future Xcode that drops either private key refuses the route instead of aiming blind. The gestureViewport unit test now pins the route answer to that resolver, and a new test cross-checks the resolver against the public capture of the same screen.
There was a problem hiding this comment.
3 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h">
<violation number="1" location="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h:101">
P3: The new out-parameter is named as points-per-pixel, but it contains pixels-per-point and callers divide by it. Rename it to `pixelsPerPointScale` and update the comment so future callers do not invert the conversion.</violation>
</file>
<file name="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m">
<violation number="1" location="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m:286">
P2: The resolver accepts NaN or infinite scale and dimensions because it checks only `<= 0`, then reports success with invalid geometry. Reject all non-finite values with `isfinite` before returning success.</violation>
</file>
<file name="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+SynthesizedInteractionTests.swift">
<violation number="1" location="apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+SynthesizedInteractionTests.swift:133">
P2: These expectations ignore the orientation transformation applied by `soleScreenReferenceFrame`, so the test fails whenever the device is landscape even though the route returns the correctly oriented viewport. Build the expected frame with the app's interface orientation before comparing both dimensions.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| readSize = bounds.size; | ||
| readScale = [[screens.firstObject valueForKey:@"scale"] doubleValue]; | ||
| }]; | ||
| if (exception != nil || readScale <= 0 || readSize.width <= 0 || readSize.height <= 0) { |
There was a problem hiding this comment.
P2: The resolver accepts NaN or infinite scale and dimensions because it checks only <= 0, then reports success with invalid geometry. Reject all non-finite values with isfinite before returning success.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m, line 286:
<comment>The resolver accepts NaN or infinite scale and dimensions because it checks only `<= 0`, then reports success with invalid geometry. Reject all non-finite values with `isfinite` before returning success.</comment>
<file context>
@@ -259,6 +259,45 @@ BOOL RunnerResolveApplicationScreen(
+ readSize = bounds.size;
+ readScale = [[screens.firstObject valueForKey:@"scale"] doubleValue];
+ }];
+ if (exception != nil || readScale <= 0 || readSize.width <= 0 || readSize.height <= 0) {
+ NSLog(
+ @"AGENT_DEVICE_RUNNER_APP_SCREEN_UNRESOLVED stage=sole-screen-geometry size=(%g,%g) scale=%g raised=%@",
</file context>
| if (exception != nil || readScale <= 0 || readSize.width <= 0 || readSize.height <= 0) { | |
| if (exception != nil || !isfinite(readScale) || readScale <= 0 || !isfinite(readSize.width) || readSize.width <= 0 || !isfinite(readSize.height) || readSize.height <= 0) { |
| XCTAssertEqual(response.data?.y, 0) | ||
| XCTAssertEqual(response.data?.x2, Double(screen.width)) | ||
| XCTAssertEqual(response.data?.y2, Double(screen.height)) | ||
| XCTAssertEqual(response.data?.x2, pixelSize.width / scale) |
There was a problem hiding this comment.
P2: These expectations ignore the orientation transformation applied by soleScreenReferenceFrame, so the test fails whenever the device is landscape even though the route returns the correctly oriented viewport. Build the expected frame with the app's interface orientation before comparing both dimensions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/UnitTests/RunnerTests+SynthesizedInteractionTests.swift, line 133:
<comment>These expectations ignore the orientation transformation applied by `soleScreenReferenceFrame`, so the test fails whenever the device is landscape even though the route returns the correctly oriented viewport. Build the expected frame with the app's interface orientation before comparing both dimensions.</comment>
<file context>
@@ -121,11 +121,17 @@ extension RunnerTests {
XCTAssertEqual(response.data?.y, 0)
- XCTAssertEqual(response.data?.x2, Double(screen.width))
- XCTAssertEqual(response.data?.y2, Double(screen.height))
+ XCTAssertEqual(response.data?.x2, pixelSize.width / scale)
+ XCTAssertEqual(response.data?.y2, pixelSize.height / scale)
} else {
</file context>
| // are guarded here and surface as a message instead of an exception reaching the caller. | ||
| FOUNDATION_EXPORT NSString * _Nullable RunnerResolveSoleScreenPanelGeometry( | ||
| CGSize *pixelSize, | ||
| CGFloat *pointsPerPixelScale |
There was a problem hiding this comment.
P3: The new out-parameter is named as points-per-pixel, but it contains pixels-per-point and callers divide by it. Rename it to pixelsPerPointScale and update the comment so future callers do not invert the conversion.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h, line 101:
<comment>The new out-parameter is named as points-per-pixel, but it contains pixels-per-point and callers divide by it. Rename it to `pixelsPerPointScale` and update the comment so future callers do not invert the conversion.</comment>
<file context>
@@ -90,6 +90,17 @@ FOUNDATION_EXPORT NSString * _Nullable RunnerResolveSoleScreen(
+// are guarded here and surface as a message instead of an exception reaching the caller.
+FOUNDATION_EXPORT NSString * _Nullable RunnerResolveSoleScreenPanelGeometry(
+ CGSize *pixelSize,
+ CGFloat *pointsPerPixelScale
+);
+
</file context>
Root cause
testInAppBackVerifiesSoleScreenWhenXCTestCannotReadFramesOrEnumerateandtestInAppBackStillAsksForWindowsWhereNoSoleScreenAnswershave failed the nightly since 10-23 (#3943), the night after #3894 landed — the full-suite lane is the only place those two execute.The UI-test runner process hosts no scene, so UIKit's
UIScreenanswers a placeholder panel there. Probe on the nightly's device shape (iPhone 17 Pro, iOS 26.5 simulator):UIScreen.main.bounds→ 320×480 points (nativeBounds960×1440 @3x)XCUIScreenprivatebounds→ 1206×2622 px with privatescale3XCUIScreen.screenshot()→ {402, 874} @3x — the real panelsoleScreenReferenceFramederived its frame fromUIScreen.main, so the window-less back fallback aimed at (28, 56) — the status bar, above the 62–106 pt bar band the fallback exists to hit. The tap changed nothing; the--agent-device-back-regressionstatus stayed "launched". The placeholder sole screen also "answered", so the windows route was skipped, which is why the windows-routed test failed too.Nightly evidence:
AGENT_DEVICE_RUNNER_SYNTHESIZED_SOLE_SCREEN_ROUTE resolved={{0, 0}, {320, 480}}, thenIN_APP_BACK_VISUAL_VERIFICATION … changed=nobefore #3894's assertions failed.Fix
The bridge grows
RunnerResolveSoleScreenPanelGeometry: the same sole-screen rule as the routing resolver, measuring the panel on the XCTest screen object the route gate already trusts, fail-closed through the exception catcher with the sharedAPP_SCREEN_UNRESOLVEDlog — a future Xcode that drops either private key refuses the route instead of aiming blind.soleScreenReferenceFramenow measures through that resolver;UIScreen.mainis out of the gesture path.testGestureViewportOnAnUnserializableTreeIsTheSoleScreenpins the route answer to the resolver instead of the placeholder panel.testSoleScreenPanelGeometryMatchesTheCapturedPanelcross-checks the resolver against the one public panel answer in the process (a capture of the same screen).Verification
Red first, on this simulator against
ed798f9cf: both #3894 tests failed with the same signature as the nightly (resolved={{0,0},{320,480}}, 2 failures / 2 tests). After the fix, 10/10 sole-screen and navigation tests pass, including the two previously-red ones (resolved={{0,0},{402,874}},changed=yeson the regression fixture, status "Back performed").Fixes #3943