feat: bounded retry for a dropped tap, and poll the screen more gently
Running spotify_play against the real phone showed a tap can be delivered to the app and then never acted on (the dispatcher lists the Spotify window as the touch target, and the screen does not move). The run log also showed step durations were printed as 0s while the recorded ones were correct. - wait steps accept "retry N": on timeout, re-run the step above and wait again, up to N extra times. Only a wait may use it, it needs a step above it, and it can only press something already matched by text. spotify_play uses it on the search box and on the playback check. - The success log line printed the duration before the deferred assignment ran. It now records the duration first. - uiautomator dump starts a JVM on the phone, and the wait helpers polled every 400ms. ui.PollInterval is now 1500ms, which cuts that load on a phone that is already short of memory. docs/phase-3-spotify-recipe.md logs attempts 7-10: the recipe now reaches tap_row and picks "Honky Tonk Women" at (540,878), but the app stops acting on input on this loaded phone, so the end-to-end run is still outstanding.
This commit is contained in:
@@ -96,14 +96,34 @@ func Run(r *adb.Runner, rec *Recipe, opts Options) *RunResult {
|
||||
w, h, _ := ui.ScreenSize(r) // best effort: bounds checking degrades gracefully
|
||||
|
||||
deadline := started.Add(rec.TotalTimeout)
|
||||
for i, step := range rec.Steps {
|
||||
if remaining := time.Until(deadline); remaining <= 0 {
|
||||
return fail(fmt.Errorf("recipe timed out after %s at step %d (%s)", rec.TotalTimeout, i+1, step.Verb))
|
||||
}
|
||||
sr := executeStep(r, rec, step, i, params, w, h, deadline, logf)
|
||||
res.Steps = append(res.Steps, sr)
|
||||
if !sr.OK {
|
||||
return fail(fmt.Errorf("step %d (%s %s) failed: %s", i+1, step.Verb, strings.Join(step.Args, " "), sr.Error))
|
||||
for i := 0; i < len(rec.Steps); i++ {
|
||||
step := rec.Steps[i]
|
||||
var sr StepResult
|
||||
for attempt := 0; ; attempt++ {
|
||||
if remaining := time.Until(deadline); remaining <= 0 {
|
||||
return fail(fmt.Errorf("recipe timed out after %s at step %d (%s)", rec.TotalTimeout, i+1, step.Verb))
|
||||
}
|
||||
sr = executeStep(r, rec, step, i, params, w, h, deadline, logf)
|
||||
res.Steps = append(res.Steps, sr)
|
||||
if sr.OK {
|
||||
break
|
||||
}
|
||||
// A wait step with "retry N" presses the step above again: on a
|
||||
// busy phone a tap can be delivered but never acted on, or dropped.
|
||||
if attempt >= step.Retry {
|
||||
return fail(fmt.Errorf("step %d (%s %s) failed: %s", i+1, step.Verb, strings.Join(step.Args, " "), sr.Error))
|
||||
}
|
||||
if i == 0 {
|
||||
return fail(fmt.Errorf("step %d: retry needs a step above it to repeat", i+1))
|
||||
}
|
||||
logf(" step %d %s: retrying, pressing %s again", i+1, step.Verb, rec.Steps[i-1].Verb)
|
||||
again := executeStep(r, rec, rec.Steps[i-1], i-1, params, w, h, deadline, logf)
|
||||
res.Steps = append(res.Steps, again)
|
||||
if !again.OK {
|
||||
// The wait is the real failure; the repeat just could not run.
|
||||
return fail(fmt.Errorf("step %d (%s %s) failed: %s; repeating %s failed too: %s",
|
||||
i+1, step.Verb, strings.Join(step.Args, " "), sr.Error, rec.Steps[i-1].Verb, again.Error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +204,7 @@ func executeStep(r *adb.Runner, rec *Recipe, step Step, index int, params map[st
|
||||
}
|
||||
return fmt.Errorf("%s never came to the foreground within %s (foreground=%s)", pkg, timeout, got)
|
||||
}
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
time.Sleep(ui.PollInterval)
|
||||
}
|
||||
|
||||
case "wait":
|
||||
@@ -269,6 +289,8 @@ func executeStep(r *adb.Runner, rec *Recipe, step Step, index int, params map[st
|
||||
}
|
||||
|
||||
sr.OK = true
|
||||
sr.Duration = time.Since(started)
|
||||
sr.DurationMS = sr.Duration.Milliseconds()
|
||||
logf(" step %d %s %v: ok (%s)", sr.Index, step.Verb, args, sr.Duration.Round(time.Millisecond))
|
||||
return sr
|
||||
}
|
||||
@@ -321,7 +343,7 @@ func tapRow(r *adb.Runner, rec *Recipe, query string, timeout time.Duration, w,
|
||||
} else {
|
||||
lastErr = err
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
time.Sleep(ui.PollInterval)
|
||||
}
|
||||
if lastErr == nil {
|
||||
lastErr = fmt.Errorf("no result row matches %q", query)
|
||||
|
||||
Reference in New Issue
Block a user