testsAndMisc/python_pkg/praca_magisterska_video/tests/test_gen_q9.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

344 lines
9.5 KiB
Python

"""Tests for Q9 diagram generation (concurrency: processes & threads)."""
from __future__ import annotations
import matplotlib as mpl
import matplotlib.pyplot as plt
import pytest
@pytest.fixture(autouse=True)
def _patch_savefig(monkeypatch: pytest.MonkeyPatch) -> None:
"""Prevent matplotlib from writing files to disk."""
monkeypatch.setattr(mpl.figure.Figure, "savefig", lambda *_a, **_kw: None)
monkeypatch.setattr(plt, "savefig", lambda *_a, **_kw: None)
# =====================================================================
# _q9_common
# =====================================================================
class TestQ9Common:
"""Tests for _q9_common constants and helpers."""
def test_constants_exist(self) -> None:
from _q9_common import (
BG,
DPI,
FS,
FS_LABEL,
FS_SMALL,
FS_TITLE,
GRAY1,
GRAY2,
GRAY3,
GRAY4,
GRAY5,
LN,
OCCUPIED_SLOTS,
OUTPUT_DIR,
)
assert DPI == 300
assert BG == "white"
assert LN == "black"
assert FS == 8
assert FS_TITLE == 11
assert OCCUPIED_SLOTS == 2
assert isinstance(FS_SMALL, float)
assert isinstance(FS_LABEL, int)
assert isinstance(GRAY1, str)
assert isinstance(GRAY2, str)
assert isinstance(GRAY3, str)
assert isinstance(GRAY4, str)
assert isinstance(GRAY5, str)
assert isinstance(OUTPUT_DIR, str)
def test_draw_box_rounded(self) -> None:
from _q9_common import draw_box
fig, ax = plt.subplots()
draw_box(ax, 1.0, 2.0, 3.0, 1.0, "test")
assert len(ax.patches) == 1
plt.close(fig)
def test_draw_box_not_rounded(self) -> None:
from _q9_common import draw_box
fig, ax = plt.subplots()
draw_box(ax, 0.0, 0.0, 2.0, 1.0, "rect", rounded=False)
assert len(ax.patches) == 1
plt.close(fig)
def test_draw_box_custom_edgecolor_linestyle(self) -> None:
from _q9_common import draw_box
fig, ax = plt.subplots()
draw_box(
ax,
0.0,
0.0,
2.0,
1.0,
"custom",
edgecolor="red",
linestyle="--",
rounded=True,
)
assert len(ax.patches) == 1
plt.close(fig)
def test_draw_box_not_rounded_custom_linestyle(self) -> None:
from _q9_common import draw_box
fig, ax = plt.subplots()
draw_box(
ax,
0.0,
0.0,
2.0,
1.0,
"dashed",
edgecolor="blue",
linestyle="--",
rounded=False,
)
assert len(ax.patches) == 1
plt.close(fig)
def test_draw_arrow(self) -> None:
from _q9_common import draw_arrow
fig, ax = plt.subplots()
draw_arrow(ax, 0.0, 0.0, 1.0, 1.0)
plt.close(fig)
def test_draw_double_arrow(self) -> None:
from _q9_common import draw_double_arrow
fig, ax = plt.subplots()
draw_double_arrow(ax, 0.0, 0.0, 1.0, 1.0)
plt.close(fig)
def test_save_fig(self) -> None:
from _q9_common import save_fig
fig, _ax = plt.subplots()
save_fig(fig, "test_output.png")
def test_draw_table(self) -> None:
from _q9_common import draw_table
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-5, 2)
headers = ["A", "B", "C"]
rows = [["1", "2", "3"], ["4", "5", "6"]]
draw_table(ax, headers, rows, 0, 1, [2.0, 3.0, 3.0], row_h=0.5)
assert len(ax.patches) > 0
plt.close(fig)
def test_draw_table_custom_fills(self) -> None:
from _q9_common import draw_table
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-5, 2)
headers = ["A", "B"]
rows = [["1", "2"], ["3", "4"]]
draw_table(
ax,
headers,
rows,
0,
1,
[2.0, 3.0],
row_fills=["#FF0000", "#00FF00"],
header_fontsize=10,
)
plt.close(fig)
def test_draw_table_no_header_fontsize(self) -> None:
from _q9_common import draw_table
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-5, 2)
draw_table(
ax,
["H1"],
[["V1"]],
0,
1,
[3.0],
header_fontsize=None,
)
plt.close(fig)
# =====================================================================
# _q9_basics
# =====================================================================
class TestQ9Basics:
"""Tests for the 6 basic process/thread diagrams."""
def test_gen_process_vs_thread(self) -> None:
from _q9_basics import gen_process_vs_thread
gen_process_vs_thread()
def test_gen_memory_layout(self) -> None:
from _q9_basics import gen_memory_layout
gen_memory_layout()
def test_gen_process_states(self) -> None:
from _q9_basics import gen_process_states
gen_process_states()
def test_gen_thread_structure(self) -> None:
from _q9_basics import gen_thread_structure
gen_thread_structure()
def test_gen_pcb_structure(self) -> None:
from _q9_basics import gen_pcb_structure
gen_pcb_structure()
def test_gen_speed_comparison(self) -> None:
from _q9_basics import gen_speed_comparison
gen_speed_comparison()
# =====================================================================
# _q9_classic_sync
# =====================================================================
class TestQ9ClassicSync:
"""Tests for classic sync problems."""
def test_draw_bounded_buffer_panel(self) -> None:
from _q9_classic_sync import _draw_bounded_buffer_panel
fig, ax = plt.subplots()
_draw_bounded_buffer_panel(ax)
assert len(ax.patches) > 0
plt.close(fig)
def test_draw_readers_writers_panel(self) -> None:
from _q9_classic_sync import _draw_readers_writers_panel
fig, ax = plt.subplots()
_draw_readers_writers_panel(ax)
assert len(ax.patches) > 0
plt.close(fig)
def test_draw_philosophers_panel(self) -> None:
from _q9_classic_sync import _draw_philosophers_panel
fig, ax = plt.subplots()
_draw_philosophers_panel(ax)
assert len(ax.patches) > 0
plt.close(fig)
def test_gen_classic_problems(self) -> None:
from _q9_classic_sync import gen_classic_problems
gen_classic_problems()
def test_gen_sync_comparison(self) -> None:
from _q9_classic_sync import gen_sync_comparison
gen_sync_comparison()
def test_gen_semaphore_concept(self) -> None:
from _q9_classic_sync import gen_semaphore_concept
gen_semaphore_concept()
# =====================================================================
# _q9_ipc
# =====================================================================
class TestQ9Ipc:
"""Tests for IPC mechanism diagrams."""
def test_gen_scenario_table(self) -> None:
from _q9_ipc import gen_scenario_table
gen_scenario_table()
def test_gen_ipc_details(self) -> None:
from _q9_ipc import gen_ipc_details
gen_ipc_details()
def test_gen_ipc_table(self) -> None:
from _q9_ipc import gen_ipc_table
gen_ipc_table()
# =====================================================================
# _q9_race_deadlock
# =====================================================================
class TestQ9RaceDeadlock:
"""Tests for race condition, deadlock, and starvation diagrams."""
def test_gen_race_condition(self) -> None:
from _q9_race_deadlock import gen_race_condition
gen_race_condition()
def test_gen_deadlock_scenario(self) -> None:
from _q9_race_deadlock import gen_deadlock_scenario
gen_deadlock_scenario()
def test_gen_coffman_strategies(self) -> None:
from _q9_race_deadlock import gen_coffman_strategies
gen_coffman_strategies()
def test_gen_starvation_priority(self) -> None:
from _q9_race_deadlock import gen_starvation_priority
gen_starvation_priority()
# =====================================================================
# generate_q9_all_diagrams
# =====================================================================
class TestGenerateQ9AllDiagrams:
"""Tests for the Q9 diagram generation entrypoint."""
def test_module_exports(self) -> None:
from generate_q9_all_diagrams import __all__
expected = [
"gen_classic_problems",
"gen_coffman_strategies",
"gen_deadlock_scenario",
"gen_ipc_details",
"gen_ipc_table",
"gen_memory_layout",
"gen_pcb_structure",
"gen_process_states",
"gen_process_vs_thread",
"gen_race_condition",
"gen_scenario_table",
"gen_semaphore_concept",
"gen_speed_comparison",
"gen_starvation_priority",
"gen_sync_comparison",
"gen_thread_structure",
]
assert sorted(__all__) == sorted(expected)
def test_all_functions_callable(self) -> None:
import generate_q9_all_diagrams as mod
for name in mod.__all__:
assert callable(getattr(mod, name))