Skip to content

Commit 708ad16

Browse files
Future-proof MuJoCo docs: reverse coverage, default checks, signposts
Add test_no_stale_custom_attr_names_in_rst to catch doc entries left behind after code removal. Add test_critical_defaults_match_code to spot-check key defaults. Add signpost comments at both registration sites pointing developers to update the docs.
1 parent 69201e5 commit 708ad16

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

newton/_src/solvers/mujoco/solver_mujoco.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,13 @@ def register_custom_attributes(cls, builder: ModelBuilder) -> None:
417417
`MuJoCo XML Reference <https://mujoco.readthedocs.io/en/latest/XMLreference.html>`_
418418
for detailed attribute semantics.
419419
"""
420+
# ──────────────────────────────────────────────────────────────
421+
# IMPORTANT: When adding, removing, or modifying custom attributes
422+
# or frequencies below, update docs/integrations/mujoco.rst to match.
423+
# The TestMuJoCoDocCoverage test enforces this and will fail on CI
424+
# if the docs fall out of sync.
425+
# ──────────────────────────────────────────────────────────────
426+
420427
# Register custom frequencies before adding attributes that use them
421428
# This is required as custom frequencies must be registered before use
422429

newton/_src/usd/schemas.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ class SchemaResolverMjc(SchemaResolver):
281281

282282
name: ClassVar[str] = "mjc"
283283

284+
# IMPORTANT: When adding or modifying entries below, update
285+
# docs/integrations/mujoco.rst to match. TestMuJoCoDocCoverage
286+
# enforces this and will fail on CI if the docs fall out of sync.
284287
mapping: ClassVar[dict[PrimType, dict[str, SchemaAttribute]]] = {
285288
PrimType.SCENE: {
286289
"max_solver_iterations": SchemaAttribute("mjc:option:iterations", 100),

newton/tests/test_mujoco_solver.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8349,6 +8349,85 @@ def test_all_custom_usd_attribute_names_in_rst(self):
83498349
missing.append(f"{attr.name} -> {usd_name}")
83508350
self.assertEqual(missing, [], f"Custom attr USD names missing from mujoco.rst: {missing}")
83518351

8352+
def test_no_stale_custom_attr_names_in_rst(self):
8353+
"""Every custom attr name in the .rst custom-attributes section must
8354+
still be registered in code. Catches doc entries left behind after
8355+
code removal."""
8356+
# Collect all registered mujoco custom attribute names
8357+
registered = set()
8358+
for _key, attr in self.builder.custom_attributes.items():
8359+
if attr.namespace != "mujoco":
8360+
continue
8361+
registered.add(attr.name)
8362+
8363+
# Extract custom attr names from the .rst that appear inside
8364+
# ``backticks`` within the custom-attributes-and-frequencies section
8365+
section_match = re.search(
8366+
r"Custom Attributes and Frequencies\n-+\n(.*?)(?:\n\.\. _|\Z)",
8367+
self.rst_content,
8368+
re.DOTALL,
8369+
)
8370+
if section_match is None:
8371+
self.skipTest("Custom Attributes section not found in .rst")
8372+
8373+
section = section_match.group(1)
8374+
# Match backtick-quoted names that look like custom attr names
8375+
# (lowercase, underscores, no colons — distinguishes from mjc: USD names)
8376+
rst_names = set(re.findall(r"``([a-z][a-z0-9_]+)``", section))
8377+
8378+
# Filter to plausible custom attr names (exclude RST directives, etc.)
8379+
known_single_word = {
8380+
"condim",
8381+
"gravcomp",
8382+
"impratio",
8383+
"tolerance",
8384+
"density",
8385+
"viscosity",
8386+
"wind",
8387+
"magnetic",
8388+
"iterations",
8389+
"integrator",
8390+
"solver",
8391+
"cone",
8392+
"jacobian",
8393+
"autolimits",
8394+
"ctrl",
8395+
}
8396+
plausible = {n for n in rst_names if "_" in n or n in known_single_word or n in registered}
8397+
8398+
stale = plausible - registered
8399+
# Exclude known non-attr names that appear in backticks
8400+
false_positives = {"mujoco", "model", "state", "control", "finalize"}
8401+
stale -= false_positives
8402+
8403+
self.assertEqual(
8404+
stale,
8405+
set(),
8406+
f"Custom attr names in .rst that are no longer registered: {stale}",
8407+
)
8408+
8409+
def test_critical_defaults_match_code(self):
8410+
"""Spot-check that key defaults documented in .rst match the code."""
8411+
spot_checks = {
8412+
"condim": 3,
8413+
"iterations": 100,
8414+
"impratio": 1.0,
8415+
"density": 0.0,
8416+
}
8417+
for attr_name, expected_default in spot_checks.items():
8418+
key = f"mujoco:{attr_name}"
8419+
attr = self.builder.custom_attributes.get(key)
8420+
self.assertIsNotNone(attr, f"Custom attr {key} not found")
8421+
actual = attr.default
8422+
# Handle warp scalar types
8423+
if hasattr(actual, "numpy"):
8424+
actual = actual.numpy()
8425+
self.assertEqual(
8426+
actual,
8427+
expected_default,
8428+
f"Default for {attr_name} changed in code ({actual}) — update docs/integrations/mujoco.rst",
8429+
)
8430+
83528431
def test_no_invented_mjc_names_in_rst(self):
83538432
"""Every mjc: name mentioned in the .rst must exist in either
83548433
SchemaResolverMjc or register_custom_attributes."""

0 commit comments

Comments
 (0)