Skip to content

Commit ae41e2a

Browse files
authored
Cleans up failing unit tests (#5385)
# Description - Skips `source/isaaclab_tasks/test/benchmarking/test_environments_training.py` until we resolve auto-cancel issue with pipeline where previous jobs gets cancelled on newer commits. - Marks `TheiaTiny` environment tests with xfail - Skips `test_deformable_object.py` due to crash - Reverts SDP change that removed USD fallback path for Newton model creation - Adds flaky test to `test_rendering_correctness.py‎` shadow hand test ## Type of change <!-- As you go through the list, delete the ones that are not applicable. --> - Bug fix (non-breaking change which fixes an issue) ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [ ] I have added my name to the `CONTRIBUTORS.md` or my name already exists there <!-- As you go through the checklist above, you can mark something as done by putting an x character in it For example, - [x] I have done this task - [ ] I have not done this task -->
1 parent 63da1f4 commit ae41e2a

8 files changed

Lines changed: 188 additions & 32 deletions

File tree

source/isaaclab/test/sim/test_physx_scene_data_provider_visualizer_contract.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,68 @@ def test_load_prebuilt_artifact_populates_provider_state():
6767
assert provider._xform_mask_buf is None
6868

6969

70-
def test_load_prebuilt_artifact_missing_sets_error_state():
71-
"""When no artifact is registered, model/state stay unset."""
70+
def test_load_prebuilt_artifact_missing_falls_back_to_usd_build():
71+
"""When no artifact is registered, the USD-traversal fallback is invoked."""
7272
provider = _make_provider()
73-
provider._simulation_context = SimpleNamespace(get_scene_data_visualizer_prebuilt_artifact=lambda: None)
74-
provider._load_newton_model_from_prebuilt_artifact()
73+
fallback_artifact = VisualizerPrebuiltArtifacts(
74+
model="usd-built-model",
75+
state="usd-built-state",
76+
rigid_body_paths=["/World/envs/env_0/A"],
77+
articulation_paths=[],
78+
num_envs=2,
79+
)
80+
stored: list[VisualizerPrebuiltArtifacts] = []
81+
provider._simulation_context = SimpleNamespace(
82+
get_scene_data_visualizer_prebuilt_artifact=lambda: None,
83+
set_scene_data_visualizer_prebuilt_artifact=stored.append,
84+
)
85+
provider._stage = None
86+
provider._xform_views = {}
87+
provider._view_body_index_map = {}
88+
provider._view_order_tensors = {}
89+
provider._pose_buf_num_bodies = 0
90+
provider._positions_buf = None
91+
provider._orientations_buf = None
92+
provider._covered_buf = None
93+
provider._xform_mask_buf = None
94+
95+
with (
96+
patch.object(
97+
PhysxSceneDataProvider,
98+
"_build_newton_artifact_from_usd_fallback",
99+
autospec=True,
100+
return_value=fallback_artifact,
101+
),
102+
patch(
103+
"isaaclab_physx.scene_data_providers.physx_scene_data_provider.replace_newton_shape_colors",
104+
lambda m, s: None,
105+
),
106+
):
107+
provider._load_newton_model_from_prebuilt_artifact()
108+
109+
assert provider._last_newton_model_build_source == "usd_fallback"
110+
assert provider._newton_model == "usd-built-model"
111+
assert provider._newton_state == "usd-built-state"
112+
assert provider._rigid_body_paths == ["/World/envs/env_0/A"]
113+
assert provider._num_envs_at_last_newton_build == 2
114+
# The fallback artifact is cached on the simulation context so subsequent providers see it.
115+
assert stored == [fallback_artifact]
116+
117+
118+
def test_load_prebuilt_artifact_missing_and_fallback_failed_sets_missing_state():
119+
"""When both the prebuilt artifact and the USD-traversal fallback fail, model/state stay unset."""
120+
provider = _make_provider()
121+
provider._simulation_context = SimpleNamespace(
122+
get_scene_data_visualizer_prebuilt_artifact=lambda: None,
123+
set_scene_data_visualizer_prebuilt_artifact=lambda artifact: None,
124+
)
125+
with patch.object(
126+
PhysxSceneDataProvider,
127+
"_build_newton_artifact_from_usd_fallback",
128+
autospec=True,
129+
return_value=None,
130+
):
131+
provider._load_newton_model_from_prebuilt_artifact()
75132
assert provider._last_newton_model_build_source == "missing"
76133
assert provider._newton_model is None
77134
assert provider._newton_state is None

source/isaaclab_physx/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Note: Semantic Versioning is used: https://semver.org/
4-
version = "0.5.21"
4+
version = "0.5.22"
55

66
# Description
77
title = "PhysX simulation interfaces for IsaacLab core package"

source/isaaclab_physx/docs/CHANGELOG.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
Changelog
22
---------
33

4+
0.5.22 (2026-04-23)
5+
~~~~~~~~~~~~~~~~~~~
6+
7+
Fixed
8+
^^^^^
9+
10+
* Fixed ``RuntimeError: NewtonWarpRenderer requires a Newton model but the scene data provider
11+
returned None`` when a Direct env (e.g. ``ShadowHandVisionEnv``, ``CartpoleCameraEnv``)
12+
uses ``physx`` physics with the ``newton_warp`` renderer. The
13+
:class:`~isaaclab_physx.scene_data_providers.PhysxSceneDataProvider` now falls back to a
14+
USD-traversal Newton build when the cloner-time prebuilt artifact is absent, and stashes
15+
the freshly built artifact on the simulation context so subsequent providers reuse it.
16+
17+
418
0.5.21 (2026-04-22)
519
~~~~~~~~~~~~~~~~~~~
620

source/isaaclab_physx/isaaclab_physx/scene_data_providers/physx_scene_data_provider.py

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pxr import UsdGeom, UsdPhysics
1919

2020
from isaaclab.physics.base_scene_data_provider import BaseSceneDataProvider
21+
from isaaclab.physics.scene_data_requirements import VisualizerPrebuiltArtifacts
2122
from isaaclab.sim.utils.newton_model_utils import replace_newton_shape_colors
2223

2324
logger = logging.getLogger(__name__)
@@ -105,6 +106,8 @@ def __init__(self, stage, simulation_context) -> None:
105106
"[PhysxSceneDataProvider] USD stage is None and not available from simulation_context. "
106107
"Ensure the simulation context has a valid stage when using OV/Newton/Rerun/Viser visualizers."
107108
)
109+
# Cached so the USD-traversal fallback can hand it to ``newton.ModelBuilder``.
110+
self._up_axis = UsdGeom.GetStageUpAxis(self._stage)
108111
self._num_envs_at_last_newton_build: int | None = None # for _refresh_newton_model_if_needed
109112

