zapretgui/tests/test_zapret2_control_lazy_startup.py
loop-uh dea5b115cf Speed up startup: ExeToPython metric, lazy imports, qtawesome warmup
Метрика и инструментация:
- StartupExeToPython: разрыв от создания процесса (GetProcessTimes) до
  входа в Python — раньше bootloader/DLL/антивирус были невидимы
- Пофасадные метрики StartupFeatureFacadeImport.*, разбивка attach-цепочки
  и _build_ui первой страницы

Ленивые импорты (тёплый Interactive 1.85s -> ~1.0s, окно 1.5s -> ~0.7s):
- orchestra/ignored_targets: telegram_proxy больше не тянется на старте
  (весь пакет с asyncio/mtproxy стоил 240ms тёплым, 555ms холодным)
- ui/page_deps/presets: preset_subpage_base/qtawesome не импортируются
  до первого показа окна (~200ms)
- qtawesome греется в фоне после application_bootstrap (не в prelaunch —
  там он отбирает GIL у импортов главного потока); мёртвый jinja2 убран

Контракт: отложенная сборка settings-секций снова пробовалась и снова
откачена (таймеры + интерфейс появлялся дольше) — запрет зафиксирован
в тесте с объяснением.
2026-07-06 12:32:29 +03:00

164 lines
9.1 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.

