"""Run the installed upstream's pure methods, without constructing a hardware robot.""" import ast import importlib.metadata import json import unittest from pathlib import Path from typing import Any import numpy as np class UpstreamKinematicsTests(unittest.TestCase): def test_actual_061_methods_match_shared_wheel_fixture(self): self.assertEqual(importlib.metadata.version("lerobot"), "0.6.1") path = importlib.metadata.distribution("lerobot").locate_file( "lerobot/robots/lekiwi/lekiwi.py" ) tree = ast.parse(path.read_text()) robot = next( node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "LeKiwi" ) names = {"_degps_to_raw", "_raw_to_degps", "_body_to_wheel_raw", "_wheel_raw_to_body"} methods = [ node for node in robot.body if isinstance(node, ast.FunctionDef) and node.name in names ] self.assertEqual({node.name for node in methods}, names) # Only the four audited numeric methods. No imports, constructors, buses or cameras. cls = ast.ClassDef( name="PureKinematics", bases=[], keywords=[], body=methods, decorator_list=[], type_params=[], ) namespace = {"np": np, "Any": Any} module = ast.fix_missing_locations(ast.Module(body=[cls], type_ignores=[])) exec(compile(module, str(path), "exec"), namespace) pure = namespace["PureKinematics"]() fixture = json.loads( ( Path(__file__).resolve().parents[3] / "contracts/fixtures/lekiwi-kinematics.json" ).read_text() ) for case in fixture["cases"]: with self.subTest(body=case["body"]): raw = pure._body_to_wheel_raw(*case["body"]) self.assertEqual(list(raw), fixture["wheelOrder"]) self.assertEqual(list(raw.values()), case["wheelRaw"]) if max(abs(v) for v in case["wheelRaw"]) < 3000: body = pure._wheel_raw_to_body(*case["wheelRaw"]) for observed, expected in zip(body.values(), case["body"], strict=True): self.assertAlmostEqual(observed, expected, delta=0.02)