Files
qelectrotech-source-mirror/tests/modal-quit-regression

Quit-during-modal regression test

Guards the abort reported in #904.

QETDiagramEditor::openAndAddProject() shows BackupDialog as a stack object parented to the editor and exec()s it, and QET::QetMessageBox does the same for every message box. exec() runs a nested event loop. Closing the editor during that loop turns WA_DeleteOnClose into a deleteLater() that the nested loop processes, so ~QWidget() deletes the stack-allocated dialog and the process aborts. QETMainWindow::refuseCloseWhileModal() refuses such a close.

Running it

tests/modal-quit-regression/run.sh --binary build/qelectrotech

Needs gdb with Python. It runs on the offscreen platform, so no X server or window manager is required. Takes about ten seconds.

It is also registered with CTest on Linux (ctest -R modal_quit_regression).

Exit codes: 0 survived, 1 crashed, 2 the scenario did not happen, 77 it could not run here.

That last one matters for a release build. The scenario is driven by calling QETApp::instance() and QETApp::quitQET() through gdb, so those symbols have to survive into the binary: against a stripped build there is nothing to call, and the same applies without gdb or with a gdb built without Python. All three report 77, which is CTest's SKIP_RETURN_CODE, so a build this test cannot drive is skipped rather than failed.

How it works, and why this way

On Linux there is nothing to click: the menu bar belongs to the window the dialog blocks, and Qt ignores window-manager close requests for a blocked window. The reported route is macOS, where File > Quit lives in the application menu and stays usable during a modal — and what it does is call close() while the dialog's loop is running. The test does the same thing through gdb:

  1. break on QDialog::exec();
  2. let the dialog's loop run, then interrupt it, so the main thread is inside the nested loop — the only place the bug exists, because a deleteLater() posted before exec() started is not processed by that loop;
  3. call QETApp::quitQET(), which closes every editor;
  4. let it run: an unfixed build aborts within a second.

It matches no window titles and no window ids. Titles are translated, and tests/ipc-regression once shipped a pass that could not fail because of that.

Covering the element editor too

--project decides which editor is under test, because QElectroTech picks the editor from the file extension. Pass a .qet and the run exercises QETDiagramEditor; pass a read-only .elmt and it exercises QETElementEditor, which shows a "file is read-only" message box on open and so reaches the same nested loop by a different route:

chmod -w some.elmt
tests/modal-quit-regression/run.sh --binary build/qelectrotech --project some.elmt

That distinction is why the file name is preserved when it is copied into the sandbox. Renaming a .elmt to project.qet would make the run silently test the diagram editor again, and still report PASS.

Checked both ways, in both editors, before being committed: without the fix the diagram-editor run aborts with signal 6 under ~QETDiagramEditor() and the element-editor run with double free or corruption under ~QETElementEditor(); with the fix both survive.