test: Add GPS test

Only possible to run on virtual Qemu instances right now.
This commit is contained in:
Mattias Walström
2026-03-05 08:21:34 +01:00
parent 054faa6763
commit 565a07a0b2
10 changed files with 438 additions and 2 deletions
+3
View File
@@ -7,3 +7,6 @@
- name: Watchdog reset on system lockup
case: watchdog/test.py
- name: GPS receiver basic test
case: gps_simple/test.py
+1
View File
@@ -0,0 +1 @@
test.adoc
+29
View File
@@ -0,0 +1,29 @@
=== GPS receiver basic test
ifdef::topdoc[:imagesdir: {topdoc}../../test/case/hardware/gps_simple]
==== Description
Verify that a simulated GPS receiver is detected and reports a valid
fix via the ietf-hardware operational datastore.
The test injects NMEA sentences through a QEMU pipe chardev FIFO,
which appears as a virtio serial port inside the guest.
==== Topology
image::topology.svg[GPS receiver basic test topology, align=center, scaledwidth=75%]
==== Sequence
. Set up topology and attach to target DUT
. Configure GPS hardware component
. Verify GPS is activated
. Verify GPS has a fix
. Verify the position is near the coordinates you test with
. Save the configuration to startup configuration and reboot
. Verify GPS is activated
. Verify GPS has a fix
. Verify the position is near the coordinates you test with
+119
View File
@@ -0,0 +1,119 @@
#!/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 have a 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 have a 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()
@@ -0,0 +1,22 @@
graph "1x1" {
layout="neato";
overlap="false";
esep="+80";
node [shape=record, fontname="DejaVu Sans Mono, Book"];
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
host [
label="host | { <mgmt> mgmt }",
pos="0,12!",
requires="controller",
];
target [
label="{ <mgmt> mgmt } | target",
pos="10,12!",
requires="infix gps",
];
host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"]
}
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 1x1 Pages: 1 -->
<svg width="424pt" height="45pt"
viewBox="0.00 0.00 424.03 45.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 41)">
<title>1x1</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-41 420.03,-41 420.03,4 -4,4"/>
<!-- host -->
<g id="node1" class="node">
<title>host</title>
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 100,-36.5 100,-0.5 0,-0.5"/>
<text text-anchor="middle" x="25" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
<polyline fill="none" stroke="black" points="50,-0.5 50,-36.5 "/>
<text text-anchor="middle" x="75" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
</g>
<!-- target -->
<g id="node2" class="node">
<title>target</title>
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-36.5 416.03,-36.5 416.03,-0.5 300.03,-0.5"/>
<text text-anchor="middle" x="325.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-36.5 "/>
<text text-anchor="middle" x="383.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
</g>
<!-- host&#45;&#45;target -->
<g id="edge1" class="edge">
<title>host:mgmt&#45;&#45;target:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M100,-18.5C100,-18.5 300.03,-18.5 300.03,-18.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB