From 59a0f14b60d8b2aacb0f9eb631615afe7cf4d8d9 Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Tue, 21 Jul 2026 04:23:05 -0400 Subject: [PATCH] test(sim): pure parse helper and unit tests for spherical coords Extract parse_spherical_coordinates for offline unittest coverage of SDF spherical_coordinates without Gazebo. --- .../patches/extract_spherical_coords.py | 46 +++++++++------ .../patches/test_extract_spherical_coords.py | 59 +++++++++++++++++++ 2 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 simulation/simulation_resources/patches/test_extract_spherical_coords.py diff --git a/simulation/simulation_resources/patches/extract_spherical_coords.py b/simulation/simulation_resources/patches/extract_spherical_coords.py index c3085f64..49dabb9c 100644 --- a/simulation/simulation_resources/patches/extract_spherical_coords.py +++ b/simulation/simulation_resources/patches/extract_spherical_coords.py @@ -3,36 +3,46 @@ import sys import xml.etree.ElementTree as ET + +def parse_spherical_coordinates(sdf_file): + """Parse spherical_coordinates from an SDF world file. + + Returns (latitude, longitude, elevation) as strings from the file + (defaults "0" when a child element is missing). + Raises ValueError if spherical_coordinates is absent or the file is unreadable. + """ + try: + tree = ET.parse(sdf_file) + except Exception as e: + raise ValueError(f"Error parsing SDF file: {e}") from e + root = tree.getroot() + spherical_coords = root.find(".//spherical_coordinates") + if spherical_coords is None: + raise ValueError("spherical_coordinates element not found") + latitude = spherical_coords.findtext("latitude_deg", "0") + longitude = spherical_coords.findtext("longitude_deg", "0") + elevation = spherical_coords.findtext("elevation", "0") + return latitude, longitude, elevation + + def extract_spherical_coordinates(sdf_file): # Extract spherical coordinates from an SDF world file. # Returns: lat,lon,elev,0 try: - tree = ET.parse(sdf_file) - root = tree.getroot() - - # Find the spherical_coordinates element - spherical_coords = root.find('.//spherical_coordinates') - - if spherical_coords is None: + latitude, longitude, elevation = parse_spherical_coordinates(sdf_file) + print(f"{latitude},{longitude},{elevation},0") + except ValueError as e: + if "not found" in str(e): print("0,0,0,0", file=sys.stderr) sys.exit(1) - - # Extract values - latitude = spherical_coords.findtext('latitude_deg', '0') - longitude = spherical_coords.findtext('longitude_deg', '0') - elevation = spherical_coords.findtext('elevation', '0') - - # Format as: lat,lon,elev,0 - print(f"{latitude},{longitude},{elevation},0") - - except Exception as e: print(f"Error parsing SDF file: {e}", file=sys.stderr) print("0,0,0,0") sys.exit(1) + if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: extract_spherical_coords.py ", file=sys.stderr) sys.exit(1) - + extract_spherical_coordinates(sys.argv[1]) diff --git a/simulation/simulation_resources/patches/test_extract_spherical_coords.py b/simulation/simulation_resources/patches/test_extract_spherical_coords.py new file mode 100644 index 00000000..a52c9c2d --- /dev/null +++ b/simulation/simulation_resources/patches/test_extract_spherical_coords.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Offline unit tests for extract_spherical_coords (no Gazebo).""" +import tempfile +import unittest +from pathlib import Path + +from extract_spherical_coords import parse_spherical_coordinates + + +class TestParseSpherical(unittest.TestCase): + def _write(self, body: str) -> str: + f = tempfile.NamedTemporaryFile("w", suffix=".sdf", delete=False) + f.write(body) + f.close() + return f.name + + def test_full_coords(self): + path = self._write( + """ + + + 47.4 + 8.5 + 412 + + """ + ) + lat, lon, elev = parse_spherical_coordinates(path) + self.assertEqual(lat, "47.4") + self.assertEqual(lon, "8.5") + self.assertEqual(elev, "412") + Path(path).unlink(missing_ok=True) + + def test_missing_children_default_zero(self): + path = self._write( + """ + + + """ + ) + self.assertEqual(parse_spherical_coordinates(path), ("0", "0", "0")) + Path(path).unlink(missing_ok=True) + + def test_missing_element_raises(self): + path = self._write( + """ + """ + ) + with self.assertRaises(ValueError): + parse_spherical_coordinates(path) + Path(path).unlink(missing_ok=True) + + def test_bad_file_raises(self): + with self.assertRaises(ValueError): + parse_spherical_coordinates("/no/such/file.sdf") + + +if __name__ == "__main__": + unittest.main()