Another exception: one non-converted code path in projectprintwindow.cpp to be followed-up. One issue found in the Qt6 code path of diagramview.cpp which has been fixed.
IPC open-forwarding regression test
Guards the use-after-free fixed in PR #868.
With QElectroTech already running, opening a .qet from a file manager forwards
the path to the running instance over SingleApplication's local socket. Before
#868, QETApp::receiveMessage() called openFiles() directly — inside the
socket's readyRead handler. Loading the project raises a modal backup prompt
whose exec() runs a nested event loop while that handler is still on the
stack. The second instance then exits, its socket is deleted, and when the
prompt is dismissed and the stack unwinds, QMetaObject::activate() carries on
emitting on freed memory.
Running it
tests/ipc-regression/run.sh --binary build/qelectrotech
Needs Xvfb, openbox and xdotool. It allocates its own display, runs
entirely in a sandbox, and cleans up after itself.
Exit codes: 0 survived, 1 crashed, 2 inconclusive or unusable input.
Requirements that are not obvious
Qt 6. An unfixed Qt 5 build survives this scenario every time — measured, not assumed. The script refuses to run on a Qt 5 binary rather than report a pass that cannot fail. Reproduced on Qt 6.10.2.
A Debug build. Whether a use-after-free faults depends on what the allocator
does with the freed block. The same unfixed commit crashes 3 times in 3 built
Debug, and survives every attempt built -O3 -DNDEBUG.
A large project. The load has to take long enough that the prompt is up
while the socket handler is still on the stack. examples/industrial.qet
(2.6 MB, ~4 s) is used by default.
Why it repeats
The crash is a race: the second instance's disconnect has to be processed while
the nested event loop is on the stack. Single attempts reproduced it 2 times in
3, so one attempt would let a reintroduced bug through about a third of the
time. Three attempts is roughly 96%; QET_IPC_ATTEMPTS changes it. Any single
crash fails the run, and survival must be unanimous.
Validation
| build | result |
|---|---|
ceda1e082 (before #868) |
3/3 crashed, exit 139 |
199444b6 (after #868) |
3/3 survived |
If this test is changed, re-validate it against a build without the fix. A gate nobody has watched go red is not evidence.
Things that made earlier versions of this test pass a crashing build
Recorded because each one looked exactly like success.
Answering the wrong dialog. A progress dialog appears several seconds before the backup prompt and both stay up. Dismissing the progress dialog satisfies a naive "a modal appeared, dismiss it" check while never touching the crash path.
Filtering that dialog out by title. That is a locale filter: it worked in French and silently stopped working in English. The script now dismisses every dialog and identifies none, and treats a run that saw fewer than two distinct dialogs as inconclusive rather than passing.
Sharing a home directory between attempts. A crashed attempt leaves a backup
file, so the next one opens with the restore prompt instead of the backup
prompt — a path that never unwinds the socket stack. Each attempt now gets a
fresh HOME.