"""DFD via pbauermeister/data-flow-diagram (Graphviz / Kroki)."""
from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
import pytest
from md2gost.diagram_renderer import (
DIAGRAM_LANGS,
configure_diagrams,
is_diagram_lang,
prepare_source,
render_diagram,
)
from md2gost.dfd import (
SAMPLE_CONTEXT,
SAMPLE_LEVEL0,
DfdParseError,
_compile_dot,
write_dfd_outputs,
)
from md2gost.extended_markdown import markdown
from md2gost.extended_markdown.caption import Caption as CapEl
from md2gost.renderable.caption import CaptionInfo
from md2gost.renderable.diagram import DiagramFigure
from md2gost.renderable_factory import RenderableFactory
def test_is_diagram_lang_dfd():
assert is_diagram_lang("dfd")
assert is_diagram_lang("uml-dfd")
assert is_diagram_lang("data-flow-diagram")
assert is_diagram_lang("yourdon")
assert is_diagram_lang("uml-yourdon")
assert "dfd" in DIAGRAM_LANGS
def test_prepare_dfd_stays_native():
src, dtype = prepare_source("dfd", SAMPLE_CONTEXT)
assert dtype == "dfd"
assert "@startuml" not in src.lower()
assert "process" in src
assert "System" in src
for lang in ("uml-dfd", "data-flow-diagram", "yourdon"):
_, dtype2 = prepare_source(lang, SAMPLE_LEVEL0)
assert dtype2 == "dfd"
def test_compile_dot_empty_raises():
with pytest.raises(DfdParseError, match="пустой"):
_compile_dot(" \n\n ")
def test_compile_dot_sample(monkeypatch):
"""When the package is importable, real compile; else accept mock."""
try:
dot, is_ctx = _compile_dot(SAMPLE_CONTEXT)
except DfdParseError as exc:
if "data-flow-diagram не найден" in str(exc) or "не найден" in str(exc):
pytest.skip("data-flow-diagram not installed for this Python")
raise
assert "digraph" in dot.lower() or "graph" in dot.lower()
assert is_ctx is True
def test_strip_graphviz_tcl_libs(tmp_path):
from md2gost.dfd import GRAPHVIZ_TCL_DLL_NAMES, strip_graphviz_tcl_libs
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
(bin_dir / "dot.exe").write_bytes(b"mz")
(bin_dir / "tcl86t.dll").write_bytes(b"tcl")
(bin_dir / "gdtclft.dll").write_bytes(b"gd")
(bin_dir / "cgraph.dll").write_bytes(b"keep")
tcl_mod = tmp_path / "lib" / "graphviz" / "tcl"
tcl_mod.mkdir(parents=True)
(tcl_mod / "pkgIndex.tcl").write_text("x", encoding="utf-8")
removed = strip_graphviz_tcl_libs(tmp_path)
assert removed >= 3
assert not (bin_dir / "tcl86t.dll").exists()
assert not (bin_dir / "gdtclft.dll").exists()
assert (bin_dir / "dot.exe").exists()
assert (bin_dir / "cgraph.dll").exists()
assert not tcl_mod.exists()
for name in GRAPHVIZ_TCL_DLL_NAMES:
assert not (bin_dir / name).exists()
def test_write_dfd_outputs_uses_local_or_kroki(tmp_path):
png = tmp_path / "a.png"
svg = tmp_path / "a.svg"
fake_dot = "digraph G { a -> b; }\n"
def fake_local(dot, out_path, fmt, *, context):
Path(out_path).write_bytes(
b"\x89PNG\r\n\x1a\n" if fmt == "png" else b""
)
if fmt == "png":
# minimal valid-enough PNG header for startswith checks
Path(out_path).write_bytes(
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
+ b"\x00" * 20
)
else:
Path(out_path).write_text(
"",
encoding="utf-8",
)
return True
with patch("md2gost.dfd._compile_dot", return_value=(fake_dot, False)), \
patch("md2gost.dfd._render_dot_local_combined", return_value=set()), \
patch("md2gost.dfd._render_dot_local", side_effect=fake_local), \
patch("md2gost.dfd._render_dot_kroki", return_value=False):
assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1)
assert png.read_bytes().startswith(b"\x89PNG")
assert "Клиент" in svg.read_text(encoding="utf-8")
def test_write_dfd_outputs_one_graphviz_run_for_both_formats(tmp_path):
"""PNG + SVG come from a single Graphviz process; no per-format retry."""
png = tmp_path / "a.png"
svg = tmp_path / "a.svg"
single = []
def fake_combined(dot, targets, *, context):
for out_path, fmt in targets:
Path(out_path).write_bytes(
b"\x89PNG\r\n\x1a\n" + b"\x00" * 32 if fmt == "png" else b""
)
return {fmt for _, fmt in targets}
with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \
patch("md2gost.dfd._render_dot_local_combined", side_effect=fake_combined), \
patch("md2gost.dfd._render_dot_local", side_effect=lambda *a, **k: single.append(a) or True), \
patch("md2gost.dfd._render_dot_kroki", return_value=False):
assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1)
assert single == []
assert png.read_bytes().startswith(b"\x89PNG")
assert svg.read_bytes().startswith(b"")
return True
with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \
patch("md2gost.dfd._render_dot_local_combined", side_effect=fake_combined), \
patch("md2gost.dfd._render_dot_local", side_effect=fake_local), \
patch("md2gost.dfd._render_dot_kroki", return_value=False):
assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1)
assert retried == ["svg"]
assert svg.read_bytes().startswith(b"