Files
opcda-diagnozer/tests/test_build_exe.py
T
2026-10-07 17:13:51 +02:00

76 lines
3.3 KiB
Python

"""Tests for the frozen-exe build script + frozen-aware config default.
The PyInstaller run itself is Windows-only and slow; these tests cover the
pure logic the build script and the CLI rely on. The build script is
PowerShell (``scripts/build_exe.ps1``); the tests assert on its content and
on the CLI behaviors the produced exe depends on.
"""
import sys
from pathlib import Path
from unittest import mock
import pytest
from opcda_diagnozer.main import DEFAULT_CONFIG, _default_config_path
BUILD_SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "build_exe.ps1"
def test_default_config_path_in_source_mode() -> None:
"""Not frozen: the default stays the plain relative name (CWD lookup)."""
with mock.patch.object(sys, "frozen", False, create=True):
assert _default_config_path() == DEFAULT_CONFIG
def test_default_config_path_when_frozen_resolves_next_to_exe(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Frozen: config.yaml lives next to the exe — never the service CWD (System32)."""
exe_dir = tmp_path / "Program Files" / "OPCDA-Diagnozer"
exe_dir.mkdir(parents=True)
exe = exe_dir / "opcda-diagnozer.exe"
exe.touch()
monkeypatch.setattr(sys, "frozen", True, raising=False)
monkeypatch.setattr(sys, "executable", str(exe))
assert _default_config_path() == str(exe_dir / DEFAULT_CONFIG)
@pytest.mark.skipif(sys.platform != "win32", reason="the exe is Windows-only")
class TestBuildScript:
"""Static assertions on scripts/build_exe.ps1 (no PyInstaller invocation)."""
def test_hidden_imports_cover_lazy_modules(self) -> None:
"""Every lazy import in the package must be declared to PyInstaller."""
content = BUILD_SCRIPT.read_text(encoding="utf-8")
# the deferred COM imports (opc/automation.py, opc/discovery.py)
assert "'pythoncom'" in content
assert "'win32com.client'" in content
assert "'opcda_diagnozer.opc.automation'" in content
# the lazily-imported browse module (main.py verbs)
assert "'opcda_diagnozer.opc.browse'" in content
# the Phase 3 service host stays in the bundle from day one
assert "'opcda_diagnozer.service'" in content
def test_version_info_template_uses_pyi_keywords(self) -> None:
"""The embedded version-info must use PyInstaller's exact keyword names.
FixedFileInfo.__init__ takes filevers/prodvers/mask/flags/OS/fileType/
subtype/date — anything else (fileVers, fileDate, subType) raises a
TypeError inside PyInstaller's eval at build time.
"""
content = BUILD_SCRIPT.read_text(encoding="utf-8")
assert "filevers=(" in content
assert "fileType=0x1" in content
assert "subtype=0x0" in content
assert "date=(0, 0)" in content
# the rejected spellings must NOT appear
assert "fileVers=" not in content
assert "fileDate=" not in content
assert "subType=" not in content
def test_bitness_guards_present(self) -> None:
"""The script must refuse a 64-bit interpreter and verify the PE."""
content = BUILD_SCRIPT.read_text(encoding="utf-8")
assert "struct.calcsize('P') * 8" in content # interpreter bitness check
assert "0x14C" in content # PE machine = IMAGE_FILE_MACHINE_I386 check