Skip to content

Run each GUI test in its own forked subprocess#7

Merged
jacobson30-bot merged 1 commit into
mainfrom
isolate-gui-tests-forked
Jun 5, 2026
Merged

Run each GUI test in its own forked subprocess#7
jacobson30-bot merged 1 commit into
mainfrom
isolate-gui-tests-forked

Conversation

@jacobson30-bot
Copy link
Copy Markdown
Contributor

Follow-up to #6 (the between-tests Qt drain). The drain killed the run-aborting SIGSEGV, but a rarer soft manifestation of the same disease remained: PySide's wrapper cache (keyed by C++ pointer address) hands back a stale wrapper for a recycled address, e.g. 'QGraphicsItemGroup' object has no attribute 'connect' raised by a plain QMenu.addAction(...). You can't fully close cross-test Qt heap reuse from inside one shared process.

Fix

Run each GUI test in its own forked subprocess (pytest.mark.forked, applied to the existing GUI-test detection in conftest.py when the Qt preflight passes). A fresh address space per test makes cross-test object/heap reuse impossible, and any residual crash is contained to a single reported test instead of aborting the whole run.

Verified the containment locally: a @pytest.mark.forked test that segfaults reports CRASHED with signal 11 and the next test still runs (1 failed, 1 passed).

  • Adds pytest-forked to the dev/all extras. Without it, the marker is a harmless no-op and tests run in-process (prior behaviour).
  • Non-GUI tests are unmarked and keep running fast in-process.

Running CI several times to confirm the GUI suite is stably green.

🤖 Generated with Claude Code

Offscreen Qt is a single process-wide QApplication with a global QThreadPool
and a PySide wrapper cache keyed by C++ pointer address. Objects leaked by one
test get their address — and a stale Python wrapper — reused by a later test,
surfacing as an intermittent SIGSEGV or a bogus AttributeError on a recycled
wrapper (e.g. '.connect' on a QGraphicsItemGroup inside QMenu.addAction). The
between-tests drain narrowed the window but can't fully close it from inside a
shared process.

Mark every GUI test (the existing GUI_TEST_MODULES / mixed-Qt detection) with
pytest.mark.forked when the Qt preflight passes, so each runs in a fresh address
space. Cross-test object/heap reuse becomes impossible, and any residual crash
is contained to a single reported test ("CRASHED with signal N") instead of
aborting the whole run. Requires pytest-forked (added to the dev/all extras);
without it the marker is a harmless no-op and tests run in-process as before.

Non-GUI tests are unaffected — they are not marked and keep running in-process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jacobson30-bot jacobson30-bot merged commit 731882a into main Jun 5, 2026
9 checks passed
@jacobson30-bot jacobson30-bot deleted the isolate-gui-tests-forked branch June 5, 2026 02:12
jacobson30-bot added a commit that referenced this pull request Jun 5, 2026
Several test modules construct a real QApplication (via a local `qapp`
fixture or inline) but were absent from conftest's GUI_TEST_MODULES /
MIXED_QT sets. Consequently they bypassed the `_qt_application_preflight`
subprocess gate -- hard-crashing (SIGABRT) instead of skipping on boxes
without a working Qt offscreen plugin -- and were not marked
`pytest.mark.forked`, so in CI they ran in-process, re-exposing the
cross-test Qt wrapper/heap reuse that PR #7 eliminated for the other
GUI modules.

Add the fully-GUI modules (every test needs Qt) to GUI_TEST_MODULES:
fft_phase_gui, fft_selection_gui, mains_pickup_gui,
roi_resize_handles_canvas, worker_signals_lifetime.

Add the mixed modules (only some tests use the qapp fixture; the rest
are pure math/text and must keep running) to MIXED_QT_FIXTURE_MODULES:
fft_viewer_utils, definitions_dialog, lattice_grid. The lattice_grid
inline-QApplication export test stays gated via MIXED_QT_TESTS, which
fixturename-based gating cannot see.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant