mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 19:43:11 +02:00
Fix ruff violations in ~15 source files and ~60+ test files to minimize per-file-ignores in pyproject.toml. Remaining ignores are justified with comments explaining why each suppression is necessary. Source fixes: FBT003 (keyword args), S310 (URL validation), SLF001 (private access), T201 (print→logging), C901 (complexity), E501 (line length), E402 (import order). Test fixes: SIM117 (combined with), FBT (boolean args), PERF203 (try in loop), S310/S607 (URLs/executables), E402/E501 (imports/lines), S108 (tmp paths), PLR0913 (too many args), ARG (unused args), ANN (type annotations), RUF059 (unused unpacked vars), PT019 (fixture naming). Remaining per-file-ignores (with justifications): - Tests: ARG, D, PLC0415, PLR2004, S101, SLF001 - music_gen sources: PLC0415 (heavy ML lazy imports) - moviepy_showcase: PLC0415 (circular dependency) - generate_images: PLR0913 (matplotlib helpers need many params) - praca_magisterska_video: E501, E402 (long paths, mpl.use)
274 lines
8.9 KiB
Python
274 lines
8.9 KiB
Python
"""Shared fixtures and moviepy mocking for praca_magisterska_video tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import importlib
|
|
import importlib.util as _ilu
|
|
from pathlib import Path
|
|
import sys
|
|
from typing import TYPE_CHECKING
|
|
from unittest.mock import MagicMock
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
from types import ModuleType
|
|
|
|
# Add the source directory to sys.path so bare imports like
|
|
# ``from _q24_common import ...`` resolve correctly.
|
|
_SRC_DIR = str(Path(__file__).resolve().parent.parent)
|
|
if _SRC_DIR not in sys.path:
|
|
sys.path.insert(0, _SRC_DIR)
|
|
|
|
# Also add generate_images/ so bare imports like ``from _pubsub_common import ...``
|
|
# used by sub-modules within that directory resolve correctly.
|
|
_GEN_DIR = str(Path(__file__).resolve().parent.parent / "generate_images")
|
|
if _GEN_DIR not in sys.path:
|
|
sys.path.insert(0, _GEN_DIR)
|
|
|
|
|
|
def _make_moviepy_mocks() -> dict[str, ModuleType | MagicMock]:
|
|
"""Build a mapping of module names to mocks for moviepy and heavy deps."""
|
|
mocks: dict[str, ModuleType | MagicMock] = {}
|
|
|
|
# Main moviepy module
|
|
moviepy_mod = MagicMock()
|
|
|
|
# VideoClip: needs to accept make_frame callable -> return mock with methods
|
|
def _video_clip_factory(
|
|
make_frame: Callable[[float], np.ndarray] | None = None,
|
|
duration: float | None = None,
|
|
**_kw: object,
|
|
) -> MagicMock:
|
|
clip = MagicMock()
|
|
clip.make_frame = make_frame
|
|
clip.duration = duration
|
|
clip.with_fps.return_value = clip
|
|
clip.with_duration.return_value = clip
|
|
clip.with_position.return_value = clip
|
|
clip.with_effects.return_value = clip
|
|
# If there is a make_frame callable, call it to exercise branches
|
|
if callable(make_frame) and duration is not None:
|
|
frame = make_frame(0.0)
|
|
assert isinstance(frame, np.ndarray)
|
|
# Call at ~40% progress to hit mid-range branches (e.g. for/else break)
|
|
make_frame(duration * 0.4)
|
|
# Also call at ~70% progress for branch coverage
|
|
make_frame(duration * 0.75)
|
|
# Also call near end
|
|
make_frame(duration * 0.99)
|
|
return clip
|
|
|
|
moviepy_mod.VideoClip = _video_clip_factory
|
|
|
|
def _color_clip_factory(
|
|
_size: tuple[int, int] | None = None,
|
|
_color: tuple[int, ...] | None = None,
|
|
**_kw: object,
|
|
) -> MagicMock:
|
|
clip = MagicMock()
|
|
clip.with_duration.return_value = clip
|
|
return clip
|
|
|
|
moviepy_mod.ColorClip = _color_clip_factory
|
|
|
|
def _text_clip_factory(**_kw: object) -> MagicMock:
|
|
clip = MagicMock()
|
|
clip.with_duration.return_value = clip
|
|
clip.with_position.return_value = clip
|
|
return clip
|
|
|
|
moviepy_mod.TextClip = _text_clip_factory
|
|
|
|
def _composite_factory(
|
|
_clips: list[MagicMock] | None = None,
|
|
_size: tuple[int, int] | None = None,
|
|
**_kw: object,
|
|
) -> MagicMock:
|
|
clip = MagicMock()
|
|
clip.with_effects.return_value = clip
|
|
clip.with_duration.return_value = clip
|
|
clip.write_videofile = MagicMock()
|
|
return clip
|
|
|
|
moviepy_mod.CompositeVideoClip = _composite_factory
|
|
|
|
def _concat_factory(
|
|
_clips: list[MagicMock] | None = None,
|
|
_method: str | None = None,
|
|
**_kw: object,
|
|
) -> MagicMock:
|
|
clip = MagicMock()
|
|
clip.write_videofile = MagicMock()
|
|
return clip
|
|
|
|
moviepy_mod.concatenate_videoclips = _concat_factory
|
|
|
|
mocks["moviepy"] = moviepy_mod
|
|
mocks["moviepy.video"] = MagicMock()
|
|
mocks["moviepy.video.fx"] = MagicMock()
|
|
|
|
return mocks
|
|
|
|
|
|
# Install mocks at import time so module-level code in source files works.
|
|
_MOVIEPY_MOCKS = _make_moviepy_mocks()
|
|
for _name, _mock in _MOVIEPY_MOCKS.items():
|
|
sys.modules[_name] = _mock
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Handle the _q24_common name collision.
|
|
# Both _SRC_DIR (top-level) and _GEN_DIR (generate_images/) contain a
|
|
# file called ``_q24_common.py`` with different contents.
|
|
# * top-level → moviepy video helpers (W, H, BG_COLOR, FONT_B, …)
|
|
# * gen_images → matplotlib draw helpers (draw_box, draw_arrow, …)
|
|
#
|
|
# Strategy:
|
|
# 1. Load the generate_images version and cache it as bare ``_q24_common``
|
|
# so generate_images sub-modules (imported in _BARE_MODULES below)
|
|
# find the right one when they do ``from _q24_common import draw_box``.
|
|
# 2. After _BARE_MODULES are all imported, swap ``_q24_common`` in
|
|
# sys.modules to the top-level version so that top-level source
|
|
# modules (``_q24_classical.py``, etc.) find ``BG_COLOR`` etc.
|
|
# 3. Register both under their full package paths for coverage.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Load generate_images _q24_common first.
|
|
_gen_q24_spec = _ilu.spec_from_file_location(
|
|
"_q24_common",
|
|
str(Path(_GEN_DIR) / "_q24_common.py"),
|
|
)
|
|
assert _gen_q24_spec is not None
|
|
assert _gen_q24_spec.loader is not None
|
|
_q24_common_gen = _ilu.module_from_spec(_gen_q24_spec)
|
|
# Register BEFORE exec so @dataclass can resolve __module__ in Python 3.14+.
|
|
sys.modules["_q24_common"] = _q24_common_gen
|
|
_gen_q24_spec.loader.exec_module(_q24_common_gen)
|
|
|
|
# Load top-level _q24_common.
|
|
_top_q24_spec = _ilu.spec_from_file_location(
|
|
"_q24_common_top",
|
|
str(Path(_SRC_DIR) / "_q24_common.py"),
|
|
)
|
|
assert _top_q24_spec is not None
|
|
assert _top_q24_spec.loader is not None
|
|
_q24_common_top = _ilu.module_from_spec(_top_q24_spec)
|
|
# Register BEFORE exec so @dataclass can resolve __module__ in Python 3.14+.
|
|
sys.modules["_q24_common_top"] = _q24_common_top
|
|
_top_q24_spec.loader.exec_module(_q24_common_top)
|
|
|
|
|
|
# Register generate_images sub-modules under their full package paths so
|
|
# coverage can track them correctly. The bare names are resolved via
|
|
# _GEN_DIR added to sys.path above.
|
|
_GEN_PKG = "python_pkg.praca_magisterska_video.generate_images"
|
|
_BARE_MODULES = [
|
|
"_pubsub_common",
|
|
"_pubsub_qos",
|
|
"_pubsub_topic_content",
|
|
"_pubsub_type_hierarchical",
|
|
"_q20_common",
|
|
"_q20_batch_and_windows",
|
|
"_q20_time_monitoring_sessions",
|
|
"_q20_platforms",
|
|
"_q20_architectures",
|
|
"_q20_late_and_decisions",
|
|
"generate_pubsub_diagrams",
|
|
"generate_q20_diagrams",
|
|
"_q23_common",
|
|
"_q23_architectures",
|
|
"_q23_diy_unet",
|
|
"_q23_mean_shift_ncuts",
|
|
"_q23_mnemonics",
|
|
"_q23_nn_basics",
|
|
"_q23_otsu_watershed",
|
|
"_q23_receptive_transformer",
|
|
"_q23_region_diy",
|
|
"generate_q23_diagrams",
|
|
"_q24_fpn_tasks_cnn",
|
|
"_q24_haar_integral_svm",
|
|
"_q24_hog_classical",
|
|
"_q24_iou_nms_detector",
|
|
"_q24_modern_pipelines",
|
|
"_q24_rcnn_yolo",
|
|
"generate_q24_diagrams",
|
|
"_q31_common",
|
|
"_q31_criteria_comparison",
|
|
"_q31_ev_spectrum",
|
|
"_q31_hurwicz_mnemonic",
|
|
"_q31_regret_matrix",
|
|
"generate_q31_diagrams",
|
|
"_q9_common",
|
|
"_q9_basics",
|
|
"_q9_classic_sync",
|
|
"_q9_ipc",
|
|
"_q9_race_deadlock",
|
|
"generate_q9_all_diagrams",
|
|
"_q9q12_common",
|
|
"_q9q12_network_flow",
|
|
"_q9q12_network_graph",
|
|
"_q9q12_processes",
|
|
"generate_q9_q12_diagrams",
|
|
"generate_robot_lang_diagrams",
|
|
"_robot_movement_ros",
|
|
"_robot_pyramid_vendor",
|
|
"_robot_ros_rapid",
|
|
"_sched_common",
|
|
"_sched_complexity_edd",
|
|
"_sched_graham",
|
|
"_sched_johnson",
|
|
"_sched_spt_flow_job",
|
|
"generate_scheduling_diagrams",
|
|
]
|
|
for _bare in _BARE_MODULES:
|
|
with contextlib.suppress(ImportError):
|
|
_mod = importlib.import_module(_bare)
|
|
sys.modules.setdefault(f"{_GEN_PKG}.{_bare}", _mod)
|
|
|
|
# Now swap _q24_common to the top-level version so that top-level source
|
|
# modules (``_q24_classical.py`` etc.) find BG_COLOR, W, H, etc.
|
|
sys.modules["_q24_common"] = _q24_common_top
|
|
sys.modules.setdefault(
|
|
"python_pkg.praca_magisterska_video._q24_common", _q24_common_top
|
|
)
|
|
sys.modules.setdefault(f"{_GEN_PKG}._q24_common", _q24_common_gen)
|
|
|
|
|
|
def reload_module(module_name: str) -> ModuleType:
|
|
"""Force re-import of a module to re-execute its module-level code."""
|
|
mod = importlib.import_module(module_name)
|
|
return importlib.reload(mod)
|
|
|
|
|
|
@pytest.fixture
|
|
def _no_savefig(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Prevent matplotlib from writing files to disk."""
|
|
import matplotlib.figure
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.table
|
|
|
|
monkeypatch.setattr(matplotlib.figure.Figure, "savefig", lambda *_a, **_kw: None)
|
|
monkeypatch.setattr(plt, "savefig", lambda *_a, **_kw: None)
|
|
|
|
# Source files use auto_set_font_size(auto=False) but matplotlib 3.10+
|
|
# renamed the parameter to ``value``.
|
|
_orig = matplotlib.table.Table.auto_set_font_size
|
|
|
|
def _compat_auto_set_font_size(
|
|
self: matplotlib.table.Table,
|
|
*,
|
|
value: bool = True,
|
|
**_kw: object,
|
|
) -> None:
|
|
_orig(self, value)
|
|
|
|
monkeypatch.setattr(
|
|
matplotlib.table.Table,
|
|
"auto_set_font_size",
|
|
_compat_auto_set_font_size,
|
|
)
|