Skip to content

feat: GPS route simulation — travel to ... over N seconds (FP-6) - #253

Merged
pbertsch merged 2 commits into
mainfrom
FP6
Aug 16, 2026
Merged

feat: GPS route simulation — travel to ... over N seconds (FP-6)#253
pbertsch merged 2 commits into
mainfrom
FP6

Conversation

@pbertsch

Copy link
Copy Markdown
Member

Summary

Implements FP-6: GPS route simulation, matching Maestro's travel command — a way to walk the device's GPS location through an ordered list of waypoints over time, simulating movement for maps/delivery/rideshare/fitness test flows.

New syntax:

travel to
  37.7749, -122.4194
  37.7849, -122.4094
over 10 seconds
  • An ordered, indented list of lat, lng waypoints under travel to, followed by an optional sibling over N seconds clause at the same indent level (same trailing-clause shape with examples: uses after a test body).
  • over N seconds is optional; omitting it defaults to ~1 second per leg of the route instead of jumping instantly.
  • This is pure orchestration on top of the existing single-point SetLocation primitive — the executor linearly interpolates between consecutive waypoints and calls SetLocation repeatedly at ~1-second intervals (never tighter, to avoid hammering adb/simctl). No new device-level GPS integration was added.
  • Follows the same physical-device exclusion set location already has: skips with a single warning on real devices (checked once up front in DeviceContext.Travel, not once per interpolated frame).

What changed

  • internal/parser/token.go, ast.go, parser.go — new TOKEN_TRAVEL/TOKEN_OVER tokens, Waypoint/TravelStep AST nodes, parseTravel/parseWaypoints. Extracted parseCoordinateLine out of parseActionSetLocation so both set location and travel to share the exact same "lat, lng" token-reconstruction logic (no duplicated coordinate parsing).
  • internal/runner/device_context.goDeviceContext.Travel + buildTravelRoute (pure interpolation math with no device I/O, so it's unit-testable without a real or faked adb/simctl).
  • internal/runner/executor.go — dispatch case, stepDescription, runTravel (validates waypoint count and parses lat/lng before touching the device, mirroring the cloud-mode skip pattern other DeviceContext-only verbs use).
  • Docs: website/src/content/docs/probescript/dictionary.md, syntax.md, platform/android.md, comparisons/patrol-alternative.md, README.md, CHANGELOG.md ([Unreleased]).
  • MCP: internal/mcp/server.go (write_test tool description) and website/src/content/docs/tools/mcp.md (new "GPS route simulation" example session) — per this repo's MCP Server Coverage rule.

Test plan

  • go build ./... — passes
  • go test ./... — passes, summary below
  • staticcheck ./... — clean, no new issues
  • go vet ./... — clean
  • Parser tests: waypoint parsing (2 and 3 waypoints), negative coordinates (same PT-24-class regression guard as set location), optional over clause, malformed-waypoint parse error, sibling step after the block still parses correctly
  • Executor tests: buildTravelRoute interpolation math (frame/gap counts, exact endpoint landing, default-duration fallback, degenerate <2-waypoint case), cloud-mode skip (nil deviceCtx), waypoint-count validation error, invalid-coordinate error, stepDescription output
  • NOT verified against a real emulator/simulator or physical device. This sandbox has adb and xcrun binaries present but no running emulator/simulator/device to actually drive — I did not claim or fake real-device verification. Everything above is unit/parser/executor-level coverage only; before shipping this in a release, it should be live-verified against a real Android emulator and iOS simulator the way recent CHANGELOG entries for set location document (e.g. "Live-verified fixed against a real emulator").
ok  	github.com/alphawavesystems/flutter-probe/cmd/probe	0.421s
ok  	github.com/alphawavesystems/flutter-probe/internal/ai	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/cli	0.606s
ok  	github.com/alphawavesystems/flutter-probe/internal/cloud	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/config	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/device	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/ios	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/mcp	0.842s
ok  	github.com/alphawavesystems/flutter-probe/internal/migrate	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/parser	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/plugin	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/probelink	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/redact	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/report	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/runner	(cached)
ok  	github.com/alphawavesystems/flutter-probe/internal/visual	(cached)

Out of scope for this PR (flagged, not implemented): the Maestro migration converter (internal/migrate) does not yet map Maestro's own travel: YAML step to this new syntax — it would currently fall through to a TODO comment like any other unmapped construct. Happy to follow up in a separate PR if wanted.

Adds a block-style ProbeScript construct — matching Maestro's `travel`
command — that walks the device's GPS location through an ordered list
of waypoints over a duration, for maps/delivery/rideshare/fitness test
flows. It's pure orchestration on top of the existing single-point
`set location` primitive: the executor linearly interpolates between
consecutive waypoints and calls SetLocation repeatedly at ~1s
intervals, rather than adding any new device-level GPS integration.
Follows the same physical-device exclusion `set location` already has,
and the same INDENT-delimited block-parsing shape `retry N times`
established.

- internal/parser: new TOKEN_TRAVEL/TOKEN_OVER tokens, Waypoint/
  TravelStep AST nodes, parseTravel/parseWaypoints. Extracted
  parseCoordinateLine out of parseActionSetLocation so both share the
  exact same "lat, lng" reconstruction logic.
- internal/runner: DeviceContext.Travel + buildTravelRoute (pure
  interpolation math, unit-testable without a real/faked adb/simctl),
  executor dispatch + stepDescription + runTravel.
- Docs: dictionary.md, syntax.md, platform/android.md,
  comparisons/patrol-alternative.md, README.md, CHANGELOG.md.
- MCP: write_test tool description and mcp.md example session updated
  per the MCP coverage rule.

Tested: parser tests (waypoints, negative coordinates, optional "over"
clause, malformed-waypoint errors) and executor tests (route
interpolation math, cloud-mode skip, waypoint-count/coordinate
validation). NOT verified against a real emulator/simulator/device —
no such device is reachable from this sandbox.
@pbertsch
pbertsch requested a review from a team as a code owner August 16, 2026 14:12
@pbertsch
pbertsch added this pull request to the merge queue Aug 16, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Aug 16, 2026
@pbertsch
pbertsch added this pull request to the merge queue Aug 16, 2026
Merged via the queue into main with commit b90f750 Aug 16, 2026
15 checks passed
@pbertsch
pbertsch deleted the FP6 branch August 16, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant