Testing on simulator & emulator
You can exercise almost everything without leaving your desk: simulated routes, parking, uploads, server errors, token expiry, remote config, process death and (on Android) reboots and Doze. This page covers the moving parts, then summarizes the end-to-end rig in the Long Ping repository, which does all of it from scripts.
A test config
Section titled “A test config”Shorter timings make a test drive quick to read:
await LongPing.configure({ movingIntervalSec: 10, parkedIntervalSec: 30, stopTimeoutSec: 60, // the minimum is 30 stopRadiusM: 50, http: { url: 'http://127.0.0.1:8787/locations', uploadIntervalSec: 20 }, logLevel: 'debug',});Simulated GPS
Section titled “Simulated GPS”# Drive between two points at 15 m/s, one fix per secondxcrun simctl location booted start --speed=15 --interval=1 43.80,18.40 44.30,18.40
# Park at a fixed point, then clearxcrun simctl location booted set 43.85,18.41xcrun simctl location booted clearXcode’s Debug → Simulate Location with a GPX file works too.
# One fix: note the order, longitude firstadb emu geo fix 18.41 43.85
# With speed: send NMEA RMC sentences (speed in knots) once a second from a scriptadb emu geo nmea '$GPRMC,120000.00,A,4351.000,N,01824.600,E,29.2,0.0,261026,,,A*6F'The emulator’s Extended controls → Location panel can also play a route.
Receiving uploads locally
Section titled “Receiving uploads locally”| Runs on | Your machine is | Notes |
|---|---|---|
| iOS Simulator | http://127.0.0.1:<port> |
Plain HTTP needs NSAllowsLocalNetworking under NSAppTransportSecurity (debug only). |
| Android emulator | http://10.0.2.2:<port> |
Plain HTTP needs a debug network-security config that permits cleartext to 10.0.2.2. |
Any small server will do; the one in the Quick start prints each record.
The end-to-end rig
Section titled “The end-to-end rig”The Long Ping repository runs the real plugin inside a real React Native app, built the way you would build yours, with simulated GPS and a scripted receiver as the upload server. Native behaviour is judged from what reaches the receiver.
| Part | What it is |
|---|---|
example/ |
React Native 0.81.5 app, New Architecture, app id com.longping.example, depends on the plugin via file:... Release-type build with the JS bundle embedded, no Metro. On launch it calls configure() and setAuthorization() and subscribes to every event; it never calls start() on its own. Its screen shows state, last fix, queue count and buttons for each command, so it doubles as a phone test app. |
| Command channel | Deep links longping-example://cmd/<name>?<args>: start, stop, changePace?moving=1|0, sync, getCurrentPosition?persist=1|0, setConfig?json=…, setAuthorization?token=…, state, queue, uploadLog. Every result and every plugin event is posted to the receiver’s /log. |
tools/e2e/receiver.mjs |
The upload server. Records every request; answers per mode: ok, 500, 401, 413, drop (for N requests), optional enforceToken, a one-shot longPing reply, a /refresh endpoint that issues rotating tokens. Exits by itself after --ttl. |
tools/e2e/ctl.mjs |
One-shot commands: receiver control and stats, app commands, drive, park, set, gps-clear, mark. |
tools/e2e/analyze.mjs |
Reads a run: gaps per event type, duplicates, batch sizes and order, motion changes, request timeline, app log. |
The rig’s config is the one at the top of this page. Each run writes its receiver output, app log and a
results.md with PASS/FAIL per scenario to e2e-runs/ (not committed).
Scenarios
Section titled “Scenarios”| # | Scenario | Passes when |
|---|---|---|
| S1 | Grant “Always”, launch, start |
A moving motionchange record arrives within 30 s. |
| S2 | Drive 5 min at ~15 m/s | interval records every 10 s: ≥ 90 % of gaps 8–13 s, none > 25 s; coordinates follow the route. |
| S3 | Park > 60 s | One parked motionchange 60–100 s after stopping, then gaps of 25–40 s. |
| S4 | Drive again | Moving motionchange within 40 s; gaps back to ~10 s. |
| S5 | Uploads | Batches of several records; zero duplicate uuids; timestamp order within a request. |
| S6 | Network down 2 min | Every record from the outage arrives afterwards; no hole > 25 s while moving. |
| S7 | Three 500s | Records retried and delivered; nothing lost; backoff visible in the log. |
| S8 | Token rotated, then refresh failing | Refresh then new Bearer tokens, no gap in records; when refresh fails: authorization event, uploads paused, fixes still recorded; setAuthorization delivers the backlog. |
| S9 | Remote config movingIntervalSec: 5 |
configchange; ≥ 80 % of gaps 4–7 s. |
| S10 | changePace, getCurrentPosition, sync, queue, state |
Each works; a current record arrives; queue 0 after sync. |
| S11 | App in background 5 min while driving | interval records continue at ~10 s. |
| S12 | Process death | iOS: records resume within 30 s of relaunch without start. Android: the service comes back on its own. |
| S13 | Android reboot | Records resume without opening the app. |
| S14 | Android Doze 10 min | Fixes keep being recorded; all uploads arrive. |
| S15 | Remote stop | enabledchange false; no more interval records; the queue still delivers. |
| S16 | Upload the app log | Lines for start, motion changes, uploads and backoff. |
| S17 | 10 min at movingIntervalSec: 1 |
~1 s gaps; memory not climbing; no crash; queue bounded. |