Skip to content

Kit re-use per test session#6577

Open
mataylor-nvidia wants to merge 4 commits into
isaac-sim:developfrom
mataylor-nvidia:mataylor/kit-test-reuse-fixture
Open

Kit re-use per test session#6577
mataylor-nvidia wants to merge 4 commits into
isaac-sim:developfrom
mataylor-nvidia:mataylor/kit-test-reuse-fixture

Conversation

@mataylor-nvidia

Copy link
Copy Markdown

Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
List any dependencies that are required for this change.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (existing functionality will not work without user modification)
  • Documentation update

Screenshots

Please attach before and after screenshots of the change if applicable.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • 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

@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team infrastructure labels Jul 16, 2026
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces Kit session reuse across multiple test files within a single pytest subprocess, reducing the overhead of repeatedly starting and stopping the Isaac Sim Kit application. A new _kit_session_plugin.py pytest plugin monkey-patches AppLauncher.__init__ to share the first launcher instance, writes per-file JUnit XML reports, and resets USD/timeline state between files; conftest.py gains _make_batches and _run_batch helpers that group compatible test files into shared-session subprocesses controlled by ISAACLAB_KIT_SESSION_FILES.

  • _kit_session_plugin.py: Patches AppLauncher.__init__ to copy instance state from the first launcher for all subsequent files; adds a close() no-op guard on the shared SimulationApp; _KitSessionPlugin accumulates TestCase objects per file, drains orphaned _pending entries in pytest_sessionfinish, and writes one JUnit XML per file at module teardown via the _kit_module_fence autouse fixture.
  • conftest.py: _make_batches groups consecutive camera-compatible, non-solo files up to ISAACLAB_KIT_SESSION_FILES per batch; _run_batch launches the batch subprocess with -p _kit_session_plugin and parses per-file reports; run_individual_tests dispatches multi-file batches to _run_batch and single files to the existing _run_one_pass path, tracking cold_cache_applied and combined timeouts.

Confidence Score: 4/5

The batch execution path works correctly for normal runs, but startup hangs in a batch are reported as failures without the retry attempts that the single-file path performs, making the batch path more brittle and the error message actively misleading.

The core session-reuse mechanism and JUnit report writing are sound. The one concrete defect is in _run_batch: when a batch subprocess hits a startup hang, the single-file path (_run_one_pass) would retry up to STARTUP_HANG_RETRIES=2 more times, but _run_batch calls capture_test_output_with_timeout exactly once and marks every file in the batch as STARTUP_HANG. The resulting error message ('retried N time(s)') actively conceals this by reporting retries that never happened, inflating false-failure counts and complicating root-cause analysis.

tools/conftest.py — specifically the _run_batch function and its startup-hang error handling branch.

Important Files Changed

Filename Overview
tools/_kit_session_plugin.py New pytest plugin for shared Kit sessions: monkey-patches AppLauncher.init to reuse the first launcher, adds a close() no-op guard on the shared SimulationApp, accumulates per-file JUnit XML reports via _KitSessionPlugin, and resets USD/timeline state between test files. Previous review threads about pending-case draining and close() interception are addressed in this revision.
tools/conftest.py Adds _make_batches (groups compatible test files), _run_batch (shared-session subprocess), and updates run_individual_tests to dispatch multi-file batches. The batch path is missing the startup-hang retry loop present in _run_one_pass, and the error message falsely claims STARTUP_HANG_RETRIES retries were performed.
source/isaaclab/changelog.d/mataylor-kit-test-reuse-fixture.skip Empty changelog skip-marker file; no functional content.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CI as conftest.py run_individual_tests
    participant MB as _make_batches
    participant RB as _run_batch
    participant Sub as subprocess (pytest -p _kit_session_plugin)
    participant KSP as _KitSessionPlugin
    participant AL as AppLauncher (_shared_init)

    CI->>MB: test_files, batch_size
    MB-->>CI: [[file1, file2], [file3], ...]

    loop each batch
        alt multi-file batch
            CI->>RB: batch, timeout, env
            RB->>Sub: pytest -p _kit_session_plugin file1 file2
            Sub->>KSP: pytest_configure → _patch_app_launcher
            Sub->>AL: file1 AppLauncher(...) → original_init, saves _shared_launcher, no-ops close()
            Sub->>AL: file2 AppLauncher(...) → copies __dict__ from _shared_launcher
            Sub->>KSP: _kit_module_fence teardown file1 → _write_report + _reset_kit_state
            Sub->>KSP: _kit_module_fence teardown file2 → _write_report + _reset_kit_state
            Sub->>KSP: pytest_sessionfinish → drain _pending, flush remaining suites
            RB-->>CI: xml_reports, file_statuses, failed_files
        else single file
            CI->>CI: _run_one_pass (existing path, with startup-hang retries)
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CI as conftest.py run_individual_tests
    participant MB as _make_batches
    participant RB as _run_batch
    participant Sub as subprocess (pytest -p _kit_session_plugin)
    participant KSP as _KitSessionPlugin
    participant AL as AppLauncher (_shared_init)

    CI->>MB: test_files, batch_size
    MB-->>CI: [[file1, file2], [file3], ...]

    loop each batch
        alt multi-file batch
            CI->>RB: batch, timeout, env
            RB->>Sub: pytest -p _kit_session_plugin file1 file2
            Sub->>KSP: pytest_configure → _patch_app_launcher
            Sub->>AL: file1 AppLauncher(...) → original_init, saves _shared_launcher, no-ops close()
            Sub->>AL: file2 AppLauncher(...) → copies __dict__ from _shared_launcher
            Sub->>KSP: _kit_module_fence teardown file1 → _write_report + _reset_kit_state
            Sub->>KSP: _kit_module_fence teardown file2 → _write_report + _reset_kit_state
            Sub->>KSP: pytest_sessionfinish → drain _pending, flush remaining suites
            RB-->>CI: xml_reports, file_statuses, failed_files
        else single file
            CI->>CI: _run_one_pass (existing path, with startup-hang retries)
        end
    end