110113
self._device = getattr(self._simulation_context, "device", "cuda:0")
@@ -122,7 +125,7 @@ def __init__(self, stage, simulation_context) -> None:
122125
self._xform_mask_buf = None
123126
# View index order as device tensors for vectorized scatter in _apply_view_poses.
124127
self._view_order_tensors: dict[str, Any] = {}
125-
# Last load outcome (tests / debug): "prebuilt" | "missing" | "error".
128+
# Last load outcome (tests / debug): "prebuilt" | "usd_fallback" | "missing" | "error".
126129
self._last_newton_model_build_source: str | None = None
127130
self._last_newton_model_build_elapsed_ms: float | None = None
128131

@@ -165,35 +168,40 @@ def _model_body_paths(self, model) -> list[str]:
165168
return list(getattr(model, "body_label", None) or getattr(model, "body_key", []))
166169

167170
def _load_newton_model_from_prebuilt_artifact(self) -> None:
168-
"""Load Newton model and state from the simulation context prebuilt artifact."""
171+
"""Load Newton model and state, preferring the prebuilt artifact and falling back to USD traversal.
172+
173+
The fast path consumes the artifact stashed on
174+
:class:`~isaaclab.sim.SimulationContext` by the cloner's visualizer prebuild
175+
hook. When the artifact is missing — for example when a Direct env adds a
176+
camera in :meth:`_setup_scene` after the scene's clone-time requirement
177+
resolution has already run — fall back to building the model directly from
178+
the USD stage and stash the result on the simulation context so subsequent
179+
callers hit the fast path.
180+
"""
169181
start_t = time.perf_counter()
170182
try:
171183
artifact = self._simulation_context.get_scene_data_visualizer_prebuilt_artifact()
172-
if not artifact:
173-
self._last_newton_model_build_source = "missing"
174-
logger.error(
175-
"[PhysxSceneDataProvider] No visualizer prebuilt artifact on the simulation context "
176-
"(expected VisualizerPrebuiltArtifacts from scene setup)."
177-
)
178-
self._clear_newton_model_state()
179-
return
180-
181-
model = artifact.model
182-
state = artifact.state
183-
if model is None or state is None:
184-
self._last_newton_model_build_source = "missing"
185-
logger.error(
186-
"[PhysxSceneDataProvider] Prebuilt artifact is missing model or state; cannot sync PhysX to Newton."
187-
)
188-
self._clear_newton_model_state()
189-
return
184+
if not artifact or artifact.model is None or artifact.state is None:
185+
artifact = self._build_newton_artifact_from_usd_fallback()
186+
if artifact is None:
187+
self._last_newton_model_build_source = "missing"
188+
logger.error(
189+
"[PhysxSceneDataProvider] No visualizer prebuilt artifact on the simulation context "
190+
"and the USD-traversal fallback failed; cannot sync PhysX to Newton."
191+
)
192+
self._clear_newton_model_state()
193+
return
194+
self._simulation_context.set_scene_data_visualizer_prebuilt_artifact(artifact)
195+
self._last_newton_model_build_source = "usd_fallback"
196+
else:
197+
self._last_newton_model_build_source = "prebuilt"
190198

