zapretgui/tests/test_page_float_in_contract.py
loop-uh 4a124c3a41
All checks were successful
Zapret source guards / validate (push) Successful in 15s
Меньше лишней работы процессора: выплывание карточек без перерисовки каждый кадр, один снимок процессов, без полной записи истории оркестратора на каждую строку
- Эффект выплывания снимает картинку карточки не чаще раза в 120 мс вместо
  каждого кадра (на win10 это было 43% главного потока при переключении
  страниц; замер без экрана: ~3 раза меньше времени процессора).
- Монитор процессов winws делает один снимок Toolhelp за проверку вместо трёх.
- Оркестратор в цикле чтения вывода пишет только изменившиеся цели истории
  (flush_history), полная запись — при остановке; LOCK/UNLOCK сохраняются
  одной транзакцией вместо 18.
- Полосы загрузки IndeterminateProgressBar создаются остановленными и не
  крутят анимацию у скрытой полосы.
- Строка статуса оркестратора не пересчитывает стиль, если текст и цвет те же.
2026-10-03 16:41:39 +03:00

151 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Появление есть у каждой страницы: его нельзя тихо выключить.
Все страницы реестра — BasePage (у неё выплывание подключено к content).
Большой виджет с ручной отрисовкой выплывает своим входом play_float_in,
который вызывает общий модуль ui.widgets.stagger_float_in.
"""
from __future__ import annotations
import os
import unittest
from importlib import import_module
from unittest import mock
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QWidget
import ui.widgets.stagger_float_in as float_module
from ui.widgets.stagger_float_in import attach_stagger_float_in, stagger_float_in
class _OwnEntrance(QWidget):
def __init__(self) -> None:
super().__init__()
self.played: list[int] = []
self.finished = 0
def play_float_in(self, delay_ms: int) -> None:
self.played.append(delay_ms)
def finish_float_in(self) -> None:
self.finished += 1
class PageFloatInContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def setUp(self) -> None:
patcher = mock.patch.object(float_module, "are_live_animations_enabled", return_value=True)
patcher.start()
self.addCleanup(patcher.stop)
def test_rise_effect_reuses_source_snapshot_between_frames(self) -> None:
# Для виджета Qt не кэширует sourcePixmap(): без своего снимка каждый
# кадр выплывания заново рисовал карточку со всеми детьми на CPU.
class _Counting(QWidget):
paints = 0
def paintEvent(self, event) -> None: # noqa: N802
type(self).paints += 1
host = QWidget()
host.resize(200, 120)
child = _Counting(host)
child.setGeometry(10, 10, 120, 60)
effect = float_module._RiseEffect(child)
child.setGraphicsEffect(effect)
effect.set_progress(0.5)
self.addCleanup(host.deleteLater)
clock = [100.0]
with mock.patch.object(float_module.time, "monotonic", side_effect=lambda: clock[0]):
for step in range(5):
effect.set_progress(0.5 + step * 0.05)
host.grab()
self.assertEqual(_Counting.paints, 1)
clock[0] += float_module._SOURCE_REFRESH_S
effect.set_progress(0.9)
host.grab()
self.assertEqual(_Counting.paints, 2)
def test_every_registered_page_gets_float_in(self) -> None:
from ui.page_registry import PAGE_CLASS_SPECS
from ui.pages.base_page import BasePage
for page_name, (module_name, class_name) in PAGE_CLASS_SPECS.items():
with self.subTest(page=class_name):
page_cls = getattr(import_module(module_name), class_name)
if class_name == "OrchestraSettingsPage":
# Контейнер вкладок: выплывает каждая вкладка — сама BasePage.
for tab_module, tab_class in (
("orchestra.ui.locked_page", "OrchestraLockedPage"),
("orchestra.ui.blocked_page", "OrchestraBlockedPage"),
("orchestra.ui.whitelist_page", "OrchestraWhitelistPage"),
("orchestra.ui.ratings_page", "OrchestraRatingsPage"),
):
tab_cls = getattr(import_module(tab_module), tab_class)
self.assertTrue(issubclass(tab_cls, BasePage), tab_class)
continue
self.assertTrue(issubclass(page_cls, BasePage), f"{page_name}: {class_name} без выплывания")
def test_base_page_attaches_float_in_to_content(self) -> None:
from ui.pages.base_page import BasePage
page = BasePage(title="Проверка")
self.addCleanup(page.deleteLater)
self.assertIsNotNone(stagger_float_in(page.content))
def test_own_entrance_is_played_in_the_common_queue_and_finished(self) -> None:
container = QWidget()
self.addCleanup(container.deleteLater)
layout = QVBoxLayout(container)
first = QWidget()
first.setMinimumHeight(20)
own = _OwnEntrance()
own.setMinimumHeight(40)
layout.addWidget(first)
layout.addWidget(own)
controller = attach_stagger_float_in(container)
container.resize(300, 200)
container.show()
controller.play()
self.assertEqual(own.played, [float_module.FLOAT_IN_STEP_MS])
self.assertIsNone(own.graphicsEffect())
controller.finish_all()
self.assertEqual(own.finished, 1)
def test_hosts_tiles_float_in_on_every_show(self) -> None:
from test_hosts_page_draft import HostsPageTests, _manual_snapshot
helper = HostsPageTests("test_page_scrolls_as_a_whole")
self.addCleanup(helper.doCleanups)
page = helper._page(_manual_snapshot())
grid = page.tiles
controller = stagger_float_in(page.content)
self.assertIsNotNone(controller)
for _show in range(2):
page.hide()
QApplication.processEvents()
page.show()
QApplication.processEvents()
controller.play()
self.assertIsNotNone(grid._entrance_start, "сетка hosts не выплывает при показе")
self.assertTrue(grid._entrance_order)
# В начале входа плитки ещё прозрачны, к концу — на месте.
first = next(iter(grid._entrance_order))
self.assertEqual(grid._entrance_progress(first, grid._entrance_start), 0.0)
late = grid._entrance_start + 10.0
self.assertEqual(grid._entrance_progress(first, late), 1.0)
self.assertTrue(grid._entrance_finished(late))
controller.finish_all()
self.assertIsNone(grid._entrance_start)
if __name__ == "__main__":
unittest.main()