Loading

Reviews (2): Last reviewed commit: "Address Greptile review: fix _pending dr..." | Re-trigger Greptile

Comment thread tools/_kit_session_plugin.py Outdated
Comment on lines +216 to +219
def pytest_sessionfinish(self, session, exitstatus) -> None:
"""Flush any per-file report not yet written (e.g. fixture teardown skipped)."""
for filepath in list(self._suites):
self._write_report(filepath)

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.

P1 Pending test cases silently dropped on early session exit

pytest_sessionfinish flushes suites but never drains _pending. Any test whose setup phase passed (creating an entry in _pending) but whose call phase never fires — because the session was aborted via --maxfail, KeyboardInterrupt, or a collection error — will simply disappear from the JUnit output. The conftest already wrote the file-level report path, so downstream tooling will count zero results for these tests rather than seeing them as errors, masking the gap entirely.

Consider iterating self._pending in pytest_sessionfinish and adding each orphaned case to its suite with an Error result before flushing.

Comment thread tools/conftest.py Outdated
Comment on lines +956 to +957
batch_env["ISAACLAB_REUSE_KIT_SESSION"] = "1"
# Make _kit_session_plugin importable in the subprocess (-p needs it on sys.path).

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.

P2 ISAACLAB_REUSE_KIT_SESSION=1 is injected into the subprocess environment but is never read by _kit_session_plugin.py or any other code. Session reuse is driven entirely by the -p _kit_session_plugin flag already present in the command. This dead env-var may mislead future maintainers into thinking it controls the reuse behaviour.

Suggested change
batch_env["ISAACLAB_REUSE_KIT_SESSION"] = "1"
# Make _kit_session_plugin importable in the subprocess (-p needs it on sys.path).
# Make _kit_session_plugin importable in the subprocess (-p needs it on sys.path).

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread tools/_kit_session_plugin.py Outdated
Comment on lines +99 to +110
original_init = AppLauncher.__init__

def _shared_init(self, launcher_args=None, **kwargs):
global _shared_launcher
if _shared_launcher is not None:
# Reuse the existing Kit session: copy all instance state.
self.__dict__.update(_shared_launcher.__dict__)
return
original_init(self, launcher_args, **kwargs)
_shared_launcher = self

AppLauncher.__init__ = _shared_init

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.

P2 No guard against test-file teardown closing the shared SimulationApp

The shared-init patch intercepts AppLauncher.__init__ so subsequent files reuse the first launcher's state, but does nothing to intercept close() or __del__. If any test file in the batch calls something like simulation_app.close() in its teardown_module, the underlying SimulationApp is shut down, and every file that follows in the same batch will fail with cryptic startup/import errors rather than a clear "shared session was closed" message. There is no documented contract telling test authors that closing the launcher is forbidden in batch mode, nor any runtime guard that detects and surfaces this condition.

@mataylor-nvidia mataylor-nvidia changed the title progress Kit re-use per test session Jul 16, 2026
…rd SimulationApp.close

- pytest_sessionfinish now drains _pending before flushing suites; tests
  interrupted between setup and call phases are recorded as Error entries
  instead of silently vanishing from JUnit reports.
- Remove dead ISAACLAB_REUSE_KIT_SESSION env-var from _run_batch; session
  reuse is controlled entirely by the -p _kit_session_plugin flag.
- Patch simulation_app.close() to a no-op after the shared launcher is
  created, preventing test-file teardown from shutting down Kit for
  remaining files in the batch.
@mataylor-nvidia

Copy link
Copy Markdown
Author

@greptile review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working infrastructure isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant