76 lines
3.3 KiB
Python
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 |