Repository navigation
fix(ios): read status bars from a throwing snapshot so a keyboard race cannot kill the runner - #6
Merged
Conversation
…e cannot kill the runner
Every view-hierarchy request fetched SpringBoard's status bars with
`statusBars.firstMatch.exists` and `statusBars.allElementsBoundByIndex`.
On iOS 26 one of those status bars is hosted by the keyboard process
(com.apple.InputUI). When the keyboard goes away while the element is
being resolved, XCTest gets kAXErrorInvalidUIElement. These APIs do not
throw: they record a test failure ("Failed to resolve query: Failed to
resolve remote element AX element pid: <InputUI>"), which ends
testHttpServer and the runner exits. The CLI then fails every remaining
flow with "Unable to set permissions ... Failed to connect to 127.0.0.1".
Take one throwing `snapshot()` of SpringBoard and collect the status bar
nodes from it (new AXElement.descendants(ofType:), unit-tested). A race
now throws, is caught, and the hierarchy is returned without status bars.
Verified on an iPhone 17 / iOS 26.5 simulator: the hierarchy has the same
24 nodes as before, with the two status bars in a different order; there
are no StatusBar element queries left; two stress flows (15 login cycles,
and 6 cycles while InputUI was killed 14 times) pass; the status bar step
went from 0.062 s to 0.040 s on average.
Also rebuilds the simulator driver zips, which now include #5 as well.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An iOS 26 simulator run failed 13/13 flows with
Unable to set permissions for app <app>: Failed to connect to /127.0.0.1:<port>. The re-run on a fresh runner failed the same way. This isn't a permissions problem: the XCTest runner had exited.The runner log shows the cause:
com.apple.InputUI, the iOS 26 keyboard process. It was moving to the background at that moment./viewHierarchyrequest runsfullStatusBars(springboard), which callsstatusBars.firstMatch.existsandstatusBars.allElementsBoundByIndex. One of SpringBoard's two status bars ("Element at index 1") is hosted by InputUI.testHttpServer(continueAfterFailuredoesn't help), and the runner exits.This is the same mechanism as the
webViews.countissue fixed in #4, in a different call.Fix
fullStatusBarsnow takes a single throwingsnapshot()of SpringBoard and collects the.statusBarnodes from it.AXElement.descendants(ofType:)in MaestroDriverLib, returns nodes in the same pre-order asallElementsBoundByIndex, including nested matches. It has unit tests.Testing (iPhone 17, iOS 26.5 simulator, Xcode 26.4.1)
swift test: all tests pass, including the 2 new ones.maestro hierarchybefore and after: the same 24 nodes. The only differences are the clock text and the order of the two status bars (the empty InputUI one is now listed second).Descendants matching type StatusBarqueries.tapOn "Not now"→ log out):Failed to resolvein the runner log.Not reproduced: I couldn't trigger the original race locally. The unfixed runner also survived the InputUI-kill stress. The failing run hit it while InputUI was moving to the background, not when it was killed. The fix works by taking the non-throwing APIs out of this code path, not by a repro that now passes.
Deployment note
The simulator hosts that ran the failing job use stock Maestro 2.6.0. Neither this fix nor #4 helps there until the forked runner is deployed to them.