diff --git a/tests/test_models.py b/tests/test_models.py index 29bd3746..bcdc84f6 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -142,6 +142,61 @@ def test_pr2(self): rp.models.PR2() +class TestModelSmoke(unittest.TestCase): + """Generic smoke test: every model exported from DH/URDF/ETS must + construct with no arguments. + + The tests above are hand-written per model, so a model can be added to + __all__ and never get a dedicated test -- Valkyrie, Fetch, KinovaGen3, + FetchCamera and LBR all sat with zero test coverage this way, three of + them silently broken (see tech-debt.md for details/history). Iterating + __all__ directly means newly-added models are covered automatically. + """ + + # (category, class name) pairs known to currently fail to construct. + # Remove an entry once its underlying issue is actually fixed -- if you + # don't, this test starts failing for the *opposite* reason (a listed + # failure unexpectedly started passing). + EXPECTED_FAILURES = { + # Bundled rtb-data xacro tree names this directory "kuka_lbr_iiwa", + # but the xacro file's own $(find kuka_lbr_iiwa_support) expects + # the "_support" suffix -- needs an rtb-data rename + republish. + # See tech-debt.md, "rtb-data" section. + ("URDF", "LBR"), + } + + def test_all_models_construct(self): + unexpected_failures = [] + unexpected_passes = [] + + for category_name in ("DH", "URDF", "ETS"): + category = getattr(rp.models, category_name) + for name in category.__all__: + cls = getattr(category, name) + key = (category_name, name) + try: + cls() + except Exception as e: + if key not in self.EXPECTED_FAILURES: + unexpected_failures.append( + f"{category_name}.{name}: {type(e).__name__}: {e}" + ) + else: + if key in self.EXPECTED_FAILURES: + unexpected_passes.append(f"{category_name}.{name}") + + if unexpected_failures: + self.fail( + "Model(s) failed to construct:\n" + "\n".join(unexpected_failures) + ) + if unexpected_passes: + self.fail( + "Model(s) in EXPECTED_FAILURES now construct successfully -- " + "remove from EXPECTED_FAILURES (and close out the matching " + "tech-debt.md entry):\n" + "\n".join(unexpected_passes) + ) + + if __name__ == "__main__": # pragma nocover unittest.main() # pytest.main(['tests/test_SerialLink.py'])