191-
self._newton_model = model
192-
self._newton_state = state
199+
self._newton_model = artifact.model
200+
self._newton_state = artifact.state
193201

194202
replace_newton_shape_colors(self._newton_model, self._stage)
195203

196-
body_paths = list(artifact.rigid_body_paths) or self._model_body_paths(model)
204+
body_paths = list(artifact.rigid_body_paths) or self._model_body_paths(artifact.model)
197205
self._rigid_body_paths = body_paths
198206
view_paths = list(body_paths)
199207
if artifact.articulation_paths:
@@ -212,10 +220,9 @@ def _load_newton_model_from_prebuilt_artifact(self) -> None:
212220
self._covered_buf = None
213221
self._xform_mask_buf = None
214222
self._num_envs_at_last_newton_build = int(artifact.num_envs)
215-
self._last_newton_model_build_source = "prebuilt"
216223
except Exception as exc:
217224
self._last_newton_model_build_source = "error"
218-
logger.error("[PhysxSceneDataProvider] Failed to load Newton model from prebuilt artifact: %s", exc)
225+
logger.error("[PhysxSceneDataProvider] Failed to load Newton model: %s", exc)
219226
self._clear_newton_model_state()
220227
finally:
221228
elapsed_ms = (time.perf_counter() - start_t) * 1000.0
@@ -239,6 +246,58 @@ def _clear_newton_model_state(self) -> None:
239246
self._rigid_body_view_paths = []
240247
self._num_envs_at_last_newton_build = None
241248

