Files
031-flash-card/tests/unit/test_build_executable.py
T
Tirsvad bc513810ec MIL-009: Executable with PyInstaller
Review record RC-048 (Go, autonomous mode, not independent).

Task: MIL-009#1
Task: MIL-009#2
Task: MIL-009#3
Task: MIL-009#4
Task: MIL-009#5
Task: MIL-009#6
Task: MIL-009#7
Task: MIL-009#8
2026-10-15 00:32:14 +08:00

95 lines
3.2 KiB
Python

"""Tests of the executable build tool.
@file test_build_executable.py
@brief Checks the PyInstaller arguments and the missing PyInstaller message.
"""
import importlib
import importlib.util
import os
from pathlib import Path
from types import ModuleType
import pytest
from flash_card.constants import EXECUTABLE_NAME, ICON_FILE_NAME
_TOOL_PATH = Path(__file__).resolve().parents[2] / "tools" / "build_executable.py"
ROOT = Path("project")
ASSETS = ROOT / "src" / "flash_card" / "presentation" / "assets"
def _load_tool() -> ModuleType:
spec = importlib.util.spec_from_file_location("build_executable", _TOOL_PATH)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _option(arguments: list[str], name: str) -> str:
return arguments[arguments.index(name) + 1]
def test_build_arguments_start_with_the_packaging_flags() -> None:
arguments = _load_tool().build_arguments(ROOT)
assert arguments[:3] == ["--onefile", "--windowed", "--noconfirm"]
def test_build_arguments_name_icon_and_folders() -> None:
arguments = _load_tool().build_arguments(ROOT)
assert _option(arguments, "--name") == EXECUTABLE_NAME == "flash-card"
assert _option(arguments, "--icon") == str(ASSETS / ICON_FILE_NAME)
assert _option(arguments, "--paths") == str(ROOT / "src")
assert _option(arguments, "--distpath") == str(ROOT / "dist")
assert _option(arguments, "--workpath") == str(ROOT / "build")
assert _option(arguments, "--specpath") == str(ROOT / "build")
def test_build_arguments_add_assets_and_word_list() -> None:
arguments = _load_tool().build_arguments(ROOT)
pairs = [
arguments[i + 1] for i, value in enumerate(arguments) if value == "--add-data"
]
assert pairs == [
f"{ASSETS}{os.pathsep}flash_card/presentation/assets",
f"{ROOT / 'data' / 'ms-da.csv'}{os.pathsep}data",
]
def test_build_arguments_end_with_the_entry_script() -> None:
arguments = _load_tool().build_arguments(ROOT, clean=True)
assert arguments[-1] == str(ROOT / "src" / "flash_card" / "__main__.py")
def test_build_arguments_include_speech_packages_by_default() -> None:
arguments = _load_tool().build_arguments(ROOT)
assert arguments.count("--collect-all") == 2
assert "gtts" in arguments
assert "playsound" in arguments
def test_build_arguments_leave_out_speech_packages_without_speech() -> None:
arguments = _load_tool().build_arguments(ROOT, speech=False)
assert "--collect-all" not in arguments
assert "--hidden-import" not in arguments
def test_build_arguments_clean_only_when_asked() -> None:
tool = _load_tool()
assert "--clean" not in tool.build_arguments(ROOT)
assert "--clean" in tool.build_arguments(ROOT, clean=True)
def test_main_returns_two_and_explains_when_pyinstaller_is_missing(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
def refuse(name: str, package: str | None = None) -> ModuleType:
raise ImportError(name)
tool = _load_tool()
monkeypatch.setattr(importlib, "import_module", refuse)
assert tool.main([]) == 2
assert 'pip install -e ".[build]"' in capsys.readouterr().err