Что то сделал
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
"""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"<svg></svg>"
|
||||
)
|
||||
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(
|
||||
"<svg xmlns='http://www.w3.org/2000/svg'><text>Клиент</text></svg>",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return True
|
||||
|
||||
with patch("md2gost.dfd._compile_dot", return_value=(fake_dot, False)), \
|
||||
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_falls_back_to_kroki(tmp_path):
|
||||
png = tmp_path / "a.png"
|
||||
|
||||
def fake_kroki(dot, out_path, fmt):
|
||||
Path(out_path).write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 32)
|
||||
return True
|
||||
|
||||
with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \
|
||||
patch("md2gost.dfd._render_dot_local", return_value=False), \
|
||||
patch("md2gost.dfd._render_dot_kroki", side_effect=fake_kroki):
|
||||
assert write_dfd_outputs(SAMPLE_CONTEXT, png, None, scale=1)
|
||||
assert png.read_bytes().startswith(b"\x89PNG")
|
||||
|
||||
|
||||
def test_write_dfd_outputs_raises_when_no_renderer(tmp_path):
|
||||
with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \
|
||||
patch("md2gost.dfd._render_dot_local", return_value=False), \
|
||||
patch("md2gost.dfd._render_dot_kroki", return_value=False):
|
||||
with pytest.raises(DfdParseError, match="Graphviz|Kroki"):
|
||||
write_dfd_outputs(SAMPLE_CONTEXT, tmp_path / "x.png", None)
|
||||
|
||||
|
||||
def test_render_diagram_dfd_skips_jar(tmp_path):
|
||||
calls = {"jar": 0}
|
||||
|
||||
def fake_jar(*args, **kwargs):
|
||||
calls["jar"] += 1
|
||||
return False
|
||||
|
||||
def fake_write(source, out_png, out_svg, *, scale=2.0):
|
||||
Path(out_png).write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 32)
|
||||
return True
|
||||
|
||||
configure_diagrams(fallback="off", cache_dir=str(tmp_path), diagram_scale=1)
|
||||
with patch("md2gost.diagram_renderer._render_plantuml_jar", side_effect=fake_jar), \
|
||||
patch("md2gost.dfd.write_dfd_outputs", side_effect=fake_write):
|
||||
result = render_diagram(
|
||||
"dfd", SAMPLE_CONTEXT, cache_dir=str(tmp_path), diagram_scale=1,
|
||||
)
|
||||
assert calls["jar"] == 0
|
||||
assert Path(result.png_path).read_bytes().startswith(b"\x89PNG")
|
||||
|
||||
|
||||
def test_render_diagram_dfd_bad_source(tmp_path):
|
||||
configure_diagrams(fallback="off", cache_dir=str(tmp_path))
|
||||
with patch("md2gost.dfd._compile_dot", side_effect=DfdParseError("пустой DFD")):
|
||||
with pytest.raises(RuntimeError, match="пустой|DFD"):
|
||||
render_diagram("dfd", " ", cache_dir=str(tmp_path))
|
||||
|
||||
|
||||
def test_factory_routes_dfd_to_diagram():
|
||||
from docx import Document
|
||||
from md2gost import package_dir
|
||||
import os
|
||||
|
||||
md = "%ctx Контекст DFD\n\n```dfd\n" + SAMPLE_CONTEXT + "```\n"
|
||||
parsed = markdown.parse(md)
|
||||
doc = Document(os.path.join(package_dir(), "Template.docx"))
|
||||
factory = RenderableFactory(doc._body)
|
||||
caption = None
|
||||
renderables = []
|
||||
for el in parsed.children:
|
||||
from marko.block import BlankLine
|
||||
if isinstance(el, BlankLine):
|
||||
continue
|
||||
if isinstance(el, CapEl):
|
||||
caption = CaptionInfo(el.unique_name, el.text, el.with_listing)
|
||||
continue
|
||||
renderables.append(factory.create(el, caption))
|
||||
caption = None
|
||||
assert len(renderables) == 1
|
||||
assert isinstance(renderables[0], DiagramFigure)
|
||||
assert renderables[0]._lang == "dfd"
|
||||
Reference in New Issue
Block a user