249+
def _build_newton_artifact_from_usd_fallback(self) -> VisualizerPrebuiltArtifacts | None:
250+
"""Build a Newton model from USD when no prebuilt artifact is available.
251+
252+
Used by Direct envs that add their camera in :meth:`_setup_scene` after
253+
:class:`~isaaclab.scene.InteractiveScene` has already resolved scene-data
254+
requirements (with no sensors registered). Slower than the cloner-time
255+
prebuild path because Newton has to traverse the full USD scene per
256+
environment, but functionally equivalent and required for those envs.
257+
258+
Returns:
259+
A :class:`~isaaclab.physics.scene_data_requirements.VisualizerPrebuiltArtifacts`
260+
wrapping the freshly built Newton model, or ``None`` when the build
261+
could not be performed.
262+
"""
263+
try:
264+
from newton import ModelBuilder
265+
except ModuleNotFoundError as exc:
266+
logger.error(
267+
"[PhysxSceneDataProvider] Newton module not available; cannot build USD-fallback model. "
268+
"Install the Newton backend to use newton/rerun/viser visualizers or the newton_warp renderer."
269+
)
270+
logger.debug("[PhysxSceneDataProvider] Newton import error: %s", exc)
271+
return None
272+
273+
num_envs = self.get_num_envs()
274+
if num_envs <= 0:
275+
return None
276+
277+
try:
278+
builder = ModelBuilder(up_axis=self._up_axis)
279+
builder.add_usd(self._stage, ignore_paths=[r"/World/envs/.*"])
280+
for env_id in range(num_envs):
281+
builder.begin_world()
282+
builder.add_usd(self._stage, root_path=f"/World/envs/env_{env_id}")
283+
builder.end_world()
284+
285+
model = builder.finalize(device=self._device)
286+
state = model.state()
287+
except Exception as exc:
288+
logger.error("[PhysxSceneDataProvider] USD-traversal Newton build failed: %s", exc)
289+
return None
290+
291+
body_paths = self._model_body_paths(model)
292+
articulation_paths = list(getattr(model, "articulation_label", None) or getattr(model, "articulation_key", []))
293+
return VisualizerPrebuiltArtifacts(
294+
model=model,
295+
state=state,
296+
rigid_body_paths=body_paths,
297+
articulation_paths=articulation_paths,
298+
num_envs=num_envs,
299+
)
300+
242301
def _setup_rigid_body_view(self) -> None:
243302
"""Create PhysX RigidBodyView from Newton's body paths.
244303

source/isaaclab_physx/test/assets/test_deformable_object.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
import isaaclab.utils.math as math_utils
3232
from isaaclab.sim import build_simulation_context
3333

34+
# Temporarily disabled: this suite intermittently aborts with SIGABRT on CI.
35+
# Re-enable once the underlying crash is fixed.
36+
pytestmark = pytest.mark.skip(reason="Temporarily disabled due to intermittent crash on CI.")
37+
3438

3539
def generate_cubes_scene(
3640
num_cubes: int = 1,

source/isaaclab_tasks/test/benchmarking/test_environments_training.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def train_job(workflow, task, env_config, num_gpus):
7676
return duration
7777

7878

79+
@pytest.mark.skip(reason="Temporarily disabled due to long running time.")
7980
@pytest.mark.parametrize("task_spec", setup_environment())
8081
def test_train_environments(workflow, task_spec, config_path, mode, num_gpus, kpi_store):
8182
"""Train environments provided in the config file, save KPIs, and evaluate against thresholds"""

source/isaaclab_tasks/test/env_test_utils.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
from isaaclab_tasks.utils.hydra import apply_overrides, collect_presets
2424
from isaaclab_tasks.utils.parse_cfg import load_cfg_from_registry, parse_env_cfg
2525

26+
# Map of task IDs to the reason for marking the corresponding parametrized
27+
# test cases as expected failures. Tests that consume :func:`setup_environment`
28+
# automatically pick up these marks via :class:`pytest.param`.
29+
XFAIL_TASKS: dict[str, str] = {
30+
"Isaac-Cartpole-RGB-TheiaTiny-v0": "TheiaTiny environment is currently broken; xfailed pending fix.",
31+
}
32+
2633

2734
def _is_teleop_env(task_spec) -> bool:
2835
"""Check if a task's environment config has teleop dependencies.
@@ -200,7 +207,14 @@ def setup_environment(
200207

201208
print(">>> All registered environments:", registered_tasks)
202209

203-
return registered_tasks
210+
# Wrap tasks listed in XFAIL_TASKS in pytest.param so the corresponding
211+
# parametrized test cases are reported as xfailed instead of failed.
212+
return [
213+
pytest.param(task_id, marks=pytest.mark.xfail(reason=XFAIL_TASKS[task_id], strict=False))
214+
if task_id in XFAIL_TASKS
215+
else task_id
216+
for task_id in registered_tasks
217+
]
204218

205219

206220
def _run_environments(

source/isaaclab_tasks/test/test_rendering_correctness.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,14 @@ def test_dexsuite_kuka_allegro_lift(dexsuite_kuka_allegro_lift_env):
778778
("Isaac-Cartpole-SimpleShading-Constant-Camera-Direct-v0", "cartpole"),
779779
("Isaac-Cartpole-SimpleShading-Diffuse-Camera-Direct-v0", "cartpole"),
780780
("Isaac-Cartpole-SimpleShading-Full-Camera-Direct-v0", "cartpole"),
781-
("Isaac-Repose-Cube-Shadow-Vision-Direct-v0", "shadow_hand"),
781+
pytest.param(
782+
"Isaac-Repose-Cube-Shadow-Vision-Direct-v0",
783+
"shadow_hand",
784+
# The Shadow-Vision render is right at the SSIM/diff-pixel tolerance and intermittently
785+
# exceeds the 3% diff threshold by a fraction of a percent. Allow up to 3 attempts and
786+
# require at least one pass while we tighten the validation tolerances for this scene.
787+
marks=pytest.mark.flaky(max_runs=3, min_passes=1),
788+
),
782789
]
783790

784791

0 commit comments

Comments
 (0)