v0.6.0 BigUpdate
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
"""DFD Гейна–Сарсона: локальный Pillow, без Graphviz."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from md2gost.dfd_gs import (
|
||||
SAMPLE,
|
||||
DfdGsParseError,
|
||||
_arrange,
|
||||
parse_dfd_gs,
|
||||
render_dfd_gs,
|
||||
)
|
||||
from md2gost.diagram_renderer import (
|
||||
DIAGRAM_LANGS,
|
||||
configure_diagrams,
|
||||
is_diagram_lang,
|
||||
prepare_source,
|
||||
render_diagram,
|
||||
)
|
||||
|
||||
|
||||
def test_fence_aliases_do_not_steal_yourdon():
|
||||
assert is_diagram_lang("dfd-gs")
|
||||
assert is_diagram_lang("uml-dfd-gs")
|
||||
assert is_diagram_lang("gane-sarson")
|
||||
assert is_diagram_lang("uml-gane-sarson")
|
||||
assert "dfd-gs" in DIAGRAM_LANGS
|
||||
assert prepare_source("dfd-gs", SAMPLE)[1] == "dfd-gs"
|
||||
assert prepare_source("gane-sarson", SAMPLE)[1] == "dfd-gs"
|
||||
assert prepare_source("uml-dfd-gs", SAMPLE)[1] == "dfd-gs"
|
||||
assert prepare_source("dfd", "entity A Клиент\n")[1] == "dfd"
|
||||
assert prepare_source("uml-dfd", "entity A Клиент\n")[1] == "dfd"
|
||||
assert prepare_source("yourdon", "entity A Клиент\n")[1] == "dfd"
|
||||
|
||||
|
||||
def test_badges_and_colon_label():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
entity A Художник
|
||||
entity B Заказчик
|
||||
process Accept Принять заявку
|
||||
process 3.1 Проверить срок
|
||||
store Orders Заказы
|
||||
store D4 Акты
|
||||
A --> Accept : файлы работы
|
||||
"""
|
||||
)
|
||||
by_id = {node.id: node for node in diagram.nodes}
|
||||
assert by_id["A"].badge == "1"
|
||||
assert by_id["B"].badge == "2"
|
||||
assert by_id["Accept"].badge == "1"
|
||||
assert by_id["3.1"].badge == "3.1"
|
||||
assert by_id["Orders"].badge == "D1"
|
||||
assert by_id["D4"].badge == "D4"
|
||||
assert diagram.flows[0].label == "файлы работы"
|
||||
assert diagram.form == "kit"
|
||||
assert diagram.title == ""
|
||||
|
||||
|
||||
def test_store_code_skips_taken_numbers():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
store D1 Заказы
|
||||
store Orders Журнал
|
||||
"""
|
||||
)
|
||||
badges = {node.id: node.badge for node in diagram.nodes}
|
||||
assert badges == {"D1": "D1", "Orders": "D2"}
|
||||
|
||||
|
||||
def test_parse_errors():
|
||||
with pytest.raises(DfdGsParseError, match="пустой"):
|
||||
parse_dfd_gs(" \n\n")
|
||||
with pytest.raises(DfdGsParseError, match="без узлов"):
|
||||
parse_dfd_gs("# только комментарий\n")
|
||||
with pytest.raises(DfdGsParseError, match="без узлов"):
|
||||
parse_dfd_gs("title Только шапка\n")
|
||||
with pytest.raises(DfdGsParseError, match="повтор"):
|
||||
parse_dfd_gs("entity A Один\nprocess A Два\n")
|
||||
with pytest.raises(DfdGsParseError, match="неизвестный узел"):
|
||||
parse_dfd_gs("entity A Один\nA --> B поток\n")
|
||||
with pytest.raises(DfdGsParseError, match="в себя"):
|
||||
parse_dfd_gs("entity A Один\nA --> A петля\n")
|
||||
with pytest.raises(DfdGsParseError, match="form"):
|
||||
parse_dfd_gs("form fancy\nentity A Один\n")
|
||||
with pytest.raises(DfdGsParseError, match="нет подписи"):
|
||||
parse_dfd_gs("process 1\n")
|
||||
|
||||
|
||||
def test_columns_and_straight_arrow():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
entity A Художник
|
||||
process P Принять
|
||||
store S Файлы
|
||||
A --> P заявка
|
||||
P --> S файл
|
||||
"""
|
||||
)
|
||||
layout = _arrange(diagram, 1)
|
||||
assert layout.placed["A"].x < layout.placed["P"].x < layout.placed["S"].x
|
||||
straight = _arrange(
|
||||
parse_dfd_gs("entity A Художник\nprocess P Принять\nA --> P заявка\n"),
|
||||
1,
|
||||
)
|
||||
pts = straight.routes[0].pts
|
||||
assert len(pts) == 2
|
||||
assert abs(pts[0][1] - pts[1][1]) < 1
|
||||
|
||||
|
||||
def test_opposite_flows_do_not_share_a_port():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
process P Принять
|
||||
store S Заказы
|
||||
P --> S статус
|
||||
S --> P обновление
|
||||
"""
|
||||
)
|
||||
layout = _arrange(diagram, 1)
|
||||
y1 = layout.routes[0].pts[0][1]
|
||||
y2 = layout.routes[1].pts[0][1]
|
||||
assert abs(y1 - y2) > 10
|
||||
|
||||
|
||||
def test_stacked_processes_link_vertically():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
process A Один
|
||||
process B Два
|
||||
A --> B дальше
|
||||
"""
|
||||
)
|
||||
pts = _arrange(diagram, 1).routes[0].pts
|
||||
assert len(pts) == 2
|
||||
assert abs(pts[0][0] - pts[1][0]) < 1
|
||||
assert pts[0][1] < pts[1][1]
|
||||
|
||||
|
||||
def test_skipped_process_is_an_elbow():
|
||||
diagram = parse_dfd_gs(
|
||||
"""
|
||||
process A Один
|
||||
process B Два
|
||||
process C Три
|
||||
A --> C дальше
|
||||
"""
|
||||
)
|
||||
pts = _arrange(diagram, 1).routes[0].pts
|
||||
assert len(pts) >= 3
|
||||
|
||||
|
||||
def test_render_png_and_svg_without_graphviz():
|
||||
png, svg = render_dfd_gs(parse_dfd_gs(SAMPLE), scale=1)
|
||||
assert png.startswith(b"\x89PNG")
|
||||
assert svg is not None
|
||||
assert "USED AT" in svg
|
||||
assert "Тарасов" in svg
|
||||
assert "A44" in svg
|
||||
assert "rx=" in svg
|
||||
assert "Художник" in svg
|
||||
assert "rotate(-90" in svg
|
||||
assert "D2" in svg
|
||||
|
||||
plain = parse_dfd_gs("form plain\nentity A Художник\nprocess P Принять\nA --> P заявка\n")
|
||||
_png, svg_plain = render_dfd_gs(plain, scale=1)
|
||||
assert "USED AT" not in (svg_plain or "")
|
||||
assert "rx=" in (svg_plain or "")
|
||||
|
||||
|
||||
def test_render_diagram_skips_jar_and_kroki(tmp_path):
|
||||
calls = {"jar": 0, "kroki": 0}
|
||||
|
||||
def fake_jar(*_args, **_kwargs):
|
||||
calls["jar"] += 1
|
||||
return False
|
||||
|
||||
def fake_kroki(*_args, **_kwargs):
|
||||
calls["kroki"] += 1
|
||||
return False
|
||||
|
||||
configure_diagrams(fallback="off", cache_dir=str(tmp_path), diagram_scale=1, diagram_format="svg")
|
||||
with patch("md2gost.diagram_renderer._render_plantuml_jar", side_effect=fake_jar), \
|
||||
patch("md2gost.diagram_renderer._render_kroki", side_effect=fake_kroki):
|
||||
result = render_diagram(
|
||||
"dfd-gs", SAMPLE, cache_dir=str(tmp_path), diagram_scale=1, diagram_format="svg",
|
||||
)
|
||||
assert calls["jar"] == 0
|
||||
assert calls["kroki"] == 0
|
||||
assert Path(result.png_path).read_bytes().startswith(b"\x89PNG")
|
||||
assert result.svg_path and Path(result.svg_path).read_text(encoding="utf-8").startswith("<svg")
|
||||
assert result.pixel_scale == 1
|
||||
|
||||
|
||||
def test_render_diagram_bad_source(tmp_path):
|
||||
configure_diagrams(fallback="off", cache_dir=str(tmp_path))
|
||||
with pytest.raises(RuntimeError, match="пустой"):
|
||||
render_diagram("gane-sarson", " ", cache_dir=str(tmp_path))
|
||||
Reference in New Issue
Block a user