feat: detect a wedged app with expect_change
An app can stop consuming input while it keeps drawing: Android delivers the tap, the app never acts on it. Phase 3 hit this three times against Spotify and the recipe failed on the label it was waiting for, which names the wrong problem. The new `expect_change <duration>` step fingerprints the screen (foreground package plus every visible label and its bounds) and fails when the screen does not change after the previous step, with the distinct reason "the app stopped consuming input". It supports `retry N`, which re-presses the step above a bounded number of times, so a dropped tap is still recovered and a wedged app still ends the run (FR-11). Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -230,11 +230,12 @@ func TestRetryOnWait(t *testing.T) {
|
||||
|
||||
func TestRetryRejectsBadUse(t *testing.T) {
|
||||
for name, src := range map[string]string{
|
||||
"on a tap": "steps:\n - tap \"x\" retry 3\n",
|
||||
"on tap_row": "steps:\n - tap_row \"x\" retry 3\n",
|
||||
"no number": "steps:\n - tap_row \"x\"\n - wait \"y\" retry\n",
|
||||
"zero": "steps:\n - tap_row \"x\"\n - wait \"y\" retry 0\n",
|
||||
"not a number": "steps:\n - tap_row \"x\"\n - wait \"y\" retry soon\n",
|
||||
"on a tap": "steps:\n - tap \"x\" retry 3\n",
|
||||
"on tap_row": "steps:\n - tap_row \"x\" retry 3\n",
|
||||
"no number": "steps:\n - tap_row \"x\"\n - wait \"y\" retry\n",
|
||||
"zero": "steps:\n - tap_row \"x\"\n - wait \"y\" retry 0\n",
|
||||
"not a number": "steps:\n - tap_row \"x\"\n - wait \"y\" retry soon\n",
|
||||
"expect_change without a duration": "steps:\n - key enter\n - expect_change\n",
|
||||
} {
|
||||
if _, err := Parse(src); err == nil {
|
||||
t.Errorf("%s: expected a parse error", name)
|
||||
@@ -242,6 +243,26 @@ func TestRetryRejectsBadUse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestExpectChangeStep covers the wedged-app check: a delivered input that the
|
||||
// app never acts on must fail with its own reason, and may re-press the step
|
||||
// above a bounded number of times (FR-11).
|
||||
func TestExpectChangeStep(t *testing.T) {
|
||||
rec, err := Parse("steps:\n - key enter\n - expect_change 12s retry 2 else \"the app stopped accepting input\"\n")
|
||||
if err != nil {
|
||||
t.Fatalf("Parse: %v", err)
|
||||
}
|
||||
step := rec.Steps[1]
|
||||
if step.Verb != "expect_change" || step.Timeout != 12*time.Second {
|
||||
t.Fatalf("expect_change step = %+v", step)
|
||||
}
|
||||
if step.Retry != 2 || step.Reason != "the app stopped accepting input" {
|
||||
t.Fatalf("retry/reason = %d / %q", step.Retry, step.Reason)
|
||||
}
|
||||
if len(step.Args) != 1 || step.Args[0] != "12s" {
|
||||
t.Fatalf("args = %v, want the duration as the only argument", step.Args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlayingStep(t *testing.T) {
|
||||
rec, err := Parse("app: com.spotify.music\nsteps:\n - tap_row \"{query}\"\n - playing \"{query}\" 30s retry 3\n")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user