From fb6c3ca517f515a9967b9ab89b069930ca36766a Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 18 Apr 2026 01:59:41 +0700 Subject: [PATCH] fix(runner): fetch hierarchy before SDK pause to avoid stale uiautomator dumps --- internal/runner/runner.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 128d24b..dcb0d63 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -57,20 +57,29 @@ func Run(ctx context.Context, options Options) (Summary, error) { stepIndex++ stepStart := time.Now() + // Fetch hierarchy BEFORE pausing the SDK: uiautomator dump calls + // waitForIdle internally, and the SDK's Choreographer-held pause + // stalls the main thread, so dumping during the pause yields the + // pre-pause (stale) tree. Doing this first also means the spec sees + // a hierarchy that matches the snapshots captured a moment later. + tree, hierarchyErr := fetchHierarchy(ctx, options.Driver) + if hierarchyErr != nil { + fmt.Printf("warning: step %d hierarchy: %v\n", stepIndex, hierarchyErr) + } + treeSize := 0 + if tree != nil { + treeSize = len(tree.Elements) + } + snapshot, err := snapshotStep(ctx, options) if err != nil { return summary, fmt.Errorf("step %d snapshot: %w", stepIndex, err) } - tree, hierarchyErr := fetchHierarchy(ctx, options.Driver) - if hierarchyErr != nil { - // Degrade gracefully — property evaluation still works without ax. - fmt.Printf("warning: step %d hierarchy: %v\n", stepIndex, hierarchyErr) - } - if err := options.Verifier.PushSnapshot(verifier.Snapshots(snapshot.Snapshots), tree); err != nil { return summary, fmt.Errorf("step %d push: %w", stepIndex, err) } + fmt.Printf("step %d: screen=%q hierarchy=%d nodes\n", stepIndex, screenFromSnapshot(snapshot.Snapshots), treeSize) verdicts := options.Verifier.EvaluateProperties() violations := violationNames(verdicts)