From 813dafd6b86f38272ed4befdd3a7360938056681 Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 15 Feb 2026 22:01:51 +0300 Subject: [PATCH] fix(updater,ci): headless tests and immediate app shutdown - stub PySide6 in test_updater_gui to run on linux runner without libGL - close main app immediately after launching updater, without blocking OK dialog --- main.py | 9 ++---- tests/test_updater_gui.py | 66 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 55dfc40..e515051 100644 --- a/main.py +++ b/main.py @@ -1212,12 +1212,9 @@ class VkChatManager(QMainWindow): version=latest_version, ) self._log_event("auto_update", f"Update {latest_version} started from {download_url}") - QMessageBox.information( - self, - "Обновление запущено", - "Обновление скачано. Открылось окно обновления.", - ) - QApplication.instance().quit() + self.status_label.setText("Статус: обновление запущено, закрываю приложение...") + self.close() + QTimer.singleShot(0, QApplication.instance().quit) return True except Exception as e: self._log_event("auto_update_failed", str(e), level="ERROR") diff --git a/tests/test_updater_gui.py b/tests/test_updater_gui.py index 8ec5e2e..b3d5304 100644 --- a/tests/test_updater_gui.py +++ b/tests/test_updater_gui.py @@ -1,10 +1,76 @@ import importlib.util +import sys import tempfile import unittest from pathlib import Path +import types + + +def _install_pyside6_stubs(): + if "PySide6" in sys.modules: + return + + pyside6_module = types.ModuleType("PySide6") + qtcore_module = types.ModuleType("PySide6.QtCore") + qtgui_module = types.ModuleType("PySide6.QtGui") + qtwidgets_module = types.ModuleType("PySide6.QtWidgets") + + class _Signal: + def __init__(self, *args, **kwargs): + pass + + def connect(self, *args, **kwargs): + pass + + class _QObject: + pass + + class _QThread: + def __init__(self, *args, **kwargs): + pass + + class _QTimer: + @staticmethod + def singleShot(*args, **kwargs): + pass + + class _QUrl: + @staticmethod + def fromLocalFile(path): + return path + + class _QDesktopServices: + @staticmethod + def openUrl(*args, **kwargs): + return True + + class _Widget: + def __init__(self, *args, **kwargs): + pass + + qtcore_module.QObject = _QObject + qtcore_module.Qt = type("Qt", (), {}) + qtcore_module.QThread = _QThread + qtcore_module.Signal = _Signal + qtcore_module.QTimer = _QTimer + qtcore_module.QUrl = _QUrl + qtgui_module.QDesktopServices = _QDesktopServices + qtwidgets_module.QApplication = _Widget + qtwidgets_module.QLabel = _Widget + qtwidgets_module.QProgressBar = _Widget + qtwidgets_module.QVBoxLayout = _Widget + qtwidgets_module.QWidget = _Widget + qtwidgets_module.QPushButton = _Widget + qtwidgets_module.QHBoxLayout = _Widget + + sys.modules["PySide6"] = pyside6_module + sys.modules["PySide6.QtCore"] = qtcore_module + sys.modules["PySide6.QtGui"] = qtgui_module + sys.modules["PySide6.QtWidgets"] = qtwidgets_module MODULE_PATH = Path("updater_gui.py") +_install_pyside6_stubs() SPEC = importlib.util.spec_from_file_location("updater_gui_under_test", MODULE_PATH) updater_gui = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(updater_gui)