zapretgui/tests/test_subscription_badge_shine.py
loop-uh 1afa63e1e9
All checks were successful
Zapret source guards / validate (push) Successful in 15s
Windows-сборка / Dev Dev 21.1.7.1 опубликован
Живые анимации рисуются в общем такте и не рисуются, когда экран никто не видит
Каждая постоянная анимация вела свой таймер: значок «Работает» в заголовке
(62 кадра в секунду), точка и сцена статуса на главной, блик Premium. Их кадры
шли вразнобой, и на каждый Qt отдельно перерисовывал окно и отдельно отдавал
его на экран, а без видеокарты кадр собирал ещё и dwm.exe. Теперь один общий
такт (ui/frame_clock.py): кадры всех анимаций приходят в одном обороте цикла
событий и сливаются в одну перерисовку окна. Стенд главной (значок + сцена
обхода): 87 перерисовок окна в секунду -> 30, 8,3% ядра -> около 4%.

- Значок «Работает» и точка статуса — 30 кадров в секунду, фаза по времени.
  Кадры значка совпадают со старыми до пикселя (сравнено 96 кадров: фазы,
  наведение, темы). Цвета значка считаются один раз на фазу и тему.
- Блик Premium оставлен на 60 кадрах, но тоже на общем такте.
- Такт на точном таймере: на Windows обычный таймер на 33 мс давал 20 кадров
  рывками (интервалы 42–63 мс), точный — ровные 30 (замер на win10, Qt 6.11).
- Сеанс заблокирован или отключён, дисплей выключен — такт стоит
  (WM_WTSSESSION_CHANGE, GUID_CONSOLE_DISPLAY_STATE); активация окна снимает
  паузу на случай потерянного уведомления.
- nativeEvent разбирает структуру сообщения Windows один раз и зовёт
  обработчики только для своих номеров, а не на каждое движение мыши.
2026-10-03 19:38:53 +03:00

76 lines
3 KiB
Python

from __future__ import annotations
import os
import unittest
from unittest import mock
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QWidget
import ui.subscription_title_badge as badge_module
from donater.premium_display import TIER_ACTIVE, TIER_FREE, PremiumDisplay
from ui.subscription_title_badge import SubscriptionTitleBadge
class SubscriptionBadgeShineTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def setUp(self) -> None:
patcher = mock.patch.object(badge_module, "are_live_animations_enabled", return_value=True)
patcher.start()
self.addCleanup(patcher.stop)
self.host = QWidget()
self.addCleanup(self.host.deleteLater)
self.badge = SubscriptionTitleBadge(self.host, language_provider=lambda: "ru")
QHBoxLayout(self.host).addWidget(self.badge)
self.host.show()
def test_premium_badge_schedules_shine_and_repeats(self) -> None:
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
QApplication.processEvents()
self.assertTrue(self.badge._shine_timer.isActive())
self.badge.play_shine()
self.assertTrue(self.badge.is_shining())
# Кадр на общем такте после конца вспышки завершает её.
with mock.patch.object(
self.badge._shine, "elapsed_ms", return_value=badge_module.PREMIUM_SHINE_DURATION_MS
):
self.badge._on_shine_frame()
self.assertFalse(self.badge.is_shining())
self.assertEqual(self.badge._shine_t, 0.0)
# После вспышки ждёт следующую только одиночный таймер.
self.assertTrue(self.badge._shine_timer.isActive())
self.assertEqual(self.badge._shine_timer.interval(), badge_module.PREMIUM_SHINE_INTERVAL_MS)
def test_free_badge_never_shines(self) -> None:
self.badge.set_display(PremiumDisplay(tier=TIER_FREE))
QApplication.processEvents()
self.assertFalse(self.badge._shine_timer.isActive())
self.badge.play_shine()
self.assertFalse(self.badge.is_shining())
def test_hidden_badge_stops_shining(self) -> None:
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
QApplication.processEvents()
self.badge.play_shine()
self.host.hide()
self.assertFalse(self.badge.is_shining())
self.assertFalse(self.badge._shine_timer.isActive())
def test_no_shine_when_live_animations_are_off(self) -> None:
with mock.patch.object(badge_module, "are_live_animations_enabled", return_value=False):
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
QApplication.processEvents()
self.badge.play_shine()
self.assertFalse(self.badge.is_shining())
self.assertFalse(self.badge._shine_timer.isActive())
if __name__ == "__main__":
unittest.main()