Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_ov"
extra-pip-packages: "ovrtx ovphysx"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhm the installs could fail but CI might keep going with those importorskip, no? and quietly telling a green outcome. Just confirming this is the intended behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: the extra package install runs inside the Docker test command under set -e, and .github/actions/run-tests/action.yml returns the container exit code after copying reports. So if pip install ovphysx fails, the job fails before pytest/importorskip can produce a green result. The importorskip guards are only for local or partial installs where the optional wheel was not requested.

container-name: isaac-lab-ov-test

# Folded from the former standalone verify-base-non-root job: reuses the
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ ignore-words-list = "haa,slq,collapsable,buss,reacher,thirdparty"

markers = [
"isaacsim_ci: mark test to run in isaacsim ci",
"device_split: re-invoke this file once per device (CPU and GPU) in CI due to process-global device locks (e.g., ovphysx<=0.3.7 gap G5)",
]

# Add pypi.nvidia.com so that `uv pip install isaaclab[isaacsim]` works without --extra-index-url.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Fixed
^^^^^

* Re-enabled both CPU and GPU coverage in CI for OVPhysX tests by tagging
:file:`test/assets/test_articulation.py`,
:file:`test/assets/test_rigid_object.py`,
:file:`test/assets/test_rigid_object_collection.py`,
:file:`test/sensors/test_contact_sensor.py`, and
:file:`test/sim/test_views_xform_prim_ovphysx.py` with the new
``device_split`` pytest marker, which causes the CI driver to invoke each
file once per device in separate subprocesses. Works around the
``ovphysx<=0.3.7`` process-global device lock (gap G5).
7 changes: 4 additions & 3 deletions source/isaaclab_ovphysx/test/assets/test_articulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@
import torch
import warp as wp

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx import tensor_types as TT # noqa: E402
Expand All @@ -83,6 +82,8 @@

wp.init()

pytestmark = pytest.mark.device_split


_OMNI_PHYSX_SCHEMAS_GAP_REASON = (
"Schema-level fixed-joint creation in :mod:`isaaclab.sim.schemas` imports "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@

from pxr import Sdf, Usd, UsdPhysics

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx.assets.articulation.articulation import Articulation # noqa: E402
Expand Down
7 changes: 4 additions & 3 deletions source/isaaclab_ovphysx/test/assets/test_rigid_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
import warp as wp
from flaky import flaky

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx.assets import RigidObject # noqa: E402
Expand All @@ -56,6 +55,8 @@

wp.init()

pytestmark = pytest.mark.device_split

_logger = logging.getLogger(__name__)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
import torch
import warp as wp

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx.assets import RigidObjectCollection # noqa: E402
Expand All @@ -53,6 +52,8 @@

wp.init()

pytestmark = pytest.mark.device_split


_LOCKED_DEVICE: list[str | None] = [None]
"""Device the session pins to on the first parametrized test that runs."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
import pytest
import warp as wp

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx import tensor_types as TT # noqa: E402
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

import pytest

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")


Expand Down
7 changes: 4 additions & 3 deletions source/isaaclab_ovphysx/test/sensors/test_contact_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@
import warp as wp
from flaky import flaky

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx.assets import RigidObject # noqa: E402
Expand All @@ -64,6 +63,8 @@

wp.init()

pytestmark = pytest.mark.device_split

# ---------------------------------------------------------------------------
# Device-lock autouse fixture
# ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@

import pytest

# The CI isaaclab_ov* pattern unintentionally collects isaaclab_ovphysx tests,
# but the ovphysx wheel is not installed in that environment. Skip gracefully
# so the isaaclab_ov CI pipeline is not blocked by an unrelated dependency.
# The OVPhysX runtime wheel is optional. Skip gracefully when it is not installed;
# CI jobs that need OVPhysX coverage install it explicitly.
pytest.importorskip("ovphysx.types", reason="ovphysx wheel not installed")

from isaaclab_ovphysx.physics import OvPhysxCfg # noqa: E402
Expand All @@ -25,6 +24,8 @@

OVPHYSX_SIM_CFG = SimulationCfg(physics=OvPhysxCfg())

pytestmark = pytest.mark.device_split


@pytest.mark.parametrize("device", ["cpu", "cuda:0"])
def test_factory_dispatches_to_ovphysx_frame_view(device):
Expand Down
65 changes: 65 additions & 0 deletions tools/_device_split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Helpers for detecting and driving the ``device_split`` pytest marker.

Test files that declare ``pytestmark = pytest.mark.device_split`` at module
scope must be re-invoked once per device (CPU and GPU) in separate processes
to work around process-global device locks such as ``ovphysx<=0.3.7`` gap G5.
The :func:`is_device_split_file` predicate lets the per-file CI runner in
``tools/conftest.py`` detect this without importing the test module.
"""

from __future__ import annotations

import re
from pathlib import Path

_DEVICE_SPLIT_MARK_RE = re.compile(r"^\s*pytestmark\b.*\bdevice_split\b", re.MULTILINE)
"""Match a module-level ``pytestmark`` assignment that mentions ``device_split``.

Recognises both single-mark and single-line list forms:

* ``pytestmark = pytest.mark.device_split``
* ``pytestmark = [pytest.mark.device_split, pytest.mark.foo]``

Multi-line list forms are not supported (currently no test file uses one); if
a future test needs that, expand the parsing rule.
"""

# Per-pass pytest ``-k`` selectors used by ``tools/conftest.py`` when a file
# declares the ``device_split`` marker. Each entry is ``(suffix, k_expr)``:
# - ``suffix`` is appended to the JUnit report filename to keep both passes' XML.
# - ``k_expr`` is the ``-k`` keyword expression. ``"cpu or not cuda"`` keeps
# non-parametrized tests in the CPU pass; ``"cuda"`` catches GPU-parametrized
# tests only.
DEVICE_SPLIT_PASSES: list[tuple[str, str]] = [
("-cpu", "cpu or not cuda"),
("-cuda", "cuda"),
]


def is_device_split_file(path: Path | str, source: str | None = None) -> bool:
"""Return whether the test file at ``path`` declares the ``device_split`` marker.

Matches :data:`_DEVICE_SPLIT_MARK_RE` against ``source`` when supplied.
Otherwise, reads the file source from ``path``. A missing or unreadable
file returns ``False`` so the caller falls back to the default single-pass
invocation.

Args:
path: Filesystem path to a candidate test file.
source: Optional preloaded source text to inspect.

Returns:
``True`` when the file's module-level ``pytestmark`` mentions
``device_split``; ``False`` otherwise.
"""
if source is None:
try:
source = Path(path).read_text(encoding="utf-8", errors="replace")
except OSError:
return False
return bool(_DEVICE_SPLIT_MARK_RE.search(source))
Loading
Loading