synodic-client 0.0.1.dev57__tar.gz → 0.0.1.dev61__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/PKG-INFO +1 -1
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/pyproject.toml +1 -1
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/init.py +10 -6
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/screen.py +4 -9
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/settings.py +17 -6
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/spinner.py +1 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/logging.py +7 -6
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_gather_packages.py +4 -10
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_logging.py +4 -5
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_settings.py +25 -4
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_init.py +24 -2
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/LICENSE.md +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/README.md +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/__main__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/bootstrap.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/data.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/icon.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/instance.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/qt.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/schema.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/action_card.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/card.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/install.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/install_workers.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/log_panel.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/plugin_row.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/projects.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/schema.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/sidebar.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/tool_update_controller.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/tray.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/update_banner.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/theme.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/update_controller.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/uri.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/workers.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/cli.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/client.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/config.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/protocol.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/py.typed +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/resolution.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/schema.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/startup.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/updater.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/conftest.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/conftest.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_action_card.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_install_preview.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_log_panel.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_preview_model.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_sidebar.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_tray_window_show.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_banner.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_controller.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_feedback.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_cli.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_client_updater.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_client_version.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_config.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_examples.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_install.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_resolution.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_updater.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_uri.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/test_workers.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/__init__.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/conftest.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/test_protocol.py +0 -0
- {synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/test_startup.py +0 -0
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/init.py
RENAMED
|
@@ -53,12 +53,16 @@ def run_startup_preamble(exe_path: str | None = None) -> None:
|
|
|
53
53
|
# Seed user config from the build config (one-time propagation).
|
|
54
54
|
seed_user_config_from_build()
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
frozen = getattr(sys, 'frozen', False)
|
|
57
|
+
|
|
58
|
+
if frozen:
|
|
59
|
+
register_protocol(exe_path)
|
|
57
60
|
|
|
58
61
|
config = resolve_config()
|
|
59
|
-
if
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if frozen:
|
|
63
|
+
if config.auto_start:
|
|
64
|
+
register_startup(exe_path)
|
|
65
|
+
else:
|
|
66
|
+
remove_startup()
|
|
63
67
|
|
|
64
|
-
logger.info('Startup preamble complete (auto_start=%s)', config.auto_start)
|
|
68
|
+
logger.info('Startup preamble complete (auto_start=%s, frozen=%s)', config.auto_start, frozen)
|
|
@@ -315,11 +315,10 @@ class ToolsView(QWidget):
|
|
|
315
315
|
pkg_tasks: dict[str, asyncio.Task] = {}
|
|
316
316
|
for plugin in updatable_plugins:
|
|
317
317
|
if plugin.name in runtime_probed:
|
|
318
|
-
#
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
)
|
|
318
|
+
# Runtime-probed plugins show only per-runtime
|
|
319
|
+
# packages; venv/project packages belong in
|
|
320
|
+
# ProjectsView and are intentionally skipped here.
|
|
321
|
+
continue
|
|
323
322
|
else:
|
|
324
323
|
pkg_tasks[plugin.name] = tg.create_task(
|
|
325
324
|
self._gather_packages(
|
|
@@ -390,10 +389,6 @@ class ToolsView(QWidget):
|
|
|
390
389
|
for plugin in kind_buckets[kind]:
|
|
391
390
|
if plugin.name in data.runtime_packages:
|
|
392
391
|
self._build_runtime_sections(plugin, data, auto_update_map)
|
|
393
|
-
# Also emit venv packages (if any) as a separate
|
|
394
|
-
# provider header without a runtime tag.
|
|
395
|
-
if data.packages_map.get(plugin.name):
|
|
396
|
-
self._build_plugin_section(plugin, data, auto_update_map)
|
|
397
392
|
else:
|
|
398
393
|
self._build_plugin_section(plugin, data, auto_update_map)
|
|
399
394
|
|
|
@@ -120,6 +120,7 @@ class SettingsWindow(QMainWindow):
|
|
|
120
120
|
|
|
121
121
|
scroll.setWidget(container)
|
|
122
122
|
self.setCentralWidget(scroll)
|
|
123
|
+
self._scroll_content = container
|
|
123
124
|
|
|
124
125
|
def _build_updates_section(self) -> CardFrame:
|
|
125
126
|
"""Construct the *Updates* settings card."""
|
|
@@ -221,6 +222,9 @@ class SettingsWindow(QMainWindow):
|
|
|
221
222
|
card = CardFrame('Startup')
|
|
222
223
|
self._auto_start_check = QCheckBox('Start with Windows')
|
|
223
224
|
self._auto_start_check.toggled.connect(self._on_auto_start_changed)
|
|
225
|
+
if not getattr(sys, 'frozen', False):
|
|
226
|
+
self._auto_start_check.setEnabled(False)
|
|
227
|
+
self._auto_start_check.setToolTip('Auto-start is only available for installed builds')
|
|
224
228
|
card.content_layout.addWidget(self._auto_start_check)
|
|
225
229
|
return card
|
|
226
230
|
|
|
@@ -314,8 +318,14 @@ class SettingsWindow(QMainWindow):
|
|
|
314
318
|
def show(self) -> None:
|
|
315
319
|
"""Sync controls from config, size to content, then show the window."""
|
|
316
320
|
self.sync_from_config()
|
|
317
|
-
#
|
|
318
|
-
|
|
321
|
+
# QScrollArea doesn't propagate its content's sizeHint, so
|
|
322
|
+
# adjustSize() only reaches the minimum. Compute the ideal
|
|
323
|
+
# height from the content widget directly.
|
|
324
|
+
content_hint = self._scroll_content.sizeHint()
|
|
325
|
+
margins = self._scroll_content.layout().contentsMargins()
|
|
326
|
+
ideal_w = max(content_hint.width() + margins.left() + margins.right(), self.minimumWidth())
|
|
327
|
+
ideal_h = max(content_hint.height() + margins.top() + margins.bottom(), self.minimumHeight())
|
|
328
|
+
self.resize(ideal_w, ideal_h)
|
|
319
329
|
super().show()
|
|
320
330
|
self.raise_()
|
|
321
331
|
self.activateWindow()
|
|
@@ -388,10 +398,11 @@ class SettingsWindow(QMainWindow):
|
|
|
388
398
|
|
|
389
399
|
def _on_auto_start_changed(self, checked: bool) -> None:
|
|
390
400
|
self._config = update_user_config(auto_start=checked)
|
|
391
|
-
if
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
401
|
+
if getattr(sys, 'frozen', False):
|
|
402
|
+
if checked:
|
|
403
|
+
register_startup(sys.executable)
|
|
404
|
+
else:
|
|
405
|
+
remove_startup()
|
|
395
406
|
self.settings_changed.emit(self._config)
|
|
396
407
|
|
|
397
408
|
def _on_debug_logging_changed(self, checked: bool) -> None:
|
|
@@ -128,6 +128,7 @@ class SpinnerWidget(QWidget):
|
|
|
128
128
|
# Auto-overlay: track parent geometry via event filter
|
|
129
129
|
if parent is not None:
|
|
130
130
|
self.setAutoFillBackground(True)
|
|
131
|
+
self.setStyleSheet('background: palette(window);')
|
|
131
132
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
|
132
133
|
parent.installEventFilter(self)
|
|
133
134
|
self.setGeometry(parent.rect())
|
|
@@ -6,15 +6,14 @@ log-level switching via :func:`set_debug_level`.
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import sys
|
|
9
|
-
import tempfile
|
|
10
9
|
from logging.handlers import RotatingFileHandler
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
|
|
13
|
-
from synodic_client.config import is_dev_mode
|
|
12
|
+
from synodic_client.config import config_dir, is_dev_mode
|
|
14
13
|
|
|
15
14
|
_LOG_FILENAME = 'synodic.log'
|
|
16
15
|
_LOG_FILENAME_DEV = 'synodic-dev.log'
|
|
17
|
-
_MAX_BYTES =
|
|
16
|
+
_MAX_BYTES = 1_048_576 # 1 MB
|
|
18
17
|
_BACKUP_COUNT = 3
|
|
19
18
|
_FORMAT = '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
|
20
19
|
|
|
@@ -24,13 +23,13 @@ _debug_active: bool = False
|
|
|
24
23
|
def log_path() -> Path:
|
|
25
24
|
"""Return the path to the application log file.
|
|
26
25
|
|
|
27
|
-
The file lives
|
|
28
|
-
|
|
26
|
+
The file lives under ``config_dir() / 'logs'`` so that agents and
|
|
27
|
+
developers can find it at a deterministic, well-known location.
|
|
29
28
|
|
|
30
29
|
Returns:
|
|
31
30
|
Path to the log file.
|
|
32
31
|
"""
|
|
33
|
-
return
|
|
32
|
+
return config_dir() / 'logs' / (_LOG_FILENAME_DEV if is_dev_mode() else _LOG_FILENAME)
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
class EagerRotatingFileHandler(RotatingFileHandler):
|
|
@@ -68,6 +67,8 @@ def configure_logging(*, debug: bool = False) -> None:
|
|
|
68
67
|
|
|
69
68
|
logging.basicConfig(level=logging.INFO)
|
|
70
69
|
|
|
70
|
+
log_path().parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
|
|
71
72
|
handler = EagerRotatingFileHandler(
|
|
72
73
|
str(log_path()),
|
|
73
74
|
maxBytes=_MAX_BYTES,
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_gather_packages.py
RENAMED
|
@@ -922,7 +922,6 @@ class TestRuntimePluginSupport:
|
|
|
922
922
|
|
|
923
923
|
# Expected widget counts (avoids PLR2004)
|
|
924
924
|
_EXPECTED_RUNTIME_PROVIDERS = 2
|
|
925
|
-
_EXPECTED_RUNTIME_PROVIDERS_WITH_VENV = 3
|
|
926
925
|
_EXPECTED_DEFAULT_RT_PACKAGES = 2
|
|
927
926
|
_EXPECTED_NON_DEFAULT_RT_PACKAGES = 1
|
|
928
927
|
|
|
@@ -1050,8 +1049,8 @@ class TestPerRuntimeDisplay:
|
|
|
1050
1049
|
assert len(non_default_rows) == _EXPECTED_NON_DEFAULT_RT_PACKAGES
|
|
1051
1050
|
assert non_default_rows[0]._package_name == 'django'
|
|
1052
1051
|
|
|
1053
|
-
def
|
|
1054
|
-
"""Venv packages appear in
|
|
1052
|
+
def test_venv_packages_excluded_for_runtime_probed_plugin(self) -> None:
|
|
1053
|
+
"""Venv packages must not appear in ToolsView for runtime-probed plugins."""
|
|
1055
1054
|
view = ToolsView(_make_porringer(), _make_config())
|
|
1056
1055
|
default_exe = Path('C:/Python314/python.exe')
|
|
1057
1056
|
plugin = self._pip_plugin()
|
|
@@ -1078,13 +1077,8 @@ class TestPerRuntimeDisplay:
|
|
|
1078
1077
|
view._build_widget_tree(data)
|
|
1079
1078
|
|
|
1080
1079
|
providers = [w for w in view._section_widgets if isinstance(w, PluginProviderHeader)]
|
|
1081
|
-
# 2 runtime providers
|
|
1082
|
-
assert len(providers) ==
|
|
1083
|
-
|
|
1084
|
-
# The last provider should NOT have a runtime tag
|
|
1085
|
-
last_provider = providers[-1]
|
|
1086
|
-
runtime_tags = [w for w in last_provider.findChildren(QLabel) if 'Python' in w.text()]
|
|
1087
|
-
assert len(runtime_tags) == 0, 'Venv provider should not have a runtime tag'
|
|
1080
|
+
# Only the 2 runtime providers — no extra venv provider
|
|
1081
|
+
assert len(providers) == _EXPECTED_RUNTIME_PROVIDERS
|
|
1088
1082
|
|
|
1089
1083
|
def test_runtime_tag_uses_default_style(self) -> None:
|
|
1090
1084
|
"""The default runtime tag uses the green highlight style."""
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import sys
|
|
5
|
-
import tempfile
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from unittest.mock import patch
|
|
8
7
|
|
|
9
8
|
from synodic_client.application.screen.settings import SettingsWindow
|
|
10
|
-
from synodic_client.config import set_dev_mode
|
|
9
|
+
from synodic_client.config import config_dir, set_dev_mode
|
|
11
10
|
from synodic_client.logging import (
|
|
12
11
|
EagerRotatingFileHandler,
|
|
13
12
|
configure_logging,
|
|
@@ -19,10 +18,10 @@ class TestLogPath:
|
|
|
19
18
|
"""Tests for log_path()."""
|
|
20
19
|
|
|
21
20
|
@staticmethod
|
|
22
|
-
def
|
|
23
|
-
"""log_path() should resolve inside
|
|
21
|
+
def test_returns_path_in_config_logs_dir() -> None:
|
|
22
|
+
"""log_path() should resolve inside config_dir() / 'logs'."""
|
|
24
23
|
path = log_path()
|
|
25
|
-
assert path.parent ==
|
|
24
|
+
assert path.parent == config_dir() / 'logs'
|
|
26
25
|
|
|
27
26
|
@staticmethod
|
|
28
27
|
def test_filename() -> None:
|
|
@@ -272,8 +272,8 @@ class TestSettingsCallbacks:
|
|
|
272
272
|
signal_spy.assert_called_once_with(new_config)
|
|
273
273
|
|
|
274
274
|
@staticmethod
|
|
275
|
-
def
|
|
276
|
-
"""Enabling auto-start calls register_startup."""
|
|
275
|
+
def test_auto_start_registers_startup_when_frozen() -> None:
|
|
276
|
+
"""Enabling auto-start calls register_startup in frozen builds."""
|
|
277
277
|
config = _make_config()
|
|
278
278
|
window = _make_window(config)
|
|
279
279
|
|
|
@@ -282,14 +282,15 @@ class TestSettingsCallbacks:
|
|
|
282
282
|
patch('synodic_client.application.screen.settings.update_user_config', return_value=new_config),
|
|
283
283
|
patch('synodic_client.application.screen.settings.register_startup') as mock_register,
|
|
284
284
|
patch('synodic_client.application.screen.settings.is_startup_registered', return_value=False),
|
|
285
|
+
patch('synodic_client.application.screen.settings.getattr', return_value=True),
|
|
285
286
|
):
|
|
286
287
|
window._auto_start_check.setChecked(True)
|
|
287
288
|
|
|
288
289
|
mock_register.assert_called_once()
|
|
289
290
|
|
|
290
291
|
@staticmethod
|
|
291
|
-
def
|
|
292
|
-
"""Disabling auto-start calls remove_startup."""
|
|
292
|
+
def test_auto_start_removes_startup_when_frozen() -> None:
|
|
293
|
+
"""Disabling auto-start calls remove_startup in frozen builds."""
|
|
293
294
|
config = _make_config(auto_start=True)
|
|
294
295
|
window = _make_window(config)
|
|
295
296
|
# Manually set initial state without triggering signals
|
|
@@ -301,11 +302,31 @@ class TestSettingsCallbacks:
|
|
|
301
302
|
with (
|
|
302
303
|
patch('synodic_client.application.screen.settings.update_user_config', return_value=new_config),
|
|
303
304
|
patch('synodic_client.application.screen.settings.remove_startup') as mock_remove,
|
|
305
|
+
patch('synodic_client.application.screen.settings.getattr', return_value=True),
|
|
304
306
|
):
|
|
305
307
|
window._auto_start_check.setChecked(False)
|
|
306
308
|
|
|
307
309
|
mock_remove.assert_called_once()
|
|
308
310
|
|
|
311
|
+
@staticmethod
|
|
312
|
+
def test_auto_start_skips_registry_when_not_frozen() -> None:
|
|
313
|
+
"""Auto-start toggle persists config but skips registry in non-frozen builds."""
|
|
314
|
+
config = _make_config()
|
|
315
|
+
window = _make_window(config)
|
|
316
|
+
|
|
317
|
+
new_config = _make_config(auto_start=True)
|
|
318
|
+
with (
|
|
319
|
+
patch('synodic_client.application.screen.settings.update_user_config', return_value=new_config),
|
|
320
|
+
patch('synodic_client.application.screen.settings.register_startup') as mock_register,
|
|
321
|
+
patch('synodic_client.application.screen.settings.remove_startup') as mock_remove,
|
|
322
|
+
patch('synodic_client.application.screen.settings.is_startup_registered', return_value=False),
|
|
323
|
+
patch('synodic_client.application.screen.settings.getattr', return_value=False),
|
|
324
|
+
):
|
|
325
|
+
window._auto_start_check.setChecked(True)
|
|
326
|
+
|
|
327
|
+
mock_register.assert_not_called()
|
|
328
|
+
mock_remove.assert_not_called()
|
|
329
|
+
|
|
309
330
|
|
|
310
331
|
# ---------------------------------------------------------------------------
|
|
311
332
|
# sync_from_config does not emit signals
|
|
@@ -28,6 +28,7 @@ class TestRunStartupPreamble:
|
|
|
28
28
|
patch(f'{_MODULE}.resolve_config') as mock_resolve,
|
|
29
29
|
patch(f'{_MODULE}.register_startup'),
|
|
30
30
|
patch(f'{_MODULE}.remove_startup'),
|
|
31
|
+
patch(f'{_MODULE}.getattr', return_value=True),
|
|
31
32
|
):
|
|
32
33
|
mock_resolve.return_value = MagicMock(auto_start=True)
|
|
33
34
|
run_startup_preamble(r'C:\app\synodic.exe')
|
|
@@ -38,13 +39,14 @@ class TestRunStartupPreamble:
|
|
|
38
39
|
|
|
39
40
|
@staticmethod
|
|
40
41
|
def test_registers_startup_when_auto_start_true() -> None:
|
|
41
|
-
"""register_startup is called when auto_start is True."""
|
|
42
|
+
"""register_startup is called when auto_start is True and frozen."""
|
|
42
43
|
with (
|
|
43
44
|
patch(f'{_MODULE}.seed_user_config_from_build'),
|
|
44
45
|
patch(f'{_MODULE}.register_protocol'),
|
|
45
46
|
patch(f'{_MODULE}.resolve_config') as mock_resolve,
|
|
46
47
|
patch(f'{_MODULE}.register_startup') as mock_register,
|
|
47
48
|
patch(f'{_MODULE}.remove_startup') as mock_remove,
|
|
49
|
+
patch(f'{_MODULE}.getattr', return_value=True),
|
|
48
50
|
):
|
|
49
51
|
mock_resolve.return_value = MagicMock(auto_start=True)
|
|
50
52
|
run_startup_preamble(r'C:\app\synodic.exe')
|
|
@@ -54,13 +56,14 @@ class TestRunStartupPreamble:
|
|
|
54
56
|
|
|
55
57
|
@staticmethod
|
|
56
58
|
def test_removes_startup_when_auto_start_false() -> None:
|
|
57
|
-
"""remove_startup is called when auto_start is False."""
|
|
59
|
+
"""remove_startup is called when auto_start is False and frozen."""
|
|
58
60
|
with (
|
|
59
61
|
patch(f'{_MODULE}.seed_user_config_from_build'),
|
|
60
62
|
patch(f'{_MODULE}.register_protocol'),
|
|
61
63
|
patch(f'{_MODULE}.resolve_config') as mock_resolve,
|
|
62
64
|
patch(f'{_MODULE}.register_startup') as mock_register,
|
|
63
65
|
patch(f'{_MODULE}.remove_startup') as mock_remove,
|
|
66
|
+
patch(f'{_MODULE}.getattr', return_value=True),
|
|
64
67
|
):
|
|
65
68
|
mock_resolve.return_value = MagicMock(auto_start=False)
|
|
66
69
|
run_startup_preamble(r'C:\app\synodic.exe')
|
|
@@ -78,6 +81,7 @@ class TestRunStartupPreamble:
|
|
|
78
81
|
patch(f'{_MODULE}.register_startup') as mock_register,
|
|
79
82
|
patch(f'{_MODULE}.remove_startup'),
|
|
80
83
|
patch(f'{_MODULE}.sys') as mock_sys,
|
|
84
|
+
patch(f'{_MODULE}.getattr', return_value=True),
|
|
81
85
|
):
|
|
82
86
|
mock_sys.executable = r'C:\Python\python.exe'
|
|
83
87
|
mock_resolve.return_value = MagicMock(auto_start=True)
|
|
@@ -86,6 +90,24 @@ class TestRunStartupPreamble:
|
|
|
86
90
|
mock_proto.assert_called_once_with(r'C:\Python\python.exe')
|
|
87
91
|
mock_register.assert_called_once_with(r'C:\Python\python.exe')
|
|
88
92
|
|
|
93
|
+
@staticmethod
|
|
94
|
+
def test_skips_registry_when_not_frozen() -> None:
|
|
95
|
+
"""Protocol and startup registration are skipped in non-frozen builds."""
|
|
96
|
+
with (
|
|
97
|
+
patch(f'{_MODULE}.seed_user_config_from_build'),
|
|
98
|
+
patch(f'{_MODULE}.register_protocol') as mock_proto,
|
|
99
|
+
patch(f'{_MODULE}.resolve_config') as mock_resolve,
|
|
100
|
+
patch(f'{_MODULE}.register_startup') as mock_register,
|
|
101
|
+
patch(f'{_MODULE}.remove_startup') as mock_remove,
|
|
102
|
+
patch(f'{_MODULE}.getattr', return_value=False),
|
|
103
|
+
):
|
|
104
|
+
mock_resolve.return_value = MagicMock(auto_start=True)
|
|
105
|
+
run_startup_preamble(r'C:\Python\python.exe')
|
|
106
|
+
|
|
107
|
+
mock_proto.assert_not_called()
|
|
108
|
+
mock_register.assert_not_called()
|
|
109
|
+
mock_remove.assert_not_called()
|
|
110
|
+
|
|
89
111
|
@staticmethod
|
|
90
112
|
def test_idempotent_on_second_call() -> None:
|
|
91
113
|
"""A second call is a no-op; the preamble runs only once."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/__init__.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/bootstrap.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/data.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/icon.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/instance.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/card.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/screen/tray.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/theme.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/synodic_client/application/workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_install_preview.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_preview_model.py
RENAMED
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_tray_window_show.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_banner.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_controller.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/qt/test_update_feedback.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/test_protocol.py
RENAMED
|
File without changes
|
{synodic_client-0.0.1.dev57 → synodic_client-0.0.1.dev61}/tests/unit/windows/test_startup.py
RENAMED
|
File without changes
|