mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
refactor(runner): observe each step via the atomic Snapshot RPC
fetchSyncedState now issues one Snapshot per attempt so hierarchy and screenshot describe the same on-device frame. The transitional retry stays: that case handles a fully-captured but mid cross-fade frame, which atomic capture cannot fix.
This commit is contained in:
1 parent
c57be03b69
commit
1931c0b57f
1 file changed
+14
-31
+14
-31
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -97,9 +96,9 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
// budget on a hung tab.
|
// budget on a hung tab.
|
||||||
g, gctx := errgroup.WithContext(ctx)
|
g, gctx := errgroup.WithContext(ctx)
|
||||||
si := stepIndex
|
si := stepIndex
|
||||||
// fetchSyncedState pairs hierarchy and screenshot in the same goroutine
|
// fetchSyncedState issues a single Snapshot RPC so hierarchy and
|
||||||
// so when a retry happens on a transitional tree, the screenshot stays
|
// screenshot describe the same frame, then re-fetches the pair
|
||||||
// aligned with the final hierarchy snapshot.
|
// while the tree still looks transitional.
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
tree, hierarchyErr = fetchSyncedState(gctx, options, logger, si)
|
tree, hierarchyErr = fetchSyncedState(gctx, options, logger, si)
|
||||||
return nil
|
return nil
|
||||||
@@ -486,14 +485,6 @@ func resolveCoordinates(action verifier.Action, tree *hierarchy.Tree) (int, int,
|
|||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchHierarchy(ctx context.Context, drv driver.DeviceDriver) (*hierarchy.Tree, error) {
|
|
||||||
xmlText, err := drv.Hierarchy(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return hierarchy.Parse(xmlText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// transitionalRetryAttempts caps how many times we re-fetch hierarchy when a
|
// transitionalRetryAttempts caps how many times we re-fetch hierarchy when a
|
||||||
// tree carries more than one route-level Screen tag (NavHost cross-fade in
|
// tree carries more than one route-level Screen tag (NavHost cross-fade in
|
||||||
// flight). Each retry pauses transitionalRetrySleep before the next fetch.
|
// flight). Each retry pauses transitionalRetrySleep before the next fetch.
|
||||||
@@ -508,31 +499,23 @@ const (
|
|||||||
// and re-fetches the pair, up to transitionalRetryAttempts times. This
|
// and re-fetches the pair, up to transitionalRetryAttempts times. This
|
||||||
// handles transitions whose async work begins after the sidecar's settle
|
// handles transitions whose async work begins after the sidecar's settle
|
||||||
// poll has already exited.
|
// poll has already exited.
|
||||||
|
//
|
||||||
|
// The driver's Snapshot RPC captures both reads under a backend-side mutex
|
||||||
|
// so they describe the same on-device frame; the retry exists for the
|
||||||
|
// orthogonal case where the frame itself is transitional.
|
||||||
func fetchSyncedState(ctx context.Context, options Options, logger *slog.Logger, stepIndex int) (*hierarchy.Tree, error) {
|
func fetchSyncedState(ctx context.Context, options Options, logger *slog.Logger, stepIndex int) (*hierarchy.Tree, error) {
|
||||||
var tree *hierarchy.Tree
|
var tree *hierarchy.Tree
|
||||||
var hierarchyErr error
|
var hierarchyErr error
|
||||||
var pngBytes []byte
|
var pngBytes []byte
|
||||||
retryLoop:
|
retryLoop:
|
||||||
for attempt := range transitionalRetryAttempts {
|
for attempt := range transitionalRetryAttempts {
|
||||||
var wg sync.WaitGroup
|
hierarchyJSON, image, snapshotErr := options.Driver.Snapshot(ctx)
|
||||||
var localTree *hierarchy.Tree
|
if snapshotErr != nil {
|
||||||
var localHierErr error
|
hierarchyErr = snapshotErr
|
||||||
var localImg driver.Image
|
tree = nil
|
||||||
var localImgErr error
|
} else {
|
||||||
wg.Add(2)
|
tree, hierarchyErr = hierarchy.Parse(hierarchyJSON)
|
||||||
go func() {
|
pngBytes = image.PNG
|
||||||
defer wg.Done()
|
|
||||||
localTree, localHierErr = fetchHierarchy(ctx, options.Driver)
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
localImg, localImgErr = options.Driver.Screenshot(ctx)
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
tree = localTree
|
|
||||||
hierarchyErr = localHierErr
|
|
||||||
if localImgErr == nil {
|
|
||||||
pngBytes = localImg.PNG
|
|
||||||
}
|
}
|
||||||
if hierarchyErr != nil || !isTransitionalHierarchy(tree) {
|
if hierarchyErr != nil || !isTransitionalHierarchy(tree) {
|
||||||
break
|
break
|
||||||
|
|||||||
Reference in new issue
Block a user