fix(ioscompanion): retry describe past transient collapsed accessibility dumps

This commit is contained in:
pj committed 2026-06-07 11:46:09 +05:30
1 parent 8b7a51c5b5
commit a745ec7938
2 files changed
+44 -1

No files matched your search

+30 -1
View File
@@ -660,8 +660,18 @@ func (d *Driver) ForegroundApp(ctx context.Context) (string, error) {
return other, nil
}
// describeAll fetches the flat accessibility dump with one-restart recovery.
// collapsedDumpRetries and collapsedDumpDelay bound how long describeAll waits
// out a collapsed accessibility dump. The bridge briefly reports only the app
// shell (no UI content) during cold start and screen transitions; it recovers
// within a few hundred milliseconds. Re-fetching past the collapse keeps the
// runner from acting on, and snapshotting, an empty tree.
const collapsedDumpRetries = 6
const collapsedDumpDelay = 150 * time.Millisecond
// describeAll fetches the flat accessibility dump with one-restart recovery,
// retrying past a transient collapsed dump so callers see real UI content.
func (d *Driver) describeAll(ctx context.Context) ([]byte, error) {
fetch := func() ([]byte, error) {
var dump []byte
err := d.withRecovery(ctx, func() error {
info, infoErr := d.companion.AccessibilityInfo(ctx)
@@ -672,6 +682,25 @@ func (d *Driver) describeAll(ctx context.Context) ([]byte, error) {
return nil
})
return dump, err
}
dump, err := fetch()
if err != nil {
return dump, err
}
for attempt := 0; attempt < collapsedDumpRetries && dumpIsCollapsed(dump); attempt++ {
select {
case <-ctx.Done():
return dump, nil
case <-time.After(collapsedDumpDelay):
}
next, nextErr := fetch()
if nextErr != nil {
return dump, nil
}
dump = next
}
return dump, nil
}
// makeRunner builds the input runner backed by the current transport. The text
@@ -19,6 +19,20 @@ type rawFrame struct {
// it maps to an empty value rather than surfacing as literal field text.
const emptyFieldValueSentinel = "Invalid"
// dumpIsCollapsed reports whether a flat describe-all dump carries no real UI
// content: it is empty or holds only the application shell. The accessibility
// bridge briefly returns this state during cold start and screen transitions
// before the real tree reappears.
func dumpIsCollapsed(dump []byte) bool {
elements := decodeDump(dump)
for _, element := range elements {
if element.Type != "" && element.Type != "Application" {
return false
}
}
return true
}
// rawElement is one entry in the flat describe-all dump returned by the
// simulator companion. Only the fields the mapper consumes are declared;
// unknown fields are ignored.