Files
kernalix7--winpodx/tests/test_busydialog_and_layout.py
wehub-resource-sync 3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

80 lines
2.8 KiB
Python

# SPDX-License-Identifier: MIT
"""Regression tests for #550 (BusyDialog auto-close from a worker thread) and
#553 (word-wrap labels in a resizable scroll area must have a constant wrap
width so QBoxLayout::heightForWidth can't recurse to a SIGSEGV).
Runs headless via the offscreen Qt platform — no display server required.
"""
from __future__ import annotations
import os
import threading
import time
import pytest
# Must be set before the QApplication ctor.
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
pytest.importorskip("PySide6")
def _qapp():
from PySide6.QtWidgets import QApplication
return QApplication.instance() or QApplication([])
def test_busydialog_finish_from_worker_thread_closes_the_dialog():
# #550: a bare worker thread has no Qt event loop, so the old
# `QTimer.singleShot(0, dlg.finish)` never fired and the dialog hung open.
# finish() is now signal-based, so a cross-thread call queues accept() onto
# the GUI thread's exec() loop. The watchdog reject bounds the test so a
# regression fails fast instead of hanging CI.
from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QDialog
from winpodx.gui._widget_helpers import BusyDialog
_qapp()
dlg = BusyDialog(None, "Working", "Doing a thing...")
def worker():
time.sleep(0.05) # let exec()'s loop start first (the realistic case)
dlg.finish() # cross-thread close
threading.Thread(target=worker, daemon=True).start()
QTimer.singleShot(3000, dlg.reject) # watchdog: a hang would reject, not hang
result = dlg.exec()
assert result == QDialog.DialogCode.Accepted # closed via finish(), not the watchdog
def test_busydialog_finish_is_signal_based():
from winpodx.gui._widget_helpers import BusyDialog
_qapp()
dlg = BusyDialog(None, "T", "m")
# The cross-thread-safe close path: a signal connected to accept().
assert hasattr(dlg, "_close_requested")
def test_empty_panel_wrapped_labels_have_constant_width():
# #553: word-wrap QLabels inside the resizable app-list scroll area must be
# fixed-width, else their height tracks the viewport width and feeds back
# into QBoxLayout::heightForWidth -> unbounded recursion -> SIGSEGV on 6.11.
from PySide6.QtWidgets import QLabel
from winpodx.gui._widget_helpers import make_empty_panel
_qapp()
panel = make_empty_panel("A title that is reasonably long", "Some body text here too")
wrapped = [lbl for lbl in panel.findChildren(QLabel) if lbl.wordWrap()]
assert wrapped, "empty panel should have word-wrap labels"
for lbl in wrapped:
# setFixedWidth makes min == max == the constant wrap width.
assert lbl.minimumWidth() == lbl.maximumWidth() > 0, (
"word-wrap label in a resizable scroll area must have a fixed width"
)