testsAndMisc/python_pkg/praca_magisterska_video/tests/test_gen_q23.py
Krzysztof kuhy Rudnicki 2545d72710 test: achieve 100% branch coverage across all python_pkg packages
- Add comprehensive tests for all packages (3572 tests, 100% branch coverage)
- Split oversized test files to stay under 500-line limit
- Add per-file ruff ignores for test-appropriate suppressions
- Fix _cache_decks.py to properly convert JSON lists to tuples
- Add session-scoped conftest fixture for logging handler cleanup (Python 3.14)
- Update ruff pre-commit hook to v0.15.2
- Add codespell ignore words for test data
- Add generated output files to .gitignore
2026-03-21 17:51:36 +01:00

496 lines
14 KiB
Python

"""Tests for Q23 image-segmentation diagram modules (BATCH 3 / GROUP 1).
Covers:
- _q23_common.py (constants, _save_figure, _render_text_lines)
- _q23_architectures.py (generate_fcn, generate_unet)
- _q23_diy_unet.py (generate_diy_unet, _draw_unet_layer_stack,
_draw_unet_pseudocode)
- _q23_mean_shift_ncuts.py (generate_mean_shift, generate_normalized_cuts,
_draw_ncuts_pixel_grid, _draw_ncuts_edges)
- _q23_mnemonics.py (generate_mnemonics)
- _q23_nn_basics.py (generate_relu, generate_dot_product)
- _q23_otsu_watershed.py (generate_otsu_bimodal, generate_watershed,
_draw_otsu_variance_panel, _draw_watershed_result_panel)
- _q23_receptive_transformer.py (generate_receptive_field, generate_transformer)
- _q23_region_diy.py (generate_region_growing, generate_diy_thresholding,
_draw_region_growing_grid, _draw_bfs_expansion,
_draw_otsu_variance_and_pseudocode)
- generate_q23_diagrams.py (__all__, imports, __main__ block)
"""
from __future__ import annotations
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pytest
pytestmark = pytest.mark.usefixtures("_no_savefig")
# ── _q23_common ───────────────────────────────────────────────────────
class TestQ23Constants:
"""Module-level constants and singletons."""
def test_dpi(self) -> None:
from _q23_common import DPI
assert DPI == 300
def test_output_dir_is_str(self) -> None:
from _q23_common import OUTPUT_DIR
assert isinstance(OUTPUT_DIR, str)
def test_color_constants(self) -> None:
from _q23_common import (
ACCENT,
ACCENT_LIGHT,
BLACK,
GRAY1,
GRAY2,
GRAY3,
GRAY4,
GRAY5,
GRAY6,
GREEN_ACCENT,
RED_ACCENT,
WHITE,
)
colors = [
BLACK,
WHITE,
GRAY1,
GRAY2,
GRAY3,
GRAY4,
GRAY5,
GRAY6,
ACCENT,
ACCENT_LIGHT,
RED_ACCENT,
GREEN_ACCENT,
]
assert all(isinstance(c, str) and c.startswith("#") for c in colors)
def test_font_size_constants(self) -> None:
from _q23_common import FS, FS_SMALL, FS_TINY, FS_TITLE
assert FS_TITLE > FS > FS_SMALL > FS_TINY
def test_threshold_constants(self) -> None:
from _q23_common import (
_BRIGHT_THRESHOLD,
_DARK_PIXEL_THRESHOLD,
_GRID_LAST_IDX,
_HIGHLIGHT_END,
_HIGHLIGHT_START,
_OTSU_THRESHOLD,
_RIDGE_X,
_VALLEY2_END,
)
assert _DARK_PIXEL_THRESHOLD == 100
assert _GRID_LAST_IDX == 3
assert _HIGHLIGHT_START == 3
assert _HIGHLIGHT_END == 5
assert _BRIGHT_THRESHOLD == 170
assert _OTSU_THRESHOLD == 128
assert _RIDGE_X == 5
assert _VALLEY2_END == 9
def test_rng_exists(self) -> None:
from _q23_common import rng
assert rng is not None
class TestQ23SaveFigure:
"""_save_figure from _q23_common."""
def test_runs(self) -> None:
from _q23_common import _save_figure
_fig, _ax = plt.subplots()
_save_figure("test_q23_save.png")
class TestQ23RenderTextLines:
"""_render_text_lines from _q23_common."""
def test_basic_lines(self) -> None:
from _q23_common import _render_text_lines
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
lines = [
("Hello", 10, "black", "bold"),
("World", 8, "gray", "normal"),
]
_render_text_lines(ax, lines, start_y=9.0)
plt.close(fig)
def test_empty_line_gaps(self) -> None:
from _q23_common import _render_text_lines
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
lines = [
("First", 10, "black", "bold"),
("", 0, "", ""),
("After gap", 10, "black", "normal"),
]
_render_text_lines(ax, lines, start_y=9.0, y_step=0.5, y_empty_step=0.3)
plt.close(fig)
def test_custom_x_pos(self) -> None:
from _q23_common import _render_text_lines
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
lines = [("Test", 10, "red", "normal")]
_render_text_lines(ax, lines, x_pos=0.3, start_y=8.0)
plt.close(fig)
# ── _q23_architectures ───────────────────────────────────────────────
class TestGenerateFCN:
"""generate_fcn from _q23_architectures."""
def test_runs(self) -> None:
from _q23_architectures import generate_fcn
generate_fcn()
class TestGenerateUNet:
"""generate_unet from _q23_architectures."""
def test_runs(self) -> None:
from _q23_architectures import generate_unet
generate_unet()
# ── _q23_diy_unet ────────────────────────────────────────────────────
class TestDrawUnetLayerStack:
"""_draw_unet_layer_stack from _q23_diy_unet."""
def test_without_skip(self) -> None:
from _q23_diy_unet import _draw_unet_layer_stack
fig, ax = plt.subplots()
_draw_unet_layer_stack(
ax,
[(64, 3), (32, 64), (16, 128)],
face_color="#B3D4FC",
edge_color="#4A90D9",
arrow_color="#4A90D9",
arrow_label="Conv+Pool",
)
plt.close(fig)
def test_with_skip(self) -> None:
from _q23_diy_unet import _draw_unet_layer_stack
fig, ax = plt.subplots()
_draw_unet_layer_stack(
ax,
[(8, 256), (16, 128), (32, 64)],
face_color="#C8E6C9",
edge_color="#388E3C",
arrow_color="#388E3C",
arrow_label="UpConv+Concat",
add_skip=True,
)
plt.close(fig)
def test_single_layer_no_arrows(self) -> None:
from _q23_diy_unet import _draw_unet_layer_stack
fig, ax = plt.subplots()
_draw_unet_layer_stack(
ax,
[(64, 3)],
face_color="#B3D4FC",
edge_color="#4A90D9",
arrow_color="#4A90D9",
arrow_label="X",
)
plt.close(fig)
class TestDrawUnetPseudocode:
"""_draw_unet_pseudocode from _q23_diy_unet."""
def test_runs(self) -> None:
from _q23_diy_unet import _draw_unet_pseudocode
fig, ax = plt.subplots()
_draw_unet_pseudocode(ax)
plt.close(fig)
class TestGenerateDiyUnet:
"""generate_diy_unet from _q23_diy_unet."""
@pytest.mark.filterwarnings("ignore::UserWarning")
def test_runs(self) -> None:
from _q23_diy_unet import generate_diy_unet
generate_diy_unet()
# ── _q23_mean_shift_ncuts ────────────────────────────────────────────
class TestGenerateMeanShift:
"""generate_mean_shift from _q23_mean_shift_ncuts."""
def test_runs(self) -> None:
from _q23_mean_shift_ncuts import generate_mean_shift
generate_mean_shift()
class TestDrawNcutsPixelGrid:
"""_draw_ncuts_pixel_grid from _q23_mean_shift_ncuts."""
def test_runs(self) -> None:
from _q23_mean_shift_ncuts import _draw_ncuts_pixel_grid
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
pixel_vals = np.array(
[
[30, 35, 180, 190],
[40, 30, 185, 200],
[170, 180, 40, 35],
[190, 175, 30, 45],
]
)
_draw_ncuts_pixel_grid(ax, pixel_vals)
plt.close(fig)
def test_bright_pixels(self) -> None:
"""All pixels above dark threshold → black text."""
from _q23_mean_shift_ncuts import _draw_ncuts_pixel_grid
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
bright = np.full((4, 4), 200)
_draw_ncuts_pixel_grid(ax, bright)
plt.close(fig)
def test_dark_pixels(self) -> None:
"""All pixels below dark threshold → white text."""
from _q23_mean_shift_ncuts import _draw_ncuts_pixel_grid
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
dark = np.full((4, 4), 50)
_draw_ncuts_pixel_grid(ax, dark)
plt.close(fig)
class TestDrawNcutsEdges:
"""_draw_ncuts_edges from _q23_mean_shift_ncuts."""
def test_runs(self) -> None:
from _q23_mean_shift_ncuts import _draw_ncuts_edges
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
pixel_vals = np.array(
[
[30, 35, 180, 190],
[40, 30, 185, 200],
[170, 180, 40, 35],
[190, 175, 30, 45],
]
)
_draw_ncuts_edges(ax, pixel_vals)
plt.close(fig)
def test_uniform_values(self) -> None:
"""All same values → max similarity everywhere."""
from _q23_mean_shift_ncuts import _draw_ncuts_edges
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4.5)
uniform = np.full((4, 4), 128)
_draw_ncuts_edges(ax, uniform)
plt.close(fig)
class TestGenerateNormalizedCuts:
"""generate_normalized_cuts from _q23_mean_shift_ncuts."""
def test_runs(self) -> None:
from _q23_mean_shift_ncuts import generate_normalized_cuts
generate_normalized_cuts()
# ── _q23_mnemonics ───────────────────────────────────────────────────
class TestGenerateMnemonics:
"""generate_mnemonics from _q23_mnemonics."""
def test_runs(self) -> None:
from _q23_mnemonics import generate_mnemonics
generate_mnemonics()
# ── _q23_nn_basics ───────────────────────────────────────────────────
class TestGenerateRelu:
"""generate_relu from _q23_nn_basics."""
def test_runs(self) -> None:
from _q23_nn_basics import generate_relu
generate_relu()
class TestGenerateDotProduct:
"""generate_dot_product from _q23_nn_basics."""
def test_runs(self) -> None:
from _q23_nn_basics import generate_dot_product
generate_dot_product()
# ── _q23_otsu_watershed ──────────────────────────────────────────────
class TestDrawOtsuVariancePanel:
"""_draw_otsu_variance_panel from _q23_otsu_watershed."""
def test_runs(self) -> None:
from _q23_otsu_watershed import _draw_otsu_variance_panel
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
_draw_otsu_variance_panel(ax)
plt.close(fig)
class TestGenerateOtsuBimodal:
"""generate_otsu_bimodal from _q23_otsu_watershed."""
def test_runs(self) -> None:
from _q23_otsu_watershed import generate_otsu_bimodal
generate_otsu_bimodal()
class TestDrawWatershedResultPanel:
"""_draw_watershed_result_panel from _q23_otsu_watershed."""
def test_runs(self) -> None:
from _q23_otsu_watershed import _draw_watershed_result_panel
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
_draw_watershed_result_panel(ax)
plt.close(fig)
class TestGenerateWatershed:
"""generate_watershed from _q23_otsu_watershed."""
def test_runs(self) -> None:
from _q23_otsu_watershed import generate_watershed
generate_watershed()
# ── _q23_receptive_transformer ───────────────────────────────────────
class TestGenerateReceptiveField:
"""generate_receptive_field from _q23_receptive_transformer."""
def test_runs(self) -> None:
from _q23_receptive_transformer import generate_receptive_field
generate_receptive_field()
class TestGenerateTransformer:
"""generate_transformer from _q23_receptive_transformer."""
def test_runs(self) -> None:
from _q23_receptive_transformer import generate_transformer
generate_transformer()
# ── _q23_region_diy ──────────────────────────────────────────────────
class TestDrawRegionGrowingGrid:
"""_draw_region_growing_grid from _q23_region_diy."""
def test_runs(self) -> None:
from _q23_region_diy import _draw_region_growing_grid
fig, ax = plt.subplots()
_draw_region_growing_grid(ax)
plt.close(fig)
def test_bright_pixels_in_region(self) -> None:
"""Hit elif branch: masked pixel >= _BRIGHT_THRESHOLD."""
from unittest.mock import patch
from _q23_region_diy import _draw_region_growing_grid
fig, ax = plt.subplots()
with patch("_q23_region_diy._BRIGHT_THRESHOLD", 0):
_draw_region_growing_grid(ax)
plt.close(fig)
class TestDrawBfsExpansion:
"""_draw_bfs_expansion from _q23_region_diy."""
def test_runs(self) -> None:
from _q23_region_diy import _draw_bfs_expansion
fig, ax = plt.subplots()
_draw_bfs_expansion(ax)
plt.close(fig)
class TestGenerateRegionGrowing:
"""generate_region_growing from _q23_region_diy."""
def test_runs(self) -> None:
from _q23_region_diy import generate_region_growing
generate_region_growing()