"""Сквозная проверка вёрстки: документ должен собираться без дефектов. Отдельные обработчики проверяются модульными тестами, но выход текста за поле проявляется только после сборки: он зависит от шрифта, ширины колонок и взаимодействия пакетов. Этот тест ловит регресс, который иначе видно лишь глазами в готовом 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)