Files
kefu/deploy/dpi-adaptation-20260916/payload/test_ui_display.py
T
2026-09-21 10:34:06 +08:00

110 lines
5.1 KiB
Python

"""Display geometry and native UI metrics without real accounts or listeners."""
import os
os.environ.setdefault('QT_QPA_PLATFORM', 'offscreen')
from types import SimpleNamespace
from unittest import TestCase, mock
from PySide6.QtCore import QRect
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QVBoxLayout
import wechat_gui_qt as ui
class DisplayHost(QMainWindow):
_set_initial_geometry = ui.MainWindow._set_initial_geometry
_display_metrics_changed = ui.MainWindow._display_metrics_changed
_screen_changed = ui.MainWindow._screen_changed
_apply_window_ui_scale = ui.MainWindow._apply_window_ui_scale
def __init__(self, area):
super().__init__()
self.test_screen = SimpleNamespace(availableGeometry=lambda: area,
availableGeometryChanged=mock.Mock(), logicalDotsPerInchChanged=mock.Mock())
self._scale_timer = mock.Mock()
self.runtime_settings = {'scale_ratio': '自动'}
def screen(self):
return self.test_screen
class DisplayTest(TestCase):
@classmethod
def setUpClass(cls):
cls.app = QApplication.instance() or QApplication([])
def tearDown(self):
ui.sync_ui_scale(1630, 920, factor=1)
def test_initial_window_stays_inside_small_and_offset_displays(self):
for area in [QRect(0,0,960,540), QRect(-1280,0,1280,680), QRect(1920,0,640,360), QRect(0,0,320,240)]:
with self.subTest(area=area):
host=DisplayHost(area)
host._set_initial_geometry()
self.assertTrue(area.contains(host.geometry()), (area,host.geometry()))
host.deleteLater()
def test_cross_monitor_clamps_to_current_available_geometry(self):
area=QRect(-960,100,960,500);host=DisplayHost(area)
host.setGeometry(0,0,1630,920)
host.console_shell=SimpleNamespace(page_ready=True,page=lambda:SimpleNamespace(runJavaScript=mock.Mock()))
host._display_metrics_changed()
self.assertTrue(area.contains(host.geometry()))
self.assertLessEqual(host.minimumWidth(),area.width())
self.assertLessEqual(host.minimumHeight(),area.height())
host._scale_timer.start.assert_called_once()
host.deleteLater()
def test_screen_notifications_are_not_duplicated(self):
host=DisplayHost(QRect(0,0,960,540));first=host.screen()
with mock.patch.object(ui.QTimer,'singleShot'):
host._screen_changed(first);host._screen_changed(first)
first.availableGeometryChanged.connect.assert_called_once()
first.logicalDotsPerInchChanged.connect.assert_called_once()
second=SimpleNamespace(availableGeometryChanged=mock.Mock(),logicalDotsPerInchChanged=mock.Mock())
host._screen_changed(second)
first.availableGeometryChanged.disconnect.assert_called_once()
second.availableGeometryChanged.connect.assert_called_once()
host.deleteLater()
def test_normal_window_resize_does_not_reduce_font_size(self):
host=DisplayHost(QRect(0,0,1920,1080))
for width,height in [(1630,920),(960,500),(640,330)]:
host.resize(width,height);host._apply_window_ui_scale()
self.assertEqual(ui.ui_scale_factor(),1)
host.runtime_settings['scale_ratio']='300%'
host._apply_window_ui_scale();self.assertEqual(ui.ui_scale_factor(),3)
host.deleteLater()
def test_native_dimensions_font_and_spacing_scale_together_without_compounding(self):
ui.sync_ui_scale(1630,920,factor=1)
panel=QWidget();layout=QVBoxLayout(panel);layout.setContentsMargins(10,10,10,10);layout.setSpacing(8)
button=QPushButton('测试',panel);button.setFixedSize(120,40);button.setStyleSheet('font-size:14px;')
layout.addWidget(button)
for scale in (2,3,1,2,1):
ui.sync_ui_scale(640,330,panel,factor=scale)
self.assertEqual(button.width(),120*scale)
self.assertEqual(button.height(),40*scale)
self.assertEqual(layout.spacing(),8*scale)
self.assertEqual(layout.contentsMargins().left(),10*scale)
self.assertIn(f'font-size:{14*scale}px',button.styleSheet())
panel.deleteLater()
def test_automatic_and_invalid_preferences_follow_system(self):
for preference in ('自动', '', None, 'nan', '999%', '2'):
self.assertEqual(ui.interface_scale(preference),1)
self.assertEqual(ui.interface_scale('125%'),1.25)
self.assertEqual(ui.interface_scale('200%'),2)
self.assertEqual(ui.interface_scale('300%'),3)
def test_saved_preference_does_not_set_process_dpi_multiplier(self):
import inspect
self.assertNotIn('QT_SCALE_FACTOR',inspect.getsource(ui))
def test_monitor_removal_tolerates_a_deleted_screen_object(self):
class DeletedScreen:
def __getattr__(self, name):
raise RuntimeError("Internal C++ object already deleted")
host=DisplayHost(QRect(0,0,960,540));host._watched_screen=DeletedScreen()
with mock.patch.object(ui.QTimer,'singleShot'):
host._screen_changed(host.screen())
self.assertIs(host._watched_screen,host.screen())
host.deleteLater()