diff --git a/tests/test_layout.py b/tests/test_layout.py new file mode 100644 index 0000000..a543258 --- /dev/null +++ b/tests/test_layout.py @@ -0,0 +1,122 @@ +"""Сквозная проверка вёрстки: документ должен собираться без дефектов. + +Отдельные обработчики проверяются модульными тестами, но выход текста за поле +проявляется только после сборки: он зависит от шрифта, ширины колонок и +взаимодействия пакетов. Этот тест ловит регресс, который иначе видно лишь +глазами в готовом PDF. +""" + +from __future__ import annotations + +import shutil +import subprocess +import tempfile +from pathlib import Path + +import pytest + +import md2gost +from md2gost.latex.log import parse_log +from md2gost.parser import Parser +from md2gost.render import Renderer + +pytestmark = pytest.mark.skipif( + shutil.which("xelatex") is None, reason="xelatex не установлен" +) + +# Документ собран из конструкций, на которых вёрстка ломалась: +# длинный токен в узкой колонке, подглава с двузначным номером в оглавлении, +# перечень сокращений, листинг с подписью и длинными строками. +FRONT_MATTER = """--- +титульник: ptp-report +факультет: Информатика и системы управления (ИУ) +кафедра: Теоретическая информатика и компьютерные технологии (ИУ9) +тип: Отчёт +название: Проверка вёрстки +студент: Иванов И. И. +студент_полностью: Иванов Иван Иванович +группа: ИУ9-22Б +тип_практики: проектно-технологическая практика +предприятие: МГТУ имени Н. Э. Баумана +команда: Проверка вёрстки +хранилище: https://example.invalid/very/long/repository/path +руководитель: Петров П. П. +год: 2026 +--- +""" + +SUBSECTIONS = "\n".join( + f"## Подглава номер {i}\n\nТекст подглавы номер {i}.\n" for i in range(1, 12) +) + +BODY = f""" +# АННОТАЦИЯ + +Проверка вёрстки. + +# СОДЕРЖАНИЕ + +# ПЕРЕЧЕНЬ ПРИНЯТЫХ СОКРАЩЕНИЙ + +SDR — Software-Defined Radio — программно-определяемая радиосистема +PSO — Particle Swarm Optimisation — метод роевой оптимизации + +# ВВЕДЕНИЕ + +Введение. + +# Раздел + +{SUBSECTIONS} + +Таблица 1 —— Таблица с длинным токеном + +| Направление | Формат | Назначение | +| :--- | :--- | :--- | +| сервер | HackRFTestingServer:идентификатор | Приветствие, подтверждает подключение | + +Листинг 1 —— Листинг с длинными строками + +```cpp +template +std::vector drain_with_timeout(const std::atomic& running, + const std::chrono::duration& timeout); +``` + +# ЗАКЛЮЧЕНИЕ + +Заключение. + +# СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ + +1. Гилев, И. В. Использование технологии SDR / И. В. Гилев. – Воронеж, 2025. – С. 324–328. +""" + + +def _build(md_text: str) -> str: + """Собрать документ и вернуть журнал xelatex.""" + parser = Parser(md_text) + # Картинок в эталонном документе нет — копировать нужно только эмблему. + latex = Renderer(parser.tokens, parser.front_matter).render() + emblem = Path(md2gost.__file__).parent.parent / "emblem.png" + + with tempfile.TemporaryDirectory(prefix="md2gost-layout-") as cache: + cache_dir = Path(cache) + tex = cache_dir / "doc.tex" + tex.write_text(latex, encoding="utf-8") + if emblem.is_file(): + shutil.copy2(emblem, cache_dir / "emblem.png") + for _ in range(2): + subprocess.run( + ["xelatex", "-interaction=nonstopmode", str(tex)], + cwd=cache_dir, + capture_output=True, + ) + assert (cache_dir / "doc.pdf").is_file(), "xelatex не собрал PDF" + return (cache_dir / "doc.log").read_text(errors="replace") + + +def test_reference_document_has_no_layout_defects() -> None: + diags = parse_log(_build(FRONT_MATTER + BODY)) + defects = [d for d in diags if d.kind in ("error", "overfull")] + assert not defects, "\n".join(f"{d.kind}: {d.message}" for d in defects)