diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 489b46ee060..5413579923d 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -18,4 +18,4 @@ Describe the versions that you are currently using: - Isaac Lab Version: [e.g. 2.3.0] -- Isaac Sim Version: [e.g. 5.1, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`] +- Isaac Sim Version: [e.g. 6.0, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e648f109ea..2042414b95a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,8 +150,190 @@ jobs: exit 1 fi + test-general-windows: + runs-on: [self-hosted, gpu-windows] + timeout-minutes: 180 + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + lfs: true + + - name: Setup Isaac Sim and Isaac Lab Environment + shell: powershell + run: | + Write-Host "Setting up Isaac Sim and Isaac Lab environment for Windows..." + + # Check if virtual environment exists, create if not + if (-not (Test-Path "env_isaaclab")) { + Write-Host "Creating Python 3.11 virtual environment..." + + # Use the pre-installed Python 3.11 on the self-hosted runner + $python311Path = "C:\Python311\python.exe" + + if (Test-Path $python311Path) { + Write-Host "Using Python 3.11 from: $python311Path" + & $python311Path -m venv env_isaaclab + } elseif (Get-Command py -ErrorAction SilentlyContinue) { + Write-Host "Using Python launcher (py -3.11) to create virtual environment..." + py -3.11 -m venv env_isaaclab + } else { + Write-Host "WARNING: Python 3.11 not found at expected location. Using default python..." + python -m venv env_isaaclab + } + } else { + Write-Host "Virtual environment already exists" + } + + # Activate virtual environment + Write-Host "Activating virtual environment..." + & "env_isaaclab\Scripts\Activate.ps1" + + # Upgrade pip + Write-Host "Upgrading pip..." + python -m pip install --upgrade pip + + # Check if Isaac Sim is installed + Write-Host "Checking if Isaac Sim is already installed..." + $isaacsimCheck = python -c "try:`n import isaacsim`n print('installed')`nexcept:`n print('not_installed')" 2>&1 + + if ($isaacsimCheck -match "installed") { + Write-Host "Isaac Sim already installed" + } else { + Write-Host "Installing Isaac Sim from NVIDIA PyPI..." + pip install "isaacsim[all,extscache]==5.1.0" --extra-index-url https://pypi.nvidia.com + } + + python -m pip install "isaacsim[all,extscache]==5.1.0" --extra-index-url https://pypi.nvidia.com + + # Install/update PyTorch with CUDA support + Write-Host "Installing PyTorch with CUDA 12.8..." + python -m pip install -U torch==2.7.0 torchvision==0.22.0 --index-url https://download.pytorch.org/whl/cu128 + + # Accept Isaac Sim pip EULA + $env:ISAACSIM_ACCEPT_EULA = "YES" + + # Install Isaac Lab dependencies + Write-Host "Installing Isaac Lab..." + & ".\isaaclab.bat" -i + + # Verify pytest is available + Write-Host "Verifying pytest installation..." + python -m pip show pytest + + - name: Run General Tests on Windows + id: run-general-tests-windows + shell: powershell + continue-on-error: true + run: | + Write-Host "Running general tests on Windows..." + + # Activate virtual environment + & "env_isaaclab\Scripts\Activate.ps1" + + # Create reports directory + New-Item -ItemType Directory -Force -Path "reports" | Out-Null + + # Set environment variables for headless mode + $env:OMNI_KIT_ACCEPT_EULA = "yes" + $env:ACCEPT_EULA = "Y" + $env:ISAACSIM_ACCEPT_EULA = "YES" + $env:ISAAC_SIM_HEADLESS = "1" + $env:ISAAC_SIM_LOW_MEMORY = "1" + $env:PYTHONUNBUFFERED = "1" + $env:PYTHONIOENCODING = "utf-8" + $env:WINDOWS_PLATFORM = "true" + + # Additional Windows-specific environment variables for headless Isaac Sim + $env:CARB_APP_PATH = "" + $env:OMNI_KIT_FORCE_HEADLESS = "1" + + Write-Host "=== Isaac Sim Installation Info ===" + python -m pip show isaacsim + + Write-Host "`n=== Testing Isaac Sim Import ===" + $importTest = python -c "import sys; print('Python version:', sys.version); print('Python path:', sys.executable); import isaacsim; print('Isaac Sim imported successfully')" 2>&1 + Write-Host $importTest + + if ($LASTEXITCODE -ne 0) { + Write-Host "ERROR: Isaac Sim import failed with exit code: $LASTEXITCODE" + Write-Host "This indicates Isaac Sim is not properly installed or configured." + } + + # Set environment variables for test filtering + $env:TEST_RESULT_FILE = "general-tests-windows-report.xml" + $env:TEST_EXCLUDE_PATTERN = "isaaclab_tasks" + + Write-Host "`n=== Starting Test Execution ===" + # Run tests using conftest.py which handles Windows-specific execution + & python -m pytest tools ` + -v + + $testExitCode = $LASTEXITCODE + Write-Host "Tests completed with exit code: $testExitCode" + + # Decode common Windows error codes + if ($testExitCode -eq 3221225477) { + Write-Host "ERROR: Exit code 0xC0000005 (STATUS_ACCESS_VIOLATION) - Isaac Sim crashed during initialization" + Write-Host "This typically indicates:" + Write-Host " - GPU/Graphics driver issue" + Write-Host " - Missing or incompatible DLLs" + Write-Host " - Headless mode not properly configured" + } elseif ($testExitCode -eq -1073741819) { + Write-Host "ERROR: Exit code 0xC0000005 (alternative representation) - Access violation" + } + + # Check if report was generated + $reportPath = "tests\$env:TEST_RESULT_FILE" + if (Test-Path $reportPath) { + Write-Host "Test report generated successfully at: $reportPath" + # Copy to reports directory + New-Item -ItemType Directory -Force -Path "reports" | Out-Null + Copy-Item $reportPath "reports\" -Force + } else { + Write-Host "Warning: Test report not found at $reportPath" + Write-Host "Creating reports directory and fallback report..." + New-Item -ItemType Directory -Force -Path "reports" | Out-Null + + # Create a fallback report with more detailed error info + $errorMessage = "Tests crashed with exit code $testExitCode" + if ($testExitCode -eq 3221225477) { + $errorMessage = "Tests crashed with ACCESS_VIOLATION (0xC0000005). Isaac Sim failed to initialize on Windows. This may be due to GPU/driver issues or headless configuration problems." + } + + $fallbackReport = "$errorMessage" + Set-Content -Path "reports\general-tests-windows-report.xml" -Value $fallbackReport + } + + - name: Upload Windows General Test Results + uses: actions/upload-artifact@v4 + if: always() + with: + name: general-test-results-windows + path: reports/general-tests-windows-report.xml + retention-days: 1 + compression-level: 9 + + - name: Check Test Results for Fork PRs + if: github.event.pull_request.head.repo.full_name != github.repository + shell: powershell + run: | + if (Test-Path "reports/general-tests-windows-report.xml") { + # Read the XML and check for failures + $xmlContent = Get-Content "reports/general-tests-windows-report.xml" -Raw + if ($xmlContent -match 'failures="[1-9]' -or $xmlContent -match 'errors="[1-9]') { + Write-Host "Tests failed for PR from fork. The test report is in the logs. Failing the job." + exit 1 + } + } else { + Write-Host "No test results file found. This might indicate test execution failed." + exit 1 + } + combine-results: - needs: [test-isaaclab-tasks, test-general] + needs: [test-isaaclab-tasks, test-general, test-general-windows] runs-on: [self-hosted, gpu] if: always() @@ -178,6 +360,14 @@ jobs: with: name: general-test-results path: reports/ + continue-on-error: true + + - name: Download Windows General Test Results + uses: actions/download-artifact@v4 + with: + name: general-test-results-windows + path: reports/ + continue-on-error: true - name: Combine All Test Results uses: ./.github/actions/combine-results diff --git a/isaaclab.bat b/isaaclab.bat index f47364e00b3..1bebe81a0e0 100644 --- a/isaaclab.bat +++ b/isaaclab.bat @@ -364,7 +364,7 @@ if "%arg%"=="-i" ( call :ensure_cuda_torch for /d %%d in ("%ISAACLAB_PATH%\source\*") do ( - set ext_folder="%%d" + set "ext_folder=%%d" call :install_isaaclab_extension ) rem install the python packages for supported reinforcement learning frameworks @@ -400,7 +400,7 @@ if "%arg%"=="-i" ( call :ensure_cuda_torch for /d %%d in ("%ISAACLAB_PATH%\source\*") do ( - set ext_folder="%%d" + set "ext_folder=%%d" call :install_isaaclab_extension ) rem install the python packages for supported reinforcement learning frameworks diff --git a/pytest.ini b/pytest.ini index dd4d14daf94..3e9de6b3aa2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,5 @@ [pytest] markers = isaacsim_ci: mark test to run in isaacsim ci + windows: mark test to run on windows platforms + arm: mark test to run on ARM platforms diff --git a/scripts/tools/test/test_cosmos_prompt_gen.py b/scripts/tools/test/test_cosmos_prompt_gen.py index 1520397d5cb..35687fb7223 100644 --- a/scripts/tools/test/test_cosmos_prompt_gen.py +++ b/scripts/tools/test/test_cosmos_prompt_gen.py @@ -18,6 +18,7 @@ def temp_templates_file(): """Create temporary templates file.""" temp_file = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + temp_file.close() # Close file handle before opening with json # Create test templates test_templates = { @@ -34,16 +35,31 @@ def temp_templates_file(): yield temp_file.name # Cleanup - os.remove(temp_file.name) + try: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) + os.remove(temp_file.name) @pytest.fixture def temp_output_file(): """Create temporary output file.""" temp_file = tempfile.NamedTemporaryFile(suffix=".txt", delete=False) + temp_file.close() # Close file handle immediately yield temp_file.name # Cleanup - os.remove(temp_file.name) + try: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) + os.remove(temp_file.name) class TestCosmosPromptGen: @@ -72,14 +88,22 @@ def test_generate_prompt_invalid_file(self): def test_generate_prompt_invalid_json(self): """Test generating a prompt with invalid JSON file.""" # Create a temporary file with invalid JSON - with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as temp_file: - temp_file.write(b"invalid json content") - temp_file.flush() + temp_file = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + temp_file.write(b"invalid json content") + temp_file.flush() + temp_file.close() # Close file handle before cleanup + try: + with pytest.raises(ValueError): + generate_prompt(temp_file.name) + finally: try: - with pytest.raises(ValueError): - generate_prompt(temp_file.name) - finally: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) os.remove(temp_file.name) def test_main_function_single_prompt(self, temp_templates_file, temp_output_file): diff --git a/scripts/tools/test/test_hdf5_to_mp4.py b/scripts/tools/test/test_hdf5_to_mp4.py index 1581e059854..52a5690c5de 100644 --- a/scripts/tools/test/test_hdf5_to_mp4.py +++ b/scripts/tools/test/test_hdf5_to_mp4.py @@ -19,6 +19,8 @@ def temp_hdf5_file(): """Create temporary HDF5 file with test data.""" temp_file = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + temp_file.close() # Close file handle before opening with h5py + with h5py.File(temp_file.name, "w") as h5f: # Create test data structure for demo_id in range(2): # Create 2 demos @@ -42,7 +44,14 @@ def temp_hdf5_file(): yield temp_file.name # Cleanup - os.remove(temp_file.name) + try: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) + os.remove(temp_file.name) @pytest.fixture diff --git a/scripts/tools/test/test_mp4_to_hdf5.py b/scripts/tools/test/test_mp4_to_hdf5.py index 1aa2ee8fc37..cd1f7d50681 100644 --- a/scripts/tools/test/test_mp4_to_hdf5.py +++ b/scripts/tools/test/test_mp4_to_hdf5.py @@ -20,6 +20,8 @@ def temp_hdf5_file(): """Create temporary HDF5 file with test data.""" temp_file = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + temp_file.close() # Close file handle before opening with h5py + with h5py.File(temp_file.name, "w") as h5f: # Create test data structure for 2 demos for demo_id in range(2): @@ -49,7 +51,14 @@ def temp_hdf5_file(): yield temp_file.name # Cleanup - os.remove(temp_file.name) + try: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) + os.remove(temp_file.name) @pytest.fixture(scope="class") @@ -84,9 +93,17 @@ def temp_videos_dir(): def temp_output_file(): """Create temporary output file.""" temp_file = tempfile.NamedTemporaryFile(suffix=".h5", delete=False) + temp_file.close() # Close file handle immediately yield temp_file.name # Cleanup - os.remove(temp_file.name) + try: + os.remove(temp_file.name) + except PermissionError: + # On Windows, wait a bit and retry + import time + + time.sleep(0.1) + os.remove(temp_file.name) class TestMP4ToHDF5: @@ -159,7 +176,9 @@ def test_main_function(self, temp_hdf5_file, temp_videos_dir, temp_output_file): main() # Check if output file was created with correct data - with h5py.File(temp_output_file, "r") as f: + # Use explicit close() to ensure file is closed on Windows before cleanup + f = h5py.File(temp_output_file, "r") + try: # Check if original demos were copied assert "data/demo_0" in f assert "data/demo_1" in f @@ -176,6 +195,8 @@ def test_main_function(self, temp_hdf5_file, temp_videos_dir, temp_output_file): assert f"data/demo_{demo_id}/obs/gripper_pos" in f assert f"data/demo_{demo_id}/obs/table_cam" in f assert f"data/demo_{demo_id}/obs/wrist_cam" in f + finally: + f.close() finally: # Restore original argv sys.argv = original_argv diff --git a/source/isaaclab/isaaclab/sim/converters/asset_converter_base.py b/source/isaaclab/isaaclab/sim/converters/asset_converter_base.py index 230d8e7eee5..758f2e15c98 100644 --- a/source/isaaclab/isaaclab/sim/converters/asset_converter_base.py +++ b/source/isaaclab/isaaclab/sim/converters/asset_converter_base.py @@ -9,6 +9,7 @@ import os import pathlib import random +import tempfile from datetime import datetime from isaaclab.sim.converters.asset_converter_base_cfg import AssetConverterBaseCfg @@ -64,9 +65,9 @@ def __init__(self, cfg: AssetConverterBaseCfg): # resolve USD directory name if cfg.usd_dir is None: - # a folder in "/tmp/IsaacLab" by the name: usd_{date}_{time}_{random} + # a folder in the system temp directory by the name: IsaacLab/usd_{date}_{time}_{random} time_tag = datetime.now().strftime("%Y%m%d_%H%M%S") - self._usd_dir = f"/tmp/IsaacLab/usd_{time_tag}_{random.randrange(10000)}" + self._usd_dir = os.path.join(tempfile.gettempdir(), "IsaacLab", f"usd_{time_tag}_{random.randrange(10000)}") else: self._usd_dir = cfg.usd_dir diff --git a/source/isaaclab/test/app/test_non_headless_launch.py b/source/isaaclab/test/app/test_non_headless_launch.py index 52c35a10916..c80fd4d27a6 100644 --- a/source/isaaclab/test/app/test_non_headless_launch.py +++ b/source/isaaclab/test/app/test_non_headless_launch.py @@ -25,6 +25,9 @@ from isaaclab.scene import InteractiveScene, InteractiveSceneCfg from isaaclab.utils import configclass +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class SensorsSceneCfg(InteractiveSceneCfg): diff --git a/source/isaaclab/test/assets/test_articulation.py b/source/isaaclab/test/assets/test_articulation.py index 3dda2c89396..0ca09e12718 100644 --- a/source/isaaclab/test/assets/test_articulation.py +++ b/source/isaaclab/test/assets/test_articulation.py @@ -40,6 +40,10 @@ from isaaclab_assets import ANYMAL_C_CFG, FRANKA_PANDA_CFG, SHADOW_HAND_CFG # isort:skip +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + + def generate_articulation_cfg( articulation_type: str, stiffness: float | None = 10.0, diff --git a/source/isaaclab/test/assets/test_deformable_object.py b/source/isaaclab/test/assets/test_deformable_object.py index 2d589573e69..70d7e541c83 100644 --- a/source/isaaclab/test/assets/test_deformable_object.py +++ b/source/isaaclab/test/assets/test_deformable_object.py @@ -29,6 +29,9 @@ from isaaclab.assets import DeformableObject, DeformableObjectCfg from isaaclab.sim import build_simulation_context +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def generate_cubes_scene( num_cubes: int = 1, diff --git a/source/isaaclab/test/assets/test_rigid_object.py b/source/isaaclab/test/assets/test_rigid_object.py index 6a0dc77b861..36c3e80b27c 100644 --- a/source/isaaclab/test/assets/test_rigid_object.py +++ b/source/isaaclab/test/assets/test_rigid_object.py @@ -39,6 +39,9 @@ random_orientation, ) +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def generate_cubes_scene( num_cubes: int = 1, diff --git a/source/isaaclab/test/assets/test_rigid_object_collection.py b/source/isaaclab/test/assets/test_rigid_object_collection.py index 876a2904bf1..e679d5cf509 100644 --- a/source/isaaclab/test/assets/test_rigid_object_collection.py +++ b/source/isaaclab/test/assets/test_rigid_object_collection.py @@ -37,6 +37,9 @@ subtract_frame_transforms, ) +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def generate_cubes_scene( num_envs: int = 1, diff --git a/source/isaaclab/test/assets/test_surface_gripper.py b/source/isaaclab/test/assets/test_surface_gripper.py index c2f81143f59..071c9d3a00f 100644 --- a/source/isaaclab/test/assets/test_surface_gripper.py +++ b/source/isaaclab/test/assets/test_surface_gripper.py @@ -38,6 +38,10 @@ # from isaacsim.robot.surface_gripper import GripperView +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + + def generate_surface_gripper_cfgs( kinematic_enabled: bool = False, max_grip_distance: float = 0.1, diff --git a/source/isaaclab/test/controllers/test_local_frame_task.py b/source/isaaclab/test/controllers/test_local_frame_task.py index 48c86eec082..16f710a7e75 100644 --- a/source/isaaclab/test/controllers/test_local_frame_task.py +++ b/source/isaaclab/test/controllers/test_local_frame_task.py @@ -8,6 +8,11 @@ # pinocchio is required by the Pink IK controller import sys +import pytest + +# Skip all tests in this module on Windows - MUST be before any other imports +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + if sys.platform != "win32": import pinocchio # noqa: F401 @@ -19,11 +24,13 @@ import numpy as np from pathlib import Path -import pinocchio as pin import pytest -from isaaclab.controllers.pink_ik.local_frame_task import LocalFrameTask -from isaaclab.controllers.pink_ik.pink_kinematics_configuration import PinkKinematicsConfiguration +if sys.platform != "win32": + import pinocchio as pin + + from isaaclab.controllers.pink_ik.local_frame_task import LocalFrameTask + from isaaclab.controllers.pink_ik.pink_kinematics_configuration import PinkKinematicsConfiguration # class TestLocalFrameTask: # """Test suite for LocalFrameTask class.""" diff --git a/source/isaaclab/test/controllers/test_null_space_posture_task.py b/source/isaaclab/test/controllers/test_null_space_posture_task.py index 97fc7221748..fced7e7e899 100644 --- a/source/isaaclab/test/controllers/test_null_space_posture_task.py +++ b/source/isaaclab/test/controllers/test_null_space_posture_task.py @@ -7,12 +7,14 @@ # pinocchio is required by the Pink IK controller import sys +import pytest + +# Skip all tests in this module on Windows - MUST be before any other imports +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + if sys.platform != "win32": import pinocchio # noqa: F401 import pinocchio as pin # noqa: F401 -else: - import pinocchio # noqa: F401 - import pinocchio as pin # noqa: F401 from isaaclab.app import AppLauncher @@ -24,11 +26,13 @@ import numpy as np import pytest -from pink.configuration import Configuration -from pink.tasks import FrameTask -from pinocchio.robot_wrapper import RobotWrapper -from isaaclab.controllers.pink_ik.null_space_posture_task import NullSpacePostureTask +if sys.platform != "win32": + from pink.configuration import Configuration + from pink.tasks import FrameTask + from pinocchio.robot_wrapper import RobotWrapper + + from isaaclab.controllers.pink_ik.null_space_posture_task import NullSpacePostureTask class TestNullSpacePostureTaskSimplifiedRobot: diff --git a/source/isaaclab/test/controllers/test_pink_ik.py b/source/isaaclab/test/controllers/test_pink_ik.py index 46f610c42f5..101df8cf5aa 100644 --- a/source/isaaclab/test/controllers/test_pink_ik.py +++ b/source/isaaclab/test/controllers/test_pink_ik.py @@ -8,6 +8,11 @@ # pinocchio is required by the Pink IK controller import sys +import pytest + +# Skip all tests in this module on Windows - MUST be before any other imports +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + if sys.platform != "win32": import pinocchio # noqa: F401 @@ -28,16 +33,19 @@ import omni.usd import pytest -from pink.configuration import Configuration -from pink.tasks import FrameTask from isaaclab.utils.math import axis_angle_from_quat, matrix_from_quat, quat_from_matrix, quat_inv import isaaclab_tasks # noqa: F401 -import isaaclab_tasks.manager_based.locomanipulation.pick_place # noqa: F401 -import isaaclab_tasks.manager_based.manipulation.pick_place # noqa: F401 from isaaclab_tasks.utils.parse_cfg import parse_env_cfg +if sys.platform != "win32": + from pink.configuration import Configuration + from pink.tasks import FrameTask + + import isaaclab_tasks.manager_based.locomanipulation.pick_place # noqa: F401 + import isaaclab_tasks.manager_based.manipulation.pick_place # noqa: F401 + def load_test_config(env_name): """Load test configuration based on environment type.""" diff --git a/source/isaaclab/test/controllers/test_pink_ik_components.py b/source/isaaclab/test/controllers/test_pink_ik_components.py index 6a691c353b2..6c619563f4a 100644 --- a/source/isaaclab/test/controllers/test_pink_ik_components.py +++ b/source/isaaclab/test/controllers/test_pink_ik_components.py @@ -8,6 +8,11 @@ # pinocchio is required by the Pink IK controller import sys +import pytest + +# Skip all tests in this module on Windows - MUST be before any other imports +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + if sys.platform != "win32": import pinocchio # noqa: F401 @@ -19,11 +24,13 @@ import numpy as np from pathlib import Path -import pinocchio as pin import pytest -from pink.exceptions import FrameNotFound -from isaaclab.controllers.pink_ik.pink_kinematics_configuration import PinkKinematicsConfiguration +if sys.platform != "win32": + import pinocchio as pin + from pink.exceptions import FrameNotFound + + from isaaclab.controllers.pink_ik.pink_kinematics_configuration import PinkKinematicsConfiguration class TestPinkKinematicsConfiguration: diff --git a/source/isaaclab/test/deps/test_scipy.py b/source/isaaclab/test/deps/test_scipy.py index 9cc33b37a84..78b15b9462b 100644 --- a/source/isaaclab/test/deps/test_scipy.py +++ b/source/isaaclab/test/deps/test_scipy.py @@ -13,6 +13,9 @@ import numpy as np import scipy.interpolate as interpolate +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.isaacsim_ci def test_interpolation(): diff --git a/source/isaaclab/test/deps/test_torch.py b/source/isaaclab/test/deps/test_torch.py index fcdf746e76b..0129edb1a86 100644 --- a/source/isaaclab/test/deps/test_torch.py +++ b/source/isaaclab/test/deps/test_torch.py @@ -8,6 +8,9 @@ import pytest +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.isaacsim_ci def test_array_slicing(): diff --git a/source/isaaclab/test/devices/test_device_constructors.py b/source/isaaclab/test/devices/test_device_constructors.py index ffe44740d01..14bdee53442 100644 --- a/source/isaaclab/test/devices/test_device_constructors.py +++ b/source/isaaclab/test/devices/test_device_constructors.py @@ -40,6 +40,9 @@ # Import teleop device factory for testing from isaaclab.devices.teleop_device_factory import create_teleop_device +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def mock_environment(mocker): diff --git a/source/isaaclab/test/envs/test_action_state_recorder_term.py b/source/isaaclab/test/envs/test_action_state_recorder_term.py index 64f4a726f36..00735cc631f 100644 --- a/source/isaaclab/test/envs/test_action_state_recorder_term.py +++ b/source/isaaclab/test/envs/test_action_state_recorder_term.py @@ -27,6 +27,9 @@ import isaaclab_tasks # noqa: F401 from isaaclab_tasks.utils.parse_cfg import parse_env_cfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture(scope="session", autouse=True) def setup_carb_settings(): diff --git a/source/isaaclab/test/envs/test_color_randomization.py b/source/isaaclab/test/envs/test_color_randomization.py index a550e773337..aa79a97dfe3 100644 --- a/source/isaaclab/test/envs/test_color_randomization.py +++ b/source/isaaclab/test/envs/test_color_randomization.py @@ -34,6 +34,9 @@ from isaaclab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class ActionsCfg: diff --git a/source/isaaclab/test/envs/test_direct_marl_env.py b/source/isaaclab/test/envs/test_direct_marl_env.py index b9e6142b211..274ccd75910 100644 --- a/source/isaaclab/test/envs/test_direct_marl_env.py +++ b/source/isaaclab/test/envs/test_direct_marl_env.py @@ -24,6 +24,9 @@ from isaaclab.scene import InteractiveSceneCfg from isaaclab.utils import configclass +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class EmptySceneCfg(InteractiveSceneCfg): diff --git a/source/isaaclab/test/envs/test_env_rendering_logic.py b/source/isaaclab/test/envs/test_env_rendering_logic.py index f3ba8891b9a..fe0520bab9c 100644 --- a/source/isaaclab/test/envs/test_env_rendering_logic.py +++ b/source/isaaclab/test/envs/test_env_rendering_logic.py @@ -30,6 +30,9 @@ from isaaclab.sim import SimulationCfg, SimulationContext from isaaclab.utils import configclass +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class EmptyManagerCfg: diff --git a/source/isaaclab/test/envs/test_manager_based_env.py b/source/isaaclab/test/envs/test_manager_based_env.py index c420b16f12d..af850567799 100644 --- a/source/isaaclab/test/envs/test_manager_based_env.py +++ b/source/isaaclab/test/envs/test_manager_based_env.py @@ -28,6 +28,9 @@ from isaaclab.scene import InteractiveSceneCfg from isaaclab.utils import configclass +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class EmptyManagerCfg: diff --git a/source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py b/source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py index d8a8e8e32be..05698a94a07 100644 --- a/source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py +++ b/source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py @@ -25,6 +25,9 @@ ) from isaaclab_tasks.manager_based.locomotion.velocity.config.anymal_c.rough_env_cfg import AnymalCRoughEnvCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.parametrize("device", ["cpu", "cuda"]) def test_non_concatenated_obs_groups_contain_all_terms(device): diff --git a/source/isaaclab/test/envs/test_manager_based_rl_env_ui.py b/source/isaaclab/test/envs/test_manager_based_rl_env_ui.py index e3c26a86b42..82c8518ee3a 100644 --- a/source/isaaclab/test/envs/test_manager_based_rl_env_ui.py +++ b/source/isaaclab/test/envs/test_manager_based_rl_env_ui.py @@ -18,6 +18,7 @@ import carb import omni.usd +import pytest from isaacsim.core.utils.extensions import enable_extension from isaaclab.envs import ManagerBasedRLEnv, ManagerBasedRLEnvCfg @@ -27,6 +28,9 @@ enable_extension("isaacsim.gui.components") +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class EmptyManagerCfg: diff --git a/source/isaaclab/test/envs/test_modify_env_param_curr_term.py b/source/isaaclab/test/envs/test_modify_env_param_curr_term.py index e82a842ec95..87a233683bb 100644 --- a/source/isaaclab/test/envs/test_modify_env_param_curr_term.py +++ b/source/isaaclab/test/envs/test_modify_env_param_curr_term.py @@ -23,6 +23,9 @@ from isaaclab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleEnvCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def replace_value(env, env_id, data, value, num_steps): if env.common_step_counter > num_steps and data != value: diff --git a/source/isaaclab/test/envs/test_null_command_term.py b/source/isaaclab/test/envs/test_null_command_term.py index f8699439477..606565d001b 100644 --- a/source/isaaclab/test/envs/test_null_command_term.py +++ b/source/isaaclab/test/envs/test_null_command_term.py @@ -18,6 +18,9 @@ from isaaclab.envs.mdp import NullCommandCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def env(): diff --git a/source/isaaclab/test/envs/test_scale_randomization.py b/source/isaaclab/test/envs/test_scale_randomization.py index 82c2127bc6e..a4476d3ec8b 100644 --- a/source/isaaclab/test/envs/test_scale_randomization.py +++ b/source/isaaclab/test/envs/test_scale_randomization.py @@ -44,6 +44,10 @@ ## +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + + class CubeActionTerm(ActionTerm): """Simple action term that implements a PD controller to track a target position. diff --git a/source/isaaclab/test/envs/test_spaces_utils.py b/source/isaaclab/test/envs/test_spaces_utils.py index cbb6fc0e2db..9ed16ae7f44 100644 --- a/source/isaaclab/test/envs/test_spaces_utils.py +++ b/source/isaaclab/test/envs/test_spaces_utils.py @@ -21,8 +21,13 @@ import torch from gymnasium.spaces import Box, Dict, Discrete, MultiDiscrete, Tuple +import pytest + from isaaclab.envs.utils.spaces import deserialize_space, sample_space, serialize_space, spec_to_gym_space +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def test_spec_to_gym_space(): """Test conversion of specs to gym spaces.""" diff --git a/source/isaaclab/test/envs/test_texture_randomization.py b/source/isaaclab/test/envs/test_texture_randomization.py index 417825423ac..f3eba689661 100644 --- a/source/isaaclab/test/envs/test_texture_randomization.py +++ b/source/isaaclab/test/envs/test_texture_randomization.py @@ -34,6 +34,9 @@ from isaaclab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class ActionsCfg: diff --git a/source/isaaclab/test/scene/test_interactive_scene.py b/source/isaaclab/test/scene/test_interactive_scene.py index f900c7ee44a..afaa2384bfb 100644 --- a/source/isaaclab/test/scene/test_interactive_scene.py +++ b/source/isaaclab/test/scene/test_interactive_scene.py @@ -25,6 +25,9 @@ from isaaclab.utils import configclass from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @configclass class MySceneCfg(InteractiveSceneCfg): diff --git a/source/isaaclab/test/sim/test_build_simulation_context_headless.py b/source/isaaclab/test/sim/test_build_simulation_context_headless.py index af29346f9ee..15b14e78f03 100644 --- a/source/isaaclab/test/sim/test_build_simulation_context_headless.py +++ b/source/isaaclab/test/sim/test_build_simulation_context_headless.py @@ -26,6 +26,9 @@ from isaaclab.sim.simulation_cfg import SimulationCfg from isaaclab.sim.simulation_context import build_simulation_context +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.parametrize("gravity_enabled", [True, False]) @pytest.mark.parametrize("device", ["cuda:0", "cpu"]) diff --git a/source/isaaclab/test/sim/test_mesh_converter.py b/source/isaaclab/test/sim/test_mesh_converter.py index 90bfc557c78..25b814cbdf7 100644 --- a/source/isaaclab/test/sim/test_mesh_converter.py +++ b/source/isaaclab/test/sim/test_mesh_converter.py @@ -28,6 +28,9 @@ from isaaclab.sim.schemas import schemas_cfg from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def random_quaternion(): # Generate four random numbers for the quaternion diff --git a/source/isaaclab/test/sim/test_mjcf_converter.py b/source/isaaclab/test/sim/test_mjcf_converter.py index 5921b12fc6c..0075cb81bc8 100644 --- a/source/isaaclab/test/sim/test_mjcf_converter.py +++ b/source/isaaclab/test/sim/test_mjcf_converter.py @@ -22,6 +22,9 @@ from isaaclab.sim.converters import MjcfConverter, MjcfConverterCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture(autouse=True) def test_setup_teardown(): diff --git a/source/isaaclab/test/sim/test_schemas.py b/source/isaaclab/test/sim/test_schemas.py index 29b451f4214..b155624e7b0 100644 --- a/source/isaaclab/test/sim/test_schemas.py +++ b/source/isaaclab/test/sim/test_schemas.py @@ -25,6 +25,9 @@ from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.string import to_camel_case +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def setup_simulation(): diff --git a/source/isaaclab/test/sim/test_simulation_context.py b/source/isaaclab/test/sim/test_simulation_context.py index f0f783463d2..1fab9f794aa 100644 --- a/source/isaaclab/test/sim/test_simulation_context.py +++ b/source/isaaclab/test/sim/test_simulation_context.py @@ -20,6 +20,9 @@ from isaaclab.sim import SimulationCfg, SimulationContext +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture(autouse=True) def test_setup_teardown(): diff --git a/source/isaaclab/test/sim/test_simulation_render_config.py b/source/isaaclab/test/sim/test_simulation_render_config.py index 67b96e9754c..b127359ce06 100644 --- a/source/isaaclab/test/sim/test_simulation_render_config.py +++ b/source/isaaclab/test/sim/test_simulation_render_config.py @@ -26,6 +26,9 @@ from isaaclab.sim.simulation_cfg import RenderCfg, SimulationCfg from isaaclab.sim.simulation_context import SimulationContext +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.skip(reason="Timeline not stopped") @pytest.mark.isaacsim_ci diff --git a/source/isaaclab/test/sim/test_spawn_from_files.py b/source/isaaclab/test/sim/test_spawn_from_files.py index 59b2741e4ee..b288fc59a96 100644 --- a/source/isaaclab/test/sim/test_spawn_from_files.py +++ b/source/isaaclab/test/sim/test_spawn_from_files.py @@ -21,6 +21,9 @@ import isaaclab.sim as sim_utils from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_spawn_lights.py b/source/isaaclab/test/sim/test_spawn_lights.py index ec178244e1b..d29fb4b6741 100644 --- a/source/isaaclab/test/sim/test_spawn_lights.py +++ b/source/isaaclab/test/sim/test_spawn_lights.py @@ -21,6 +21,9 @@ import isaaclab.sim as sim_utils from isaaclab.utils.string import to_camel_case +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture(autouse=True) def test_setup_teardown(): diff --git a/source/isaaclab/test/sim/test_spawn_materials.py b/source/isaaclab/test/sim/test_spawn_materials.py index e95ee6e3724..e07824fa9c8 100644 --- a/source/isaaclab/test/sim/test_spawn_materials.py +++ b/source/isaaclab/test/sim/test_spawn_materials.py @@ -21,6 +21,9 @@ import isaaclab.sim as sim_utils from isaaclab.utils.assets import NVIDIA_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_spawn_meshes.py b/source/isaaclab/test/sim/test_spawn_meshes.py index b2297255d97..7de4199c1aa 100644 --- a/source/isaaclab/test/sim/test_spawn_meshes.py +++ b/source/isaaclab/test/sim/test_spawn_meshes.py @@ -19,6 +19,9 @@ import isaaclab.sim as sim_utils +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_spawn_sensors.py b/source/isaaclab/test/sim/test_spawn_sensors.py index ac0cab828ad..e76db9d57bd 100644 --- a/source/isaaclab/test/sim/test_spawn_sensors.py +++ b/source/isaaclab/test/sim/test_spawn_sensors.py @@ -21,6 +21,9 @@ from isaaclab.sim.spawners.sensors.sensors import CUSTOM_FISHEYE_CAMERA_ATTRIBUTES, CUSTOM_PINHOLE_CAMERA_ATTRIBUTES from isaaclab.utils.string import to_camel_case +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_spawn_shapes.py b/source/isaaclab/test/sim/test_spawn_shapes.py index c889a4ab818..5e467e1dc5c 100644 --- a/source/isaaclab/test/sim/test_spawn_shapes.py +++ b/source/isaaclab/test/sim/test_spawn_shapes.py @@ -19,6 +19,9 @@ import isaaclab.sim as sim_utils +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_spawn_wrappers.py b/source/isaaclab/test/sim/test_spawn_wrappers.py index 5edae7a79d6..d56bc8e25ff 100644 --- a/source/isaaclab/test/sim/test_spawn_wrappers.py +++ b/source/isaaclab/test/sim/test_spawn_wrappers.py @@ -20,6 +20,9 @@ import isaaclab.sim as sim_utils from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_stage_in_memory.py b/source/isaaclab/test/sim/test_stage_in_memory.py index d114185862a..aa53c652cf6 100644 --- a/source/isaaclab/test/sim/test_stage_in_memory.py +++ b/source/isaaclab/test/sim/test_stage_in_memory.py @@ -26,6 +26,9 @@ from isaaclab.sim.simulation_context import SimulationCfg, SimulationContext from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture def sim(): diff --git a/source/isaaclab/test/sim/test_urdf_converter.py b/source/isaaclab/test/sim/test_urdf_converter.py index f238fd02408..74556538198 100644 --- a/source/isaaclab/test/sim/test_urdf_converter.py +++ b/source/isaaclab/test/sim/test_urdf_converter.py @@ -5,6 +5,8 @@ """Launch Isaac Sim Simulator first.""" +import sys + from isaaclab.app import AppLauncher # launch omniverse app @@ -24,6 +26,9 @@ from isaaclab.sim.converters import UrdfConverter, UrdfConverterCfg +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + # Create a fixture for setup and teardown @pytest.fixture @@ -71,6 +76,7 @@ def test_no_change(sim_config): @pytest.mark.isaacsim_ci +@pytest.mark.skipif(sys.platform == "win32", reason="Test hangs on Windows due to file locking issues") def test_config_change(sim_config): """Call conversion twice but change the config in the second call. This should generate a new USD file.""" sim, config = sim_config @@ -102,6 +108,7 @@ def test_create_prim_from_usd(sim_config): @pytest.mark.isaacsim_ci +@pytest.mark.skipif(sys.platform == "win32", reason="Test hangs on Windows due to file locking issues") def test_config_drive_type(sim_config): """Change the drive mechanism of the robot to be position.""" sim, config = sim_config diff --git a/source/isaaclab/test/sim/test_utils.py b/source/isaaclab/test/sim/test_utils.py index 61dea14def0..313b9db03e1 100644 --- a/source/isaaclab/test/sim/test_utils.py +++ b/source/isaaclab/test/sim/test_utils.py @@ -24,6 +24,9 @@ import isaaclab.utils.math as math_utils from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.fixture(autouse=True) def test_setup_teardown(): diff --git a/source/isaaclab/test/utils/test_configclass.py b/source/isaaclab/test/utils/test_configclass.py index 6fbfb4ee8f9..198cdcd6b1a 100644 --- a/source/isaaclab/test/utils/test_configclass.py +++ b/source/isaaclab/test/utils/test_configclass.py @@ -30,6 +30,9 @@ from isaaclab.utils.dict import class_to_dict, dict_to_md5_hash, update_class_from_dict from isaaclab.utils.io import dump_yaml, load_yaml +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + """ Mock classes and functions. """ diff --git a/source/isaaclab/test/utils/test_dict.py b/source/isaaclab/test/utils/test_dict.py index 9713f8c1352..137adb1b94c 100644 --- a/source/isaaclab/test/utils/test_dict.py +++ b/source/isaaclab/test/utils/test_dict.py @@ -16,8 +16,13 @@ import random +import pytest + import isaaclab.utils.dict as dict_utils +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + def _test_function(x): """Test function for string <-> callable conversion.""" diff --git a/source/isaaclab_mimic/test/test_curobo_planner_cube_stack.py b/source/isaaclab_mimic/test/test_curobo_planner_cube_stack.py index 844db6fafd5..c477fa6b282 100644 --- a/source/isaaclab_mimic/test/test_curobo_planner_cube_stack.py +++ b/source/isaaclab_mimic/test/test_curobo_planner_cube_stack.py @@ -10,10 +10,14 @@ from __future__ import annotations import random +import sys from typing import Any import pytest +# Skip all tests in this module on Windows - MUST be before AppLauncher +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + SEED: int = 42 random.seed(SEED) @@ -27,14 +31,17 @@ import torch from collections.abc import Generator +import pytest + import isaaclab.utils.math as math_utils from isaaclab.assets import Articulation, RigidObject from isaaclab.envs.manager_based_env import ManagerBasedEnv from isaaclab.markers import FRAME_MARKER_CFG, VisualizationMarkers -from isaaclab_mimic.envs.franka_stack_ik_rel_mimic_env_cfg import FrankaCubeStackIKRelMimicEnvCfg -from isaaclab_mimic.motion_planners.curobo.curobo_planner import CuroboPlanner -from isaaclab_mimic.motion_planners.curobo.curobo_planner_cfg import CuroboPlannerCfg +if sys.platform != "win32": + from isaaclab_mimic.envs.franka_stack_ik_rel_mimic_env_cfg import FrankaCubeStackIKRelMimicEnvCfg + from isaaclab_mimic.motion_planners.curobo.curobo_planner import CuroboPlanner + from isaaclab_mimic.motion_planners.curobo.curobo_planner_cfg import CuroboPlannerCfg GRIPPER_OPEN_CMD: float = 1.0 GRIPPER_CLOSE_CMD: float = -1.0 diff --git a/source/isaaclab_mimic/test/test_curobo_planner_franka.py b/source/isaaclab_mimic/test/test_curobo_planner_franka.py index 323caf99c28..ac365e08d8f 100644 --- a/source/isaaclab_mimic/test/test_curobo_planner_franka.py +++ b/source/isaaclab_mimic/test/test_curobo_planner_franka.py @@ -4,11 +4,15 @@ # SPDX-License-Identifier: Apache-2.0 import random +import sys from collections.abc import Generator from typing import Any import pytest +# Skip all tests in this module on Windows - MUST be before AppLauncher +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + SEED: int = 42 random.seed(SEED) @@ -21,6 +25,8 @@ import gymnasium as gym import torch +import pytest + import isaaclab.utils.assets as _al_assets import isaaclab.utils.math as math_utils from isaaclab.assets import Articulation, RigidObjectCfg @@ -31,10 +37,13 @@ ISAAC_NUCLEUS_DIR: str = getattr(_al_assets, "ISAAC_NUCLEUS_DIR", "/Isaac") -from isaaclab_mimic.motion_planners.curobo.curobo_planner import CuroboPlanner -from isaaclab_mimic.motion_planners.curobo.curobo_planner_cfg import CuroboPlannerCfg +if sys.platform != "win32": + from isaaclab_mimic.motion_planners.curobo.curobo_planner import CuroboPlanner + from isaaclab_mimic.motion_planners.curobo.curobo_planner_cfg import CuroboPlannerCfg -from isaaclab_tasks.manager_based.manipulation.stack.config.franka.stack_joint_pos_env_cfg import FrankaCubeStackEnvCfg + from isaaclab_tasks.manager_based.manipulation.stack.config.franka.stack_joint_pos_env_cfg import ( + FrankaCubeStackEnvCfg, + ) # Predefined EE goals for the test # Each entry is a tuple of: (goal specification, goal ID) diff --git a/source/isaaclab_mimic/test/test_generate_dataset.py b/source/isaaclab_mimic/test/test_generate_dataset.py index 9125c8e8619..f00fdfb30d5 100644 --- a/source/isaaclab_mimic/test/test_generate_dataset.py +++ b/source/isaaclab_mimic/test/test_generate_dataset.py @@ -5,17 +5,21 @@ """Test dataset generation for Isaac Lab Mimic workflow.""" -from isaaclab.app import AppLauncher - -# launch omniverse app -simulation_app = AppLauncher(headless=True).app - import os import subprocess +import sys import tempfile import pytest +# Skip all tests in this module on Windows - MUST be before AppLauncher +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + +from isaaclab.app import AppLauncher + +# launch omniverse app +simulation_app = AppLauncher(headless=True).app + from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path DATASETS_DOWNLOAD_DIR = tempfile.mkdtemp(suffix="_Isaac-Stack-Cube-Franka-IK-Rel-Mimic-v0") diff --git a/source/isaaclab_mimic/test/test_generate_dataset_skillgen.py b/source/isaaclab_mimic/test/test_generate_dataset_skillgen.py index 846604a1c0c..488f49a411f 100644 --- a/source/isaaclab_mimic/test/test_generate_dataset_skillgen.py +++ b/source/isaaclab_mimic/test/test_generate_dataset_skillgen.py @@ -5,17 +5,21 @@ """Test dataset generation with SkillGen for Isaac Lab Mimic workflow.""" -from isaaclab.app import AppLauncher - -# Launch omniverse app -simulation_app = AppLauncher(headless=True).app - import os import subprocess +import sys import tempfile import pytest +# Skip all tests in this module on Windows - MUST be before AppLauncher +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Test not supported on Windows") + +from isaaclab.app import AppLauncher + +# Launch omniverse app +simulation_app = AppLauncher(headless=True).app + from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR, retrieve_file_path DATASETS_DOWNLOAD_DIR = tempfile.mkdtemp(suffix="_Isaac-Stack-Cube-Franka-IK-Rel-Skillgen-v0") diff --git a/source/isaaclab_rl/test/test_rl_games_wrapper.py b/source/isaaclab_rl/test/test_rl_games_wrapper.py index 95a183ad0c2..edca4f13ea2 100644 --- a/source/isaaclab_rl/test/test_rl_games_wrapper.py +++ b/source/isaaclab_rl/test/test_rl_games_wrapper.py @@ -58,6 +58,8 @@ def registered_tasks(): return registered_tasks +@pytest.mark.windows +@pytest.mark.arm def test_random_actions(registered_tasks): """Run random actions and check environments return valid signals.""" # common parameters diff --git a/source/isaaclab_rl/test/test_rsl_rl_wrapper.py b/source/isaaclab_rl/test/test_rsl_rl_wrapper.py index 4eaf921be85..1d46e752d27 100644 --- a/source/isaaclab_rl/test/test_rsl_rl_wrapper.py +++ b/source/isaaclab_rl/test/test_rsl_rl_wrapper.py @@ -53,6 +53,8 @@ def registered_tasks(): return registered_tasks +@pytest.mark.windows +@pytest.mark.arm def test_random_actions(registered_tasks): """Run random actions and check environments return valid signals.""" # common parameters @@ -105,6 +107,8 @@ def test_random_actions(registered_tasks): env.close() +@pytest.mark.windows +@pytest.mark.arm def test_no_time_outs(registered_tasks): """Check that environments with finite horizon do not send time-out signals.""" # common parameters diff --git a/source/isaaclab_rl/test/test_sb3_wrapper.py b/source/isaaclab_rl/test/test_sb3_wrapper.py index 6fd63eaa73e..a2afc9e4032 100644 --- a/source/isaaclab_rl/test/test_sb3_wrapper.py +++ b/source/isaaclab_rl/test/test_sb3_wrapper.py @@ -53,6 +53,8 @@ def registered_tasks(): return registered_tasks +@pytest.mark.windows +@pytest.mark.arm def test_random_actions(registered_tasks): """Run random actions and check environments return valid signals.""" # common parameters diff --git a/source/isaaclab_rl/test/test_skrl_wrapper.py b/source/isaaclab_rl/test/test_skrl_wrapper.py index ae83058ff44..f6f5d01fc17 100644 --- a/source/isaaclab_rl/test/test_skrl_wrapper.py +++ b/source/isaaclab_rl/test/test_skrl_wrapper.py @@ -52,6 +52,8 @@ def registered_tasks(): return registered_tasks +@pytest.mark.windows +@pytest.mark.arm def test_random_actions(registered_tasks): """Run random actions and check environments return valid signals.""" # common parameters diff --git a/source/isaaclab_tasks/test/benchmarking/env_benchmark_test_utils.py b/source/isaaclab_tasks/test/benchmarking/env_benchmark_test_utils.py index 0c939ca0166..891f9aef89f 100644 --- a/source/isaaclab_tasks/test/benchmarking/env_benchmark_test_utils.py +++ b/source/isaaclab_tasks/test/benchmarking/env_benchmark_test_utils.py @@ -12,7 +12,6 @@ import yaml from datetime import datetime -import carb from tensorboard.backend.event_processing import event_accumulator @@ -135,6 +134,8 @@ def process_kpi_data(kpi_payloads, tag=""): def output_payloads(payloads): """Output the KPI payloads to a json file.""" + import carb + # first grab all log files repo_path = os.path.join(carb.tokens.get_tokens_interface().resolve("${app}"), "..") output_path = os.path.join(repo_path, "logs/kpi.json") @@ -148,6 +149,8 @@ def output_payloads(payloads): def _retrieve_logs(workflow, task): """Retrieve training logs.""" + import carb + # first grab all log files repo_path = os.path.join(carb.tokens.get_tokens_interface().resolve("${app}"), "..") from isaacsim.core.version import get_version diff --git a/source/isaaclab_tasks/test/benchmarking/test_environments_training.py b/source/isaaclab_tasks/test/benchmarking/test_environments_training.py index 5fae937ef84..7ed591dc6a4 100644 --- a/source/isaaclab_tasks/test/benchmarking/test_environments_training.py +++ b/source/isaaclab_tasks/test/benchmarking/test_environments_training.py @@ -5,6 +5,13 @@ """Launch Isaac Sim Simulator first.""" +import sys + +import pytest + +# Skip all benchmarking tests on Windows - these are resource-intensive training tests +pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Benchmarking tests not supported on Windows") + from isaaclab.app import AppLauncher # Launch omniverse app diff --git a/source/isaaclab_tasks/test/env_test_utils.py b/source/isaaclab_tasks/test/env_test_utils.py index 1034fd9ac92..66e01d1e773 100644 --- a/source/isaaclab_tasks/test/env_test_utils.py +++ b/source/isaaclab_tasks/test/env_test_utils.py @@ -20,6 +20,27 @@ from isaaclab_tasks.utils.parse_cfg import parse_env_cfg +def requires_pinocchio(task_name: str) -> bool: + """Check if a task requires pinocchio (Pink IK controller). + + Args: + task_name: The task name to check. + + Returns: + True if the task requires pinocchio, False otherwise. + """ + # List of task name patterns that require pinocchio + pinocchio_patterns = [ + "GR1T2", # All GR1T2 environments use Pink IK + "G1", # All G1 environments use Pink IK + "Pink-IK", # Explicit Pink IK in name + "UpperBodyIK", # Upper body IK environments + "Locomanipulation", # Locomanipulation environments use Pink IK + ] + + return any(pattern in task_name for pattern in pinocchio_patterns) + + def setup_environment( include_play: bool = False, factory_envs: bool | None = None, diff --git a/source/isaaclab_tasks/test/test_environment_determinism.py b/source/isaaclab_tasks/test/test_environment_determinism.py index 016e60cb2f6..6bfcec4fcfb 100644 --- a/source/isaaclab_tasks/test/test_environment_determinism.py +++ b/source/isaaclab_tasks/test/test_environment_determinism.py @@ -15,11 +15,13 @@ """Rest everything follows.""" import gymnasium as gym +import sys import torch import carb import omni.usd import pytest +from env_test_utils import requires_pinocchio import isaaclab_tasks # noqa: F401 from isaaclab_tasks.utils.parse_cfg import parse_env_cfg @@ -75,6 +77,16 @@ def test_dextrous_env_determinism(task_name, device): def _test_environment_determinism(task_name: str, device: str): """Check deterministic environment creation.""" + # Skip tasks with Mimic or Skillgen in the name on Windows + if "Mimic" in task_name or "Skillgen" in task_name: + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (Mimic/Skillgen not supported)") + + # Skip tasks that require pinocchio on Windows + if requires_pinocchio(task_name): + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (requires pinocchio which is not supported)") + # fix number of steps num_envs = 32 num_steps = 100 diff --git a/source/isaaclab_tasks/test/test_environments.py b/source/isaaclab_tasks/test/test_environments.py index 2a0c9d4ea52..39a15a9828d 100644 --- a/source/isaaclab_tasks/test/test_environments.py +++ b/source/isaaclab_tasks/test/test_environments.py @@ -21,15 +21,30 @@ """Rest everything follows.""" +import sys + import pytest -from env_test_utils import _run_environments, setup_environment +from env_test_utils import _run_environments, requires_pinocchio, setup_environment import isaaclab_tasks # noqa: F401 +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.parametrize("num_envs, device", [(32, "cuda"), (1, "cuda")]) @pytest.mark.parametrize("task_name", setup_environment(include_play=False, factory_envs=False, multi_agent=False)) @pytest.mark.isaacsim_ci def test_environments(task_name, num_envs, device): + # Skip tasks with Mimic or Skillgen in the name on Windows + if "Mimic" in task_name or "Skillgen" in task_name: + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (Mimic/Skillgen not supported)") + + # Skip tasks that require pinocchio on Windows + if requires_pinocchio(task_name): + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (requires pinocchio which is not supported)") + # run environments without stage in memory _run_environments(task_name, device, num_envs, create_stage_in_memory=False) diff --git a/source/isaaclab_tasks/test/test_environments_with_stage_in_memory.py b/source/isaaclab_tasks/test/test_environments_with_stage_in_memory.py index 70dcf94961f..04a3b66dc75 100644 --- a/source/isaaclab_tasks/test/test_environments_with_stage_in_memory.py +++ b/source/isaaclab_tasks/test/test_environments_with_stage_in_memory.py @@ -23,7 +23,7 @@ """Rest everything follows.""" import pytest -from env_test_utils import _run_environments, setup_environment +from env_test_utils import _run_environments, requires_pinocchio, setup_environment import isaaclab_tasks # noqa: F401 @@ -52,5 +52,15 @@ def test_environments_with_stage_in_memory_and_clone_in_fabric_disabled(task_nam if isaac_sim_version < 5: pytest.skip("Stage in memory is not supported in this version of Isaac Sim") + # Skip tasks with Mimic or Skillgen in the name on Windows + if "Mimic" in task_name or "Skillgen" in task_name: + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (Mimic/Skillgen not supported)") + + # Skip tasks that require pinocchio on Windows + if requires_pinocchio(task_name): + if sys.platform == "win32": + pytest.skip(f"Skipping {task_name} on Windows (requires pinocchio which is not supported)") + # run environments with stage in memory _run_environments(task_name, device, num_envs, create_stage_in_memory=True, disable_clone_in_fabric=True) diff --git a/source/isaaclab_tasks/test/test_factory_environments.py b/source/isaaclab_tasks/test/test_factory_environments.py index 7b445a453f1..f31ca1053e4 100644 --- a/source/isaaclab_tasks/test/test_factory_environments.py +++ b/source/isaaclab_tasks/test/test_factory_environments.py @@ -18,6 +18,9 @@ import isaaclab_tasks # noqa: F401 +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.parametrize("num_envs, device", [(32, "cuda"), (1, "cuda")]) @pytest.mark.parametrize("task_name", setup_environment(factory_envs=True, multi_agent=False)) diff --git a/source/isaaclab_tasks/test/test_multi_agent_environments.py b/source/isaaclab_tasks/test/test_multi_agent_environments.py index 21b3ac3b84d..8195a21be3c 100644 --- a/source/isaaclab_tasks/test/test_multi_agent_environments.py +++ b/source/isaaclab_tasks/test/test_multi_agent_environments.py @@ -19,6 +19,9 @@ import isaaclab_tasks # noqa: F401 +# Add markers for Windows and ARM platform support +pytestmark = [pytest.mark.windows, pytest.mark.arm] + @pytest.mark.parametrize("num_envs, device", [(32, "cuda"), (1, "cuda")]) @pytest.mark.parametrize("task_name", setup_environment(multi_agent=True)) diff --git a/tools/conftest.py b/tools/conftest.py index ed5db4cb69f..1e96dc95f7c 100644 --- a/tools/conftest.py +++ b/tools/conftest.py @@ -31,77 +31,146 @@ def capture_test_output_with_timeout(cmd, timeout, env): stdout_data = b"" stderr_data = b"" + print(f"šŸ” DEBUG: Platform detected: {sys.platform}") + print(f"šŸ” DEBUG: Command to execute: {cmd}") + print(f"šŸ” DEBUG: Timeout: {timeout}s") + try: # Use Popen to capture output in real-time + print("šŸ” DEBUG: Starting subprocess.Popen...") process = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=False ) + print(f"šŸ” DEBUG: Process started with PID: {process.pid}") + + # Platform detection + is_windows = sys.platform == "win32" + print(f"šŸ” DEBUG: is_windows={is_windows}") + + if is_windows: + # Windows: Use threading to read stdout/stderr concurrently + import queue + import threading + + stdout_queue = queue.Queue() + stderr_queue = queue.Queue() + + def read_output(pipe, queue_obj, output_stream): + """Read from pipe and put in queue while streaming to console.""" + with contextlib.suppress(Exception): + while True: + chunk = pipe.read(1024) + if not chunk: + break + queue_obj.put(chunk) + # Stream to console in real-time + output_stream.buffer.write(chunk) + output_stream.buffer.flush() + + # Start threads for reading stdout and stderr + stdout_thread = threading.Thread(target=read_output, args=(process.stdout, stdout_queue, sys.stdout)) + stderr_thread = threading.Thread(target=read_output, args=(process.stderr, stderr_queue, sys.stderr)) + stdout_thread.daemon = True + stderr_thread.daemon = True + stdout_thread.start() + stderr_thread.start() + + start_time = time.time() + + # Wait for process to complete or timeout + while process.poll() is None: + if time.time() - start_time > timeout: + process.kill() + # Give threads time to finish reading + stdout_thread.join(timeout=2) + stderr_thread.join(timeout=2) + # Collect remaining data from queues + while not stdout_queue.empty(): + stdout_data += stdout_queue.get_nowait() + while not stderr_queue.empty(): + stderr_data += stderr_queue.get_nowait() + return -1, stdout_data, stderr_data, True # -1 indicates timeout + time.sleep(0.1) - # Set up file descriptors for non-blocking reads - stdout_fd = process.stdout.fileno() - stderr_fd = process.stderr.fileno() + # Process finished, wait for threads to complete reading + stdout_thread.join(timeout=5) + stderr_thread.join(timeout=5) - # Set non-blocking mode (Unix systems only) - try: + # Collect all data from queues + while not stdout_queue.empty(): + stdout_data += stdout_queue.get_nowait() + while not stderr_queue.empty(): + stderr_data += stderr_queue.get_nowait() + + return process.returncode, stdout_data, stderr_data, False + + else: + # Unix/Linux: Use select for non-blocking I/O + stdout_fd = process.stdout.fileno() + stderr_fd = process.stderr.fileno() + + # Set non-blocking mode import fcntl for fd in [stdout_fd, stderr_fd]: flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - except ImportError: - # fcntl not available on Windows, use a simpler approach - pass - start_time = time.time() - - while process.poll() is None: - # Check for timeout - if time.time() - start_time > timeout: - process.kill() + start_time = time.time() + + while process.poll() is None: + # Check for timeout + if time.time() - start_time > timeout: + process.kill() + try: + remaining_stdout, remaining_stderr = process.communicate(timeout=5) + stdout_data += remaining_stdout + stderr_data += remaining_stderr + except subprocess.TimeoutExpired: + process.terminate() + remaining_stdout, remaining_stderr = process.communicate(timeout=1) + stdout_data += remaining_stdout + stderr_data += remaining_stderr + return -1, stdout_data, stderr_data, True # -1 indicates timeout + + # Check for available output using select try: - remaining_stdout, remaining_stderr = process.communicate(timeout=5) - stdout_data += remaining_stdout - stderr_data += remaining_stderr - except subprocess.TimeoutExpired: - process.terminate() - remaining_stdout, remaining_stderr = process.communicate(timeout=1) - stdout_data += remaining_stdout - stderr_data += remaining_stderr - return -1, stdout_data, stderr_data, True # -1 indicates timeout - - # Check for available output - try: - ready_fds, _, _ = select.select([stdout_fd, stderr_fd], [], [], 0.1) - - for fd in ready_fds: - with contextlib.suppress(OSError): - if fd == stdout_fd: - chunk = process.stdout.read(1024) - if chunk: - stdout_data += chunk - # Print to stdout in real-time - sys.stdout.buffer.write(chunk) - sys.stdout.buffer.flush() - elif fd == stderr_fd: - chunk = process.stderr.read(1024) - if chunk: - stderr_data += chunk - # Print to stderr in real-time - sys.stderr.buffer.write(chunk) - sys.stderr.buffer.flush() - except OSError: - # select failed, fall back to simple polling - time.sleep(0.1) - continue - - # Get any remaining output - remaining_stdout, remaining_stderr = process.communicate() - stdout_data += remaining_stdout - stderr_data += remaining_stderr - - return process.returncode, stdout_data, stderr_data, False + ready_fds, _, _ = select.select([stdout_fd, stderr_fd], [], [], 0.1) + + for fd in ready_fds: + with contextlib.suppress(OSError): + if fd == stdout_fd: + chunk = process.stdout.read(1024) + if chunk: + stdout_data += chunk + # Print to stdout in real-time + sys.stdout.buffer.write(chunk) + sys.stdout.buffer.flush() + elif fd == stderr_fd: + chunk = process.stderr.read(1024) + if chunk: + stderr_data += chunk + # Print to stderr in real-time + sys.stderr.buffer.write(chunk) + sys.stderr.buffer.flush() + except OSError: + # select failed, fall back to simple polling + time.sleep(0.1) + continue + + # Get any remaining output + remaining_stdout, remaining_stderr = process.communicate() + stdout_data += remaining_stdout + stderr_data += remaining_stderr + + return process.returncode, stdout_data, stderr_data, False except Exception as e: + error_msg = f"āŒ EXCEPTION in capture_test_output_with_timeout: {type(e).__name__}: {str(e)}" + print(error_msg) + import traceback + + traceback.print_exc() return -1, str(e).encode(), b"", False @@ -132,11 +201,14 @@ def create_timeout_test_case(test_file, timeout, stdout_data, stderr_data): return test_suite -def run_individual_tests(test_files, workspace_root, isaacsim_ci): +def run_individual_tests(test_files, workspace_root, isaacsim_ci, windows_platform=False, arm_platform=False): """Run each test file separately, ensuring one finishes before starting the next.""" failed_tests = [] test_status = {} + # Ensure tests directory exists for reports + os.makedirs("tests", exist_ok=True) + for test_file in test_files: print(f"\n\nšŸš€ Running {test_file} independently...\n") # get file name from path @@ -149,6 +221,7 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): if file_name in test_settings.PER_TEST_TIMEOUTS else test_settings.DEFAULT_TIMEOUT ) + print(f"ā±ļø Timeout set to: {timeout} seconds") # Prepare command cmd = [ @@ -165,15 +238,29 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): if isaacsim_ci: cmd.append("-m") cmd.append("isaacsim_ci") + elif windows_platform: + cmd.append("-m") + cmd.append("windows") + print("🪟 Adding Windows marker filter to command") + elif arm_platform: + cmd.append("-m") + cmd.append("arm") # Add the test file path last cmd.append(str(test_file)) + print(f"šŸ“ Command: {' '.join(cmd)}") + print(f"šŸ“‚ Working directory: {os.getcwd()}") + print(f"šŸ”§ Python executable: {sys.executable}") + print("ā³ Starting test execution...\n") + # Run test with timeout and capture output returncode, stdout_data, stderr_data, timed_out = capture_test_output_with_timeout(cmd, timeout, env) + print(f"\nāœ… Test execution completed. Return code: {returncode}, Timed out: {timed_out}") + if timed_out: - print(f"Test {test_file} timed out after {timeout} seconds...") + print(f"ā±ļø TIMEOUT: Test {test_file} timed out after {timeout} seconds...") failed_tests.append(test_file) # Create a special XML report for timeout tests with captured logs @@ -184,6 +271,7 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): # Write timeout report report_file = f"tests/test-reports-{str(file_name)}.xml" timeout_report.write(report_file) + print(f"šŸ“„ Timeout report written to: {report_file}") test_status[test_file] = { "errors": 1, @@ -196,12 +284,28 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): continue if returncode != 0: + print(f"āŒ Test returned non-zero exit code: {returncode}") + print(f"šŸ“¤ STDOUT ({len(stdout_data)} bytes):") + if stdout_data: + print(stdout_data.decode("utf-8", errors="replace")) + print(f"šŸ“¤ STDERR ({len(stderr_data)} bytes):") + if stderr_data: + print(stderr_data.decode("utf-8", errors="replace")) failed_tests.append(test_file) + else: + print("āœ… Test returned exit code 0") # check report for any failures report_file = f"tests/test-reports-{str(file_name)}.xml" + print(f"šŸ” Checking for report file: {report_file}") + print(f"šŸ” Current working directory: {os.getcwd()}") + print(f"šŸ” tests/ directory exists: {os.path.exists('tests/')}") + if os.path.exists("tests/"): + print(f"šŸ” Contents of tests/ directory: {os.listdir('tests/')}") + if not os.path.exists(report_file): - print(f"Warning: Test report not found at {report_file}") + print(f"āŒ WARNING: Test report not found at {report_file}") + print("āŒ This usually means pytest failed to run or crashed") failed_tests.append(test_file) test_status[test_file] = { "errors": 1, # Assume error since we can't read the report @@ -213,8 +317,12 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): } continue + print(f"āœ… Report file found at {report_file}") + try: + print(f"šŸ“– Parsing report file: {report_file}") report = JUnitXml.fromfile(report_file) + print("šŸ“Š Report parsed successfully") # Rename test suites to be more descriptive for suite in report: @@ -225,6 +333,7 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): # Write the updated report back report.write(report_file) + print(f"šŸ’¾ Updated report written back to: {report_file}") # Parse the integer values with None handling errors = int(report.errors) if report.errors is not None else 0 @@ -232,8 +341,16 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): skipped = int(report.skipped) if report.skipped is not None else 0 tests = int(report.tests) if report.tests is not None else 0 time_elapsed = float(report.time) if report.time is not None else 0.0 + + print( + f"šŸ“Š Test results: errors={errors}, failures={failures}, skipped={skipped}, tests={tests}," + f" time={time_elapsed}s" + ) except Exception as e: - print(f"Error reading test report {report_file}: {e}") + print(f"āŒ ERROR reading test report {report_file}: {type(e).__name__}: {e}") + import traceback + + traceback.print_exc() failed_tests.append(test_file) test_status[test_file] = { "errors": 1, @@ -265,18 +382,27 @@ def run_individual_tests(test_files, workspace_root, isaacsim_ci): def pytest_sessionstart(session): """Intercept pytest startup to execute tests in the correct order.""" + print("\n" + "=" * 80) + print("šŸš€ PYTEST SESSION START - Custom Test Runner") + print("=" * 80) + # Get the workspace root directory (one level up from tools) workspace_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + print(f"šŸ“‚ Workspace root: {workspace_root}") + source_dirs = [ os.path.join(workspace_root, "scripts"), os.path.join(workspace_root, "source"), ] + print(f"šŸ“ Source directories to scan: {source_dirs}") # Get filter pattern from environment variable or command line filter_pattern = os.environ.get("TEST_FILTER_PATTERN", "") exclude_pattern = os.environ.get("TEST_EXCLUDE_PATTERN", "") isaacsim_ci = os.environ.get("ISAACSIM_CI_SHORT", "false") == "true" + windows_platform = os.environ.get("WINDOWS_PLATFORM", "false") == "true" + arm_platform = os.environ.get("ARM_PLATFORM", "false") == "true" # Also try to get from pytest config if hasattr(session.config, "option") and hasattr(session.config.option, "filter_pattern"): @@ -291,6 +417,9 @@ def pytest_sessionstart(session): print(f"Exclude pattern: '{exclude_pattern}'") print(f"TEST_FILTER_PATTERN env var: '{os.environ.get('TEST_FILTER_PATTERN', 'NOT_SET')}'") print(f"TEST_EXCLUDE_PATTERN env var: '{os.environ.get('TEST_EXCLUDE_PATTERN', 'NOT_SET')}'") + print(f"IsaacSim CI mode: {isaacsim_ci}") + print(f"Windows platform: {windows_platform}") + print(f"ARM platform: {arm_platform}") print("=" * 50) # Get all test files in the source directories @@ -326,8 +455,30 @@ def pytest_sessionstart(session): if isaacsim_ci: new_test_files = [] for test_file in test_files: - with open(test_file) as f: - if "@pytest.mark.isaacsim_ci" in f.read(): + with open(test_file, encoding="utf-8") as f: + content = f.read() + if "@pytest.mark.isaacsim_ci" in content or "pytest.mark.isaacsim_ci" in content: + new_test_files.append(test_file) + test_files = new_test_files + elif windows_platform: + print("🪟 Filtering tests for Windows platform...") + new_test_files = [] + for test_file in test_files: + with open(test_file, encoding="utf-8") as f: + content = f.read() + if "@pytest.mark.windows" in content or "pytest.mark.windows" in content: + new_test_files.append(test_file) + print(f" āœ“ Including: {test_file}") + else: + print(f" āœ— Excluding (no windows marker): {test_file}") + test_files = new_test_files + print(f"🪟 Windows filtering complete: {len(test_files)} tests selected") + elif arm_platform: + new_test_files = [] + for test_file in test_files: + with open(test_file, encoding="utf-8") as f: + content = f.read() + if "@pytest.mark.arm" in content or "pytest.mark.arm" in content: new_test_files.append(test_file) test_files = new_test_files @@ -340,7 +491,9 @@ def pytest_sessionstart(session): print(f" - {test_file}") # Run all tests individually - failed_tests, test_status = run_individual_tests(test_files, workspace_root, isaacsim_ci) + failed_tests, test_status = run_individual_tests( + test_files, workspace_root, isaacsim_ci, windows_platform, arm_platform + ) print("failed tests:", failed_tests) @@ -350,6 +503,8 @@ def pytest_sessionstart(session): # create new full report full_report = JUnitXml() # read all reports and merge them + # Ensure tests directory exists + os.makedirs("tests", exist_ok=True) for report in os.listdir("tests"): if report.endswith(".xml"): print(report)