tests: refactor the Qt test harness to support running QObject's within a QCoreApplication#10699
tests: refactor the Qt test harness to support running QObject's within a QCoreApplication#10699accumulator wants to merge 5 commits into
Conversation
|
Interesting. These new Qt testcases unearthed a pre-existing bug. There are some lifecycle issues with some QObject subclasses (in particular I'll submit fixes in another PR. |
bfb1569 to
ba358b1
Compare
- remove lambda style signal handlers, unless absolutely necessary - replace signal self-connects with QMetaObject.invokeMethod - refactor ad-hoc broadcastSucceeded/broadcastFailed signal self-connect in QETxDetails to callbacks
There was a problem hiding this comment.
Did you see the segfault on one of the CI unittest runs? I can reproduce that on python 3.14 with the debug env vars set, with a high reliability (75+%).
(the other PR, #10701, does not fix it either - I tried merging f62096f onto this branch)
There is no segfault on master.
log
$ PYTHONASYNCIODEBUG=1 PYTHONDEVMODE=1 pytest tests
=================================================== test session starts ====================================================
platform linux -- Python 3.14.6, pytest-9.1.0, pluggy-1.6.0
rootdir: /home/user/wspace/electrum
collected 980 items
tests/plugins/test_revealer.py ... [ 0%]
tests/plugins/test_timelock_recovery.py ....... [ 1%]
tests/qml/test_qewallet.py Fatal Python error: Aborted
Thread 0x00007f80d7fff6c0 [asyncio_0] (most recent call first):
File "/opt/cpython-versions/3.14.6/lib/python3.14/concurrent/futures/thread.py", line 116 in _worker
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1024 in run
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1082 in _bootstrap_inner
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1044 in _bootstrap
Thread 0x00007f80dc9fe6c0 [EventLoop] (most recent call first):
File "/opt/cpython-versions/3.14.6/lib/python3.14/asyncio/futures.py", line 411 in _chain_future
<invalid frame>
Current thread 0x00007f80dd1ff6c0 [QtTestThread] (most recent call first):
File "/home/user/wspace/electrum/tests/qml/qt_util.py", line 145 in waitForSignal
File "/home/user/wspace/electrum/tests/qml/test_qewallet.py", line 175 in test_auth_protected_methods
File "/home/user/wspace/electrum/tests/qml/qt_util.py", line 189 in decorator
File "/home/user/wspace/electrum/tests/qml/qt_util.py", line 22 in doInvoke
File "/home/user/wspace/electrum/tests/qml/qt_util.py", line 91 in start_qt_task
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1024 in run
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1082 in _bootstrap_inner
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 1044 in _bootstrap
Thread 0x00007f80e9beebc0 [pytest] (most recent call first):
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 373 in wait
File "/opt/cpython-versions/3.14.6/lib/python3.14/threading.py", line 670 in wait
File "/home/user/wspace/electrum/tests/qml/qt_util.py", line 171 in decorator
File "/opt/cpython-versions/3.14.6/lib/python3.14/unittest/case.py", line 615 in _callTestMethod
File "/opt/cpython-versions/3.14.6/lib/python3.14/unittest/case.py", line 669 in run
File "/opt/cpython-versions/3.14.6/lib/python3.14/unittest/case.py", line 725 in __call__
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/unittest.py", line 410 in runtest
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 184 in pytest_runtest_call
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_callers.py", line 121 in _multicall
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_manager.py", line 120 in _hookexec
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_hooks.py", line 512 in __call__
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 250 in <lambda>
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 361 in from_call
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 249 in call_and_report
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 139 in runtestprotocol
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/runner.py", line 118 in pytest_runtest_protocol
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_callers.py", line 121 in _multicall
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_manager.py", line 120 in _hookexec
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_hooks.py", line 512 in __call__
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/main.py", line 408 in pytest_runtestloop
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_callers.py", line 121 in _multicall
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_manager.py", line 120 in _hookexec
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_hooks.py", line 512 in __call__
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/main.py", line 384 in _main
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/main.py", line 330 in wrap_session
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/main.py", line 377 in pytest_cmdline_main
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_callers.py", line 121 in _multicall
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_manager.py", line 120 in _hookexec
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/pluggy/_hooks.py", line 512 in __call__
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/config/__init__.py", line 229 in _main
File "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/_pytest/config/__init__.py", line 253 in _console_main
File "/home/user/wspace/electrum/.venv314/bin/pytest", line 6 in <module>
Current thread's C stack trace (most recent call first):
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at _Py_DumpStack+0x2b [0x560b81bc8c9b]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at +0x318eca [0x560b81bdeeca]
Binary file "/lib/x86_64-linux-gnu/libc.so.6", at +0x3fdf0 [0x7f80e9c2fdf0]
Binary file "/lib/x86_64-linux-gnu/libc.so.6", at +0x9495c [0x7f80e9c8495c]
Binary file "/lib/x86_64-linux-gnu/libc.so.6", at gsignal+0x12 [0x7f80e9c2fcc2]
Binary file "/lib/x86_64-linux-gnu/libc.so.6", at abort+0x22 [0x7f80e9c184ac]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _Z9qt_assertPKcS0_i+0x0 [0x7f80e26bff56]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at +0x100954 [0x7f80e2700954]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZNK14QMessageLogger5fatalEPKcz+0x105 [0x7f80e26c17e7]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x226117 [0x7f80dfa26117]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x22e98d [0x7f80dfa2e98d]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x22f82f [0x7f80dfa2f82f]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZN7QObject5eventEP6QEvent+0x279 [0x7f80e27de899]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x1e2f3e [0x7f80df9e2f3e]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent+0x150 [0x7f80e2785240]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZN23QCoreApplicationPrivate16sendPostedEventsEP7QObjectiP11QThreadData+0x275 [0x7f80e2788ec5]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at +0x47ef47 [0x7f80e2a7ef47]
Binary file "/lib/x86_64-linux-gnu/libglib-2.0.so.0", at +0x5c3c5 [0x7f80e39033c5]
Binary file "/lib/x86_64-linux-gnu/libglib-2.0.so.0", at +0x5e5f7 [0x7f80e39055f7]
Binary file "/lib/x86_64-linux-gnu/libglib-2.0.so.0", at g_main_context_iteration+0x30 [0x7f80e3905d60]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE+0x6e [0x7f80e2a7e5de]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6", at _ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE+0x24b [0x7f80e27913bb]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0xdf33b [0x7f80df8df33b]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at +0x15c1fb [0x560b81a221fb]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at _PyObject_MakeTpCall+0xc0 [0x560b819b22f0]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at _PyEval_EvalFrameDefault+0xa7f2 [0x560b81b2e722]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at +0x26fbac [0x560b81b35bac]
Binary file "/home/user/wspace/electrum/.venv314/bin/python3", at +0xedabb [0x560b819b3abb]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x22e280 [0x7f80dfa2e280]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x22e6d0 [0x7f80dfa2e6d0]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x22e87c [0x7f80dfa2e87c]
Binary file "/home/user/wspace/electrum/.venv314/lib/python3.14/site-packages/PyQt6/QtCore.abi3.so", at +0x23480e [0x7f80dfa3480e]
<truncated rest of calls>
Extension modules: multidict._multidict, yarl._quoting_c, propcache._helpers_c, aiohttp._http_writer, aiohttp._http_parser, aiohttp._websocket.mask, aiohttp._websocket.reader_c, frozenlist._frozenlist, _cffi_backend, PyQt6.QtCore, PyQt6.QtNetwork, PyQt6.QtQml, PyQt6.QtGui, PyQt6.QtQuick, PyQt6.QtMultimedia (total: 15)
Aborted (core dumped)
$ pip freeze --all
aiohappyeyeballs==2.6.2
aiohttp==3.14.1
aiohttp_socks==0.11.0
aiorpcX==0.25.0
aiosignal==1.4.0
attrs==22.2.0
certifi==2026.6.17
cffi==2.0.0
cryptography==49.0.0
dnspython==2.4.2
-e git+ssh://git@github.com/spesmilo/electrum.git@ba358b1f0a66f324a15b7cc4d74c5787fde5c9aa#egg=Electrum
electrum-aionostr==0.1.0
electrum-ecc==0.0.7
frozenlist==1.8.0
idna==3.18
iniconfig==2.3.0
jsonpatch==1.33
jsonpointer==3.1.1
multidict==6.7.1
packaging==26.2
pip==26.1.2
pluggy==1.6.0
propcache==0.5.2
protobuf==7.35.1
pycparser==3.0
Pygments==2.20.0
PyQt6==6.10.2
PyQt6-Qt6==6.10.2
PyQt6_sip==13.11.1
pytest==9.1.0
python-socks==2.8.1
QDarkStyle==3.2.3
qrcode==8.2
QtPy==2.4.3
yarl==1.24.2
There was a problem hiding this comment.
Did you see the segfault on one of the CI unittest runs? I can reproduce that on python 3.14 with the debug env vars set, with a high reliability (75+%).
Yeah, it was supposed to be a draft PR..
There was a problem hiding this comment.
- move QEWallet.getInstanceFor to QEDaemon.getQEWalletInstanceFor - parent QEWallet instances to QEDaemon, parent listmodels to QEWallet - introduce QEDaemon.unloadWallet method for clean teardown of QEWallet instances - destroyed signal handler on QEWallet is now plain bound method style
ba358b1 to
a1bc76c
Compare
…in a QCoreApplication and its event loop, so we can fully test behaviour with more complex QObject hierarchies and check for signal emits add some initial tests for QEWallet
A qt_test body runs inside app.exec() (one loop level deep, via doInvoke), so a QObject.deleteLater() posted there is never delivered by processEvents() -- Qt only delivers DeferredDelete once the loop unwinds to the level at which it was posted. The C++ objects, and everything they reference (e.g. a QEWallet's self.wallet), lingered until some later, racy spin of the loop. Flush them synchronously via sendPostedEvents(None, DeferredDelete), then gc.collect() so reference cycles (e.g. wallet<->txbatcher) are reclaimed and their EventListener.__del__ unregisters callbacks. This keeps QML tests from leaking wallet objects and callbacks into later tests (e.g. test_daemon's GC sanity checks). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
a1bc76c to
7b1d0cb
Compare
tests: refactor the
Qttest harness to support runningQObject's within aQCoreApplicationand its eventloop, so we can fully test behaviour with more complex
QObjecthierarchies and check for signal emits.adds some initial tests for
QEWallet