Files
Joachim Wiberg cf08b577ed test: gps_simple: wait for full position data before has_fix()
has_fix() only checks that fix-mode is set (2d/3d), but altitude and
other fields may not yet be populated in the operational datastore when
gpsd is still processing its first NMEA cycles after boot.  Calling
verify_position() immediately after has_fix() passes can therefore race
and fail with:

  KeyError: 'altitude'

This manifests reliably on the second GPS receiver (gps1) after reboot,
because it is initialized slightly later than gps0 and hits the window
where fix-mode is set but altitude has not yet appeared.

  not ok 11 - Verify gps1 position is near the coordinates
  # Traceback (most recent call last):
  #   File "test/case/hardware/gps_simple/test.py", line 29, in verify_position
  #     alt = float(state["altitude"])
  #                 ~~~~~^^^^^^^^^^^^
  # KeyError: 'altitude'

Add has_position() to infamy/gps.py, which gates on fix-mode AND all
position fields (latitude, longitude, altitude, satellites-used) being
present.  Replace the has_fix() polls in both the pre- and post-reboot
verify steps with has_position().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-03-20 17:05:36 +01:00

128 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""GPS receiver basic test
Verify that two simulated GPS receivers are detected and report valid
fixes via the ietf-hardware operational datastore.
The test injects NMEA sentences through QEMU pipe chardev FIFOs,
which appear as virtio serial ports inside the guest.
"""
import infamy
import infamy.gps as gps
from infamy.util import until, wait_boot
# Fun facts: The top of mount everest
test_lat = 27.9881
test_lon = 86.9250
test_alt = 8848.86
def _near(a, b, tol):
return abs(a - b) <= tol
def verify_position(target, name="gps0"):
state = gps.get_gps_state(target, name)
try:
lat = float(state["latitude"])
lon = float(state["longitude"])
alt = float(state["altitude"])
except (KeyError, TypeError, ValueError):
test.fail()
if not _near(lat, test_lat, 0.01):
test.fail()
if not _near(lon, test_lon, 0.01):
test.fail()
if not _near(alt, test_alt, 100):
test.fail()
try:
sat_used = int(state["satellites-used"])
except (KeyError, TypeError, ValueError):
test.fail()
if sat_used != 8:
test.fail()
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
target = env.attach("target", "mgmt")
phys_name = env.ltop.mapping["target"][None]
if not target.has_feature("infix-hardware", "gps"):
test.skip()
# These are hacks, will only work on virtual devices
pipe0 = f"/tmp/{phys_name}-gps"
pipe1 = f"/tmp/{phys_name}-gps1"
with gps.NMEAGenerator(pipe0, lat=test_lat, lon=test_lon, alt=test_alt), \
gps.NMEAGenerator(pipe1, lat=test_lat, lon=test_lon, alt=test_alt):
with test.step("Configure GPS hardware components"):
target.put_config_dicts({"ietf-hardware": {
"hardware": {
"component": [
{
"name": "gps0",
"class": "infix-hardware:gps",
"infix-hardware:gps-receiver": {}
},
{
"name": "gps1",
"class": "infix-hardware:gps",
"infix-hardware:gps-receiver": {}
}
]
}
}})
with test.step("Verify both GPS receivers are activated"):
until(lambda: gps.is_activated(target, "gps0"), attempts=500)
until(lambda: gps.is_activated(target, "gps1"), attempts=500)
with test.step("Verify both GPS receivers report full position data"):
until(lambda: gps.has_position(target, "gps0"), attempts=60)
until(lambda: gps.has_position(target, "gps1"), attempts=60)
with test.step("Verify both GPS receivers have a satellite fix"):
until(lambda: gps.has_fix(target, "gps0"), attempts=60)
until(lambda: gps.has_fix(target, "gps1"), attempts=60)
with test.step("Verify gps0 position is near the coordinates"):
verify_position(target, "gps0")
with test.step("Verify gps1 position is near the coordinates"):
verify_position(target, "gps1")
with test.step("Save the configuration to startup configuration and reboot"):
target.startup_override()
target.copy("running", "startup")
target.reboot()
if not wait_boot(target, env):
test.fail()
target = env.attach("target", "mgmt", test_reset=False)
with test.step("Verify both GPS receivers are activated"):
until(lambda: gps.is_activated(target, "gps0"), attempts=500)
until(lambda: gps.is_activated(target, "gps1"), attempts=500)
with test.step("Verify both GPS receivers report full position data"):
until(lambda: gps.has_position(target, "gps0"), attempts=60)
until(lambda: gps.has_position(target, "gps1"), attempts=60)
with test.step("Verify both GPS receivers have a satellite fix"):
until(lambda: gps.has_fix(target, "gps0"), attempts=60)
until(lambda: gps.has_fix(target, "gps1"), attempts=60)
with test.step("Verify gps0 position is near the coordinates"):
verify_position(target, "gps0")
with test.step("Verify gps1 position is near the coordinates"):
verify_position(target, "gps1")
test.succeed()