v0.5.3
Example document / build (push) Has been cancelled
Python application / build (push) Has been cancelled

- Мелкие исправления увеличивающие работу конвертера
This commit is contained in:
Igor20264
2026-09-10 10:50:15 +03:00
parent 510f7e7adf
commit e6b58efeb3
95 changed files with 7165 additions and 8771 deletions
+223
View File
@@ -16,11 +16,18 @@ from md2gost.diagram_includes import (
)
from md2gost.diagram_renderer import (
DIAGRAM_LANGS,
PLANTUML_CACHE_PREFIX,
PLANTUML_VERSION,
_cache_paths,
_PumlJob,
_plantuml_run,
configure_diagrams,
diagram_engine_status,
is_diagram_lang,
prepare_source,
register_pending_diagram,
render_diagram,
reset_diagram_batch,
resolve_plantuml_jar,
)
from md2gost.diagram_schemes import (
@@ -558,6 +565,222 @@ def test_attach_svg_blip():
assert len(image_rels) >= 2
class _FakeProc:
returncode = 0
stdout = ""
stderr = ""
def _fake_java(*, out_stem: str = "src", fail: bool = False):
"""Stand-in for `java -jar plantuml.jar`: writes what the real jar would."""
calls: list[list[str]] = []
def run(cmd, **kwargs):
calls.append(list(cmd))
if fail:
proc = _FakeProc()
proc.returncode = 1
proc.stderr = "boom"
return proc
fmt = next(a[2:] for a in cmd if a.startswith("-t"))
payload = (
b"\x89PNG\r\n\x1a\n" + b"\x00" * 32 if fmt == "png"
else b"<svg xmlns='http://www.w3.org/2000/svg'/>"
)
for arg in cmd:
if not str(arg).endswith(".puml"):
continue
out_dir = Path(arg).parent / "out"
out_dir.mkdir(parents=True, exist_ok=True)
(out_dir / f"{out_stem}.{fmt}").write_bytes(payload)
return _FakeProc()
return run, calls
def _dummy_jar(tmp_path: Path) -> Path:
jar = tmp_path / "plantuml.jar"
jar.write_bytes(b"PK" + b"\x00" * 2000)
return jar
def test_plantuml_run_batches_sources_into_one_jvm(tmp_path):
jar = _dummy_jar(tmp_path)
jobs = [_PumlJob(f"@startuml\nA{i} -> B{i}\n@enduml", tmp_path / f"{i}.png")
for i in range(3)]
run, calls = _fake_java()
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
done = _plantuml_run(jobs, str(jar), "png")
assert done == [True, True, True]
assert len(calls) == 1
assert sum(1 for a in calls[0] if str(a).endswith(".puml")) == 3
for job in jobs:
assert job.out_path.read_bytes().startswith(b"\x89PNG")
def test_plantuml_run_keeps_named_diagrams_apart(tmp_path):
"""`@startuml Name` renames the output file — each source needs its own -o dir."""
jar = _dummy_jar(tmp_path)
jobs = [_PumlJob("@startuml Name\nA -> B\n@enduml", tmp_path / f"{i}.png")
for i in range(2)]
run, calls = _fake_java(out_stem="Name")
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
done = _plantuml_run(jobs, str(jar), "png")
assert done == [True, True]
assert calls[0].count("-o") == 1
src_dirs = {Path(a).parent for a in calls[0] if str(a).endswith(".puml")}
assert len(src_dirs) == 2
assert all(job.out_path.is_file() for job in jobs)
def test_plantuml_run_failure_reports_every_job(tmp_path):
jar = _dummy_jar(tmp_path)
jobs = [_PumlJob("@startuml\nA -> B\n@enduml", tmp_path / f"{i}.png")
for i in range(2)]
run, _calls = _fake_java(fail=True)
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
done = _plantuml_run(jobs, str(jar), "png")
assert done == [False, False]
assert not any(job.out_path.exists() for job in jobs)
def test_plantuml_run_without_java_never_spawns(tmp_path):
jar = _dummy_jar(tmp_path)
run, calls = _fake_java()
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=False):
done = _plantuml_run([_PumlJob("@startuml\nA -> B\n@enduml", tmp_path / "a.png")],
str(jar), "png")
assert done == [False]
assert calls == []
def test_batch_prepass_draws_whole_document_in_one_jvm(tmp_path):
jar = _dummy_jar(tmp_path)
configure_diagrams(
fallback="off", cache_dir=str(tmp_path), plantuml_jar=str(jar), diagram_scale=1,
)
register_pending_diagram("uml", "A -> B")
register_pending_diagram("uml", "C -> D")
run, calls = _fake_java()
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
first = render_diagram("uml", "A -> B")
second = render_diagram("uml", "C -> D")
assert len(calls) == 1
assert first.png_path != second.png_path
for result in (first, second):
assert Path(result.png_path).read_bytes().startswith(b"\x89PNG")
def test_batch_prepass_svg_mode_uses_two_jvms_not_four(tmp_path):
jar = _dummy_jar(tmp_path)
configure_diagrams(
fallback="off", cache_dir=str(tmp_path), plantuml_jar=str(jar),
diagram_scale=1, diagram_format="svg",
)
register_pending_diagram("uml", "A -> B")
register_pending_diagram("uml", "C -> D")
run, calls = _fake_java()
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
first = render_diagram("uml", "A -> B")
second = render_diagram("uml", "C -> D")
assert sorted(next(a for a in c if a.startswith("-t")) for c in calls) == \
["-tpng", "-tsvg"]
assert first.svg_path and second.svg_path
assert Path(first.svg_path).read_bytes().startswith(b"<svg")
def test_batch_prepass_skips_cached_diagrams(tmp_path):
jar = _dummy_jar(tmp_path)
configure_diagrams(
fallback="off", cache_dir=str(tmp_path), plantuml_jar=str(jar), diagram_scale=1,
)
cached_src, _ = prepare_source("uml", "A -> B")
cached_png, _ = _cache_paths(
cached_src, str(tmp_path), cache_prefix=PLANTUML_CACHE_PREFIX,
)
cached_png.write_bytes(b"\x89PNG\r\n\x1a\n" + b"cached")
register_pending_diagram("uml", "A -> B")
register_pending_diagram("uml", "C -> D")
run, calls = _fake_java()
with patch("md2gost.diagram_renderer.subprocess.run", side_effect=run), \
patch("md2gost.diagram_renderer._java_available", return_value=True):
hit = render_diagram("uml", "A -> B")
render_diagram("uml", "C -> D")
assert len(calls) == 1
assert sum(1 for a in calls[0] if str(a).endswith(".puml")) == 1
assert Path(hit.png_path).read_bytes().endswith(b"cached")
def test_batch_prepass_not_triggered_for_explicit_overrides(tmp_path):
configure_diagrams(fallback="remote", cache_dir=str(tmp_path))
register_pending_diagram("uml", "A -> B")
def fake_kroki(source, diagram_type, base_url, out_path, fmt="png"):
out_path.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 20)
return True
with patch("md2gost.diagram_renderer._flush_plantuml_batch") as flush, \
patch("md2gost.diagram_renderer._render_plantuml_jar", return_value=False), \
patch("md2gost.diagram_renderer._render_kroki", side_effect=fake_kroki):
render_diagram("uml", "A -> B", cache_dir=str(tmp_path))
flush.assert_not_called()
reset_diagram_batch()
def test_diagram_figure_registers_for_batch():
from docx import Document
from md2gost import package_dir
import md2gost.diagram_renderer as dr
import os
reset_diagram_batch()
doc = Document(os.path.join(package_dir(), "Template.docx"))
DiagramFigure(doc._body, "uml", "A -> B")
DiagramFigure(doc._body, "uml", "A -> B")
DiagramFigure(doc._body, "mermaid", "flowchart LR\nA-->B")
assert dr._PENDING == [("uml", "A -> B"), ("mermaid", "flowchart LR\nA-->B")]
reset_diagram_batch()
assert dr._PENDING == []
def test_plantuml_cache_key_carries_jar_version(tmp_path):
assert PLANTUML_VERSION in PLANTUML_CACHE_PREFIX
prepared, dtype = prepare_source("uml", "A -> B")
assert dtype == "plantuml"
versioned, _ = _cache_paths(
prepared, str(tmp_path), cache_prefix=PLANTUML_CACHE_PREFIX,
)
legacy, _ = _cache_paths(prepared, str(tmp_path))
assert versioned != legacy
def test_stale_unversioned_cache_is_a_miss_not_a_crash(tmp_path):
"""Files cached before the jar version entered the key must be re-rendered."""
configure_diagrams(fallback="remote", cache_dir=str(tmp_path), diagram_scale=1)
prepared, _ = prepare_source("uml", "A -> B")
legacy, _ = _cache_paths(prepared, str(tmp_path))
legacy.write_bytes(b"\x89PNG\r\n\x1a\n" + b"stale")
def fake_kroki(source, diagram_type, base_url, out_path, fmt="png"):
out_path.write_bytes(b"\x89PNG\r\n\x1a\n" + b"fresh")
return True
with patch("md2gost.diagram_renderer._render_plantuml_jar", return_value=False), \
patch("md2gost.diagram_renderer._render_kroki", side_effect=fake_kroki):
result = render_diagram(
"uml", "A -> B", cache_dir=str(tmp_path), diagram_scale=1,
)
assert Path(result.png_path) != legacy
assert Path(result.png_path).read_bytes().endswith(b"fresh")
assert legacy.read_bytes().endswith(b"stale")
def test_resolve_plantuml_jar_explicit(tmp_path):
jar = tmp_path / "plantuml.jar"
jar.write_bytes(b"PK" + b"\x00" * 2000)