from __future__ import annotations
import inspect
import unittest
class ControlPageImmediateStartupTests(unittest.TestCase):
def test_control_pages_build_settings_sections_immediately(self) -> None:
"""Секции настроек строятся синхронно в _build_ui — намеренно.
Отложенную сборку (таймерами) уже пробовали дважды и откатывали:
таймеры создавались криво, интерфейс появлялся дольше. Не возвращать
без явного решения владельца.
"""
import presets.ui.control.zapret1.page as zapret1_page
import presets.ui.control.zapret2.page as zapret2_page
for page_cls in (zapret1_page.Zapret1ModeControlPage, zapret2_page.Zapret2ModeControlPage):
with self.subTest(page_cls=page_cls.__name__):
page_source = inspect.getsource(page_cls)
build_ui_source = inspect.getsource(page_cls._build_ui)
self.assertIn("_build_settings_sections", build_ui_source)
self.assertIn("_attach_program_settings_runtime", build_ui_source)
self.assertIn("_schedule_additional_settings_reload(force=True)", build_ui_source)
self.assertNotIn("_build_deferred_sections", page_source)
self.assertNotIn("_run_deferred_show_work", page_source)
self.assertNotIn("_startup_can_run_deferred_sections", page_source)
self.assertNotIn("STARTUP_DEFERRED_SECTIONS", page_source)
self.assertNotIn("_build_settings_sections_deferred", page_source)
def test_additional_settings_workers_are_imported_only_when_requested(self) -> None:
import presets.ui.control.zapret2.page as zapret2_page
module_source = inspect.getsource(zapret2_page)
import_block = "\n".join(module_source.splitlines()[:90])
reload_source = inspect.getsource(zapret2_page.Zapret2ModeControlPage._schedule_additional_settings_reload)
save_source = inspect.getsource(zapret2_page.Zapret2ModeControlPage._start_additional_settings_save_worker)
self.assertNotIn("create_additional_settings_worker as create_control_additional_settings_worker", import_block)
self.assertIn("create_additional_settings_worker as create_control_additional_settings_worker", reload_source)
self.assertIn("_create_additional_settings_save_worker", save_source)
def test_zapret2_page_runtime_is_not_imported_on_page_module_import(self) -> None:
import presets.ui.control.zapret2.page as zapret2_page
import presets.ui.control.zapret2.runtime_helpers as runtime_helpers
page_source = inspect.getsource(zapret2_page)
page_import_block = "\n".join(page_source.splitlines()[:90])
helper_source = inspect.getsource(runtime_helpers)
helper_import_block = "\n".join(helper_source.splitlines()[:30])
after_ui_source = inspect.getsource(zapret2_page.Zapret2ModeControlPage._after_ui_built)
self.assertNotIn("import presets.ui.control.zapret2.page_runtime", page_import_block)
self.assertNotIn("import presets.ui.control.zapret2.page_runtime", helper_import_block)
self.assertIn("def _zapret2_page_runtime", page_source)
self.assertNotIn("_update_stop_winws_button_text()", after_ui_source)
def test_zapret2_first_page_keeps_theme_refresh_imports_lazy(self) -> None:
import presets.ui.control.shared_builders as shared_builders
import presets.ui.control.top_summary_widget as top_summary_widget
import presets.ui.control.zapret2.page as zapret2_page
import ui.pages.base_page as base_page
shared_source = inspect.getsource(shared_builders)
shared_import_block = "\n".join(shared_source.splitlines()[:30])
top_summary_source = inspect.getsource(top_summary_widget)
top_summary_import_block = "\n".join(top_summary_source.splitlines()[:30])
zapret2_page_source = inspect.getsource(zapret2_page)
zapret2_import_block = "\n".join(zapret2_page_source.splitlines()[:45])
item_init_source = inspect.getsource(top_summary_widget.ControlTopSummaryItem.__init__)
activate_source = inspect.getsource(top_summary_widget.ControlTopSummaryItem._activate_theme_refresh)
base_page_source = inspect.getsource(base_page)
base_page_import_block = "\n".join(base_page_source.splitlines()[:35])
zapret2_source = inspect.getsource(zapret2_page.Zapret2ModeControlPage)
self.assertNotIn("from ui.theme import", top_summary_import_block)
self.assertNotIn("from ui.theme_refresh import", top_summary_import_block)
self.assertIn("_schedule_icon_refresh", item_init_source)
self.assertIn("from ui.theme_refresh import ThemeRefreshBinding", activate_source)
self.assertNotIn("from ui.theme_refresh import", base_page_import_block)
self.assertIn("_create_page_theme_refresh_if_needed", base_page_source)
self.assertNotIn("def _apply_page_theme", zapret2_source)
self.assertNotIn("from ui.fluent_widgets import", shared_import_block)
self.assertIn("from ui.pulsing_dot import PulsingDot", shared_import_block)
def test_zapret2_top_summary_icons_are_delayed_past_first_paint(self) -> None:
import inspect
from presets.ui.control.zapret2.page import Zapret2ModeControlPage
build_ui_source = inspect.getsource(Zapret2ModeControlPage._build_ui)
self.assertIn("ControlTopSummaryWidget", build_ui_source)
self.assertIn("initial_icon_delay_ms=250", build_ui_source)
def test_zapret2_startup_does_not_build_hidden_profile_mode_card(self) -> None:
import inspect
import presets.ui.control.zapret2.sections_build as sections_build
source = inspect.getsource(sections_build.build_winws2_pages_settings_sections)
widget_fields = getattr(sections_build.Zapret2SettingsBuildWidgets, "__dataclass_fields__", {})
self.assertNotIn("profile_ui_mode_card = build_push_setting_card_common", source)
self.assertNotIn("on_open_profile_ui_mode_dialog", source)
self.assertNotIn("get_cached_qta_pixmap", source)
self.assertNotIn("profile_ui_mode_btn", widget_fields)
def test_zapret2_profile_ui_mode_legacy_option_is_removed(self) -> None:
import app.ui_texts as ui_texts
import presets.ui.control.zapret2.page as zapret2_page
import presets.ui.control.zapret2.page_runtime as page_runtime
import presets.ui.control.zapret2.runtime_helpers as runtime_helpers
import settings.normalize as settings_normalize
import settings.schema as settings_schema
import settings.store as settings_store
self.assertNotIn("profile_ui_mode", settings_schema.default_program())
self.assertNotIn("profile_ui_mode", settings_normalize.normalize_program({"profile_ui_mode": "advanced"}))
self.assertFalse(hasattr(settings_schema, "VALID_PROFILE_UI_MODES"))
self.assertFalse(hasattr(settings_store, "get_profile_ui_mode"))
self.assertFalse(hasattr(settings_store, "set_profile_ui_mode"))
self.assertFalse(hasattr(runtime_helpers, "sync_profile_ui_mode_label"))
self.assertFalse(hasattr(page_runtime, "build_profile_ui_mode_label_plan"))
self.assertFalse(hasattr(zapret2_page.Zapret2ModeControlPage, "_open_profile_ui_mode_dialog"))
for key in (
"page.winws2_control.button.change_mode",
"page.winws2_control.profile_ui_mode.caption",
"page.winws2_control.mode.basic",
"page.winws2_control.mode.advanced",
"page.winws2_control.mode.dialog.title",
"page.winws2_control.mode.dialog.advanced_description",
):
self.assertNotIn(key, ui_texts.TEXTS)
def test_zapret2_program_settings_auto_height_is_not_applied_twice(self) -> None:
import inspect
import presets.ui.control.zapret2.sections_build as sections_build
from presets.ui.control.zapret2.page import Zapret2ModeControlPage
page_source = inspect.getsource(Zapret2ModeControlPage._build_settings_sections)
builder_source = inspect.getsource(sections_build.build_winws2_pages_settings_sections)
self.assertIn("enable_setting_card_group_auto_height(program_settings_card)", builder_source)
self.assertNotIn("enable_setting_card_group_auto_height(self.program_settings_card)", page_source)
def test_control_settings_sections_defer_themed_action_icons(self) -> None:
import inspect
import presets.ui.control.zapret1.sections_build as zapret1_sections
import presets.ui.control.zapret2.sections_build as zapret2_sections
for module in (zapret1_sections, zapret2_sections):
with self.subTest(module=module.__name__):
source = inspect.getsource(module)
self.assertIn("build_deferred_themed_push_setting_card_common", source)
self.assertNotIn("get_themed_qta_icon", source)
if __name__ == "__main__":
unittest.main()