Files
md_to_gost/tests/test_mirea_tz.py
T
Igor20264 516abe7b83
Python application / build (push) Has been cancelled
BigUpdate
2026-09-03 10:44:08 +03:00

381 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unit tests for MIREA TZ checker and numbering helpers."""
from md2gost.checker import check_markdown
from md2gost.numberer import Numberer
from md2gost.profiles import preprocess_markdown, find_formula_refs
from md2gost.bibliography import find_citations, extract_year
SAMPLE_OK = """
# *СОДЕРЖАНИЕ
[TOC]
# *ВВЕДЕНИЕ
Текст введения без ссылок на источники.
# Анализ предметной области
Согласно исследованиям [1] применяется подход, показанный на @Рисунок:arch.
![схема](img.png "%arch Архитектура системы")
%tbl1 Сравнение подходов
| Подход | Оценка |
|--------|--------|
| A | 1 |
| B | 2 |
Далее см. Таблицу — @Таблица:tbl1.
# *ЗАКЛЮЧЕНИЕ
Выводы по работе.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: Иванов И. И. Информационные системы. — М.: Наука, 2023. — 120 с.
[2]: Петров П. П. Базы данных. — СПб.: Питер, 2022. — 200 с.
[3]: ГОСТ 7.32-2017. Отчет о НИР. — М., 2022.
[4]: Сидоров С. С. Python. — М.: ДМК, 2024. — 300 с.
[5]: Козлов К. К. Анализ данных. — М.: Бином, 2022. — 150 с.
# *ПРИЛОЖЕНИЯ
## Приложение А Листинг модуля
Код.
"""
def test_numberer_section_scoped():
n = Numberer()
n.enter_section(1)
assert n.next_number("Рисунок", "a") == "1.1"
assert n.next_number("Рисунок", "b") == "1.2"
n.enter_section(2)
assert n.next_number("Рисунок") == "2.1"
n.enter_appendix("Б")
assert n.next_number("Рисунок", "x") == "Б.1"
def test_numberer_continuous_pis():
n = Numberer(mode="continuous")
n.enter_section(1)
assert n.next_number("Рисунок", "a") == "1"
assert n.next_number("Таблица", "t") == "1"
n.enter_section(2)
assert n.next_number("Рисунок", "b") == "2"
n.enter_appendix("А")
assert n.next_number("Рисунок", "c") == "3"
SAMPLE_PIS = """
# *СОДЕРЖАНИЕ
[TOC]
# Практическая работа №1. Формирование требований к системе
Цель работы — сформировать требования.
## Описание предметной области
Текст с рисунком @Рисунок:arch.
![схема](img.png "%arch Архитектура")
# Практическая работа №2. Моделирование процессов
## Построение модели
%tbl1 Сравнение подходов
| A | B |
|---|---|
| 1 | 2 |
См. @Таблица:tbl1.
"""
def test_checker_ok_pis_custom():
issues = check_markdown(SAMPLE_PIS, "PIS_custom")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_pis_requires_practical():
text = SAMPLE_PIS.replace("Практическая работа №1.", "Раздел 1.")
text = text.replace("Практическая работа №2.", "Раздел 2.")
issues = check_markdown(text, "PIS_custom")
assert any(i.id == "structure.practical" for i in issues)
def test_pis_styles_h1_centered():
import docx
from md2gost.styles import apply_pis_custom_styles
from docx.enum.text import WD_ALIGN_PARAGRAPH
doc = docx.Document(r"md2gost/Template.docx")
apply_pis_custom_styles(doc)
h1 = doc.styles["Heading 1"]
assert h1.paragraph_format.alignment == WD_ALIGN_PARAGRAPH.CENTER
assert h1.font.all_caps is True
h2 = doc.styles["Heading 2"]
assert h2.paragraph_format.first_line_indent is not None
assert abs(h2.paragraph_format.first_line_indent.cm - 1.25) < 0.01
assert h2.paragraph_format.left_indent is None or h2.paragraph_format.left_indent.cm == 0
def test_soderzhanie_excluded_from_toc_outline():
import docx
from md2gost.renderable.heading import Heading
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r"md2gost/Template.docx")
doc._body.clear_content()
apply_mirea_styles(doc)
h = Heading(doc._body, 1, False)
h.add_run("СОДЕРЖАНИЕ")
h.exclude_from_toc()
levels = h._docx_paragraph._p.pPr.xpath("./w:outlineLvl")
assert levels and levels[0].get(
"{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val"
) == "9"
def test_heading_font_is_tnr_black():
import docx
from docx.oxml.ns import qn
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r"md2gost/Template.docx")
apply_mirea_styles(doc)
rPr = doc.styles["Heading 1"].element.find(qn("w:rPr"))
rFonts = rPr.find(qn("w:rFonts"))
assert rFonts.get(qn("w:ascii")) == "Times New Roman"
assert rFonts.get(qn("w:hAnsi")) == "Times New Roman"
assert rFonts.get(qn("w:eastAsia")) == "Times New Roman"
color = rPr.find(qn("w:color"))
assert color is not None
assert color.get(qn("w:val")) == "000000"
assert color.get(qn("w:themeColor")) is None
def test_checker_ok_coursework():
issues = check_markdown(SAMPLE_OK, "coursework")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_ris_forbidden():
text = SAMPLE_OK.replace("@Рисунок:arch", "рис. 1.1")
issues = check_markdown(text, "coursework")
assert any(i.id == "ref.ris" for i in issues)
def test_checker_cite_in_intro():
text = SAMPLE_OK.replace("Текст введения без ссылок на источники.", "См. [1].")
issues = check_markdown(text, "coursework")
assert any(i.id == "cite.intro" for i in issues)
def test_checker_vkr_graphic():
issues = check_markdown(SAMPLE_OK, "vkr")
assert any(i.id == "structure.graphic" for i in issues)
def test_preprocess_quotes():
assert "«тест»" in preprocess_markdown('Он сказал "тест" вслух.')
def test_formula_refs():
assert find_formula_refs("см. @Формула:eq1 и далее") == {"eq1"}
def test_citations_order():
assert find_citations("a [2] b [1] c [2, 3]") == ["2", "1", "3"]
def test_extract_year():
assert extract_year("М.: Наука, 2023. — 120 с.") == 2023
def test_biblio_entries_from_concatenated_paragraph():
from md2gost.biblio_processor import entries_from_text, extract_bibliography_from_markdown
entries = entries_from_text(
"[1]: Первый источник. — М., 2024.[2]: Второй. URL: https://a.com/x-y/[3]: Третий."
)
assert [e.key for e in entries] == ["1", "2", "3"]
assert "https://a.com/x-y/" in entries[1].text
md = """# *ВВЕДЕНИЕ
Текст.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: Ааа. — М., 2024.
[2]: Ббб. — СПб., 2023.
# *ПРИЛОЖЕНИЯ
"""
flat = extract_bibliography_from_markdown(md)
assert len(flat) == 2
assert flat[0].key == "1"
def test_emdash_to_hyphen_preprocess():
from md2gost.profiles import preprocess_markdown, set_emdash_to_hyphen, dash_separator
out = preprocess_markdown("Цель — тест. Слово - слово.", emdash_to_hyphen=True)
assert "" not in out
assert "Цель - тест" in out
assert dash_separator() == " - "
out2 = preprocess_markdown("Цель — тест.", emdash_to_hyphen=False)
assert "" in out2
assert dash_separator() == ""
set_emdash_to_hyphen(False)
def test_native_toc_inserts_word_field():
import docx
from md2gost.renderable.toc import ToC
doc = docx.Document(r"md2gost/Template.docx")
doc._body.clear_content()
toc = ToC(doc._body, toc_mode="native")
toc.fill()
xml = toc._paragraph._docx_paragraph._p.xml
assert "TOC" in xml
assert "fldChar" in xml
def test_reference_ignores_trailing_punctuation():
from md2gost.extended_markdown import markdown
def refs(text):
doc = markdown.parse(text)
out = []
def walk(node):
if node.__class__.__name__ == "Reference":
out.append((node.type, node.name))
kids = getattr(node, "children", None)
if isinstance(kids, list):
for c in kids:
walk(c)
walk(doc)
return out
assert ("Рисунок", "submitted") in refs("@Рисунок:submitted, @Рисунок:lmsafter")
assert ("Рисунок", "deploy") in refs("см. (@Рисунок:deploy). Далее")
def test_heading_manual_kills_word_numbering():
import docx
from md2gost.renderable.heading import Heading
doc = docx.Document(r'md2gost/Template.docx')
doc._body.clear_content()
h = Heading(doc._body, 1, True, numbering_mode='manual')
h.add_run('1 Разработка')
h.apply_numbering_mode('manual')
assert h.text.startswith('1 ')
num_pr = h._docx_paragraph._p.pPr.xpath('.//w:numId')
assert num_pr and num_pr[0].get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val') == '0'
def test_heading_auto_strips_leading_number():
import docx
from md2gost.renderable.heading import Heading
doc = docx.Document(r'md2gost/Template.docx')
doc._body.clear_content()
h = Heading(doc._body, 2, True, numbering_mode='auto')
h.add_run('1.1 Описание')
h.apply_numbering_mode('auto')
assert h.text == 'Описание'
def test_toc_tab_stops_match_text_area():
import docx
from md2gost.styles import apply_mirea_styles
doc = docx.Document(r'md2gost/Template.docx')
apply_mirea_styles(doc)
section = doc.sections[0]
usable = section.page_width.mm - section.left_margin.mm - section.right_margin.mm
for name in ('toc 1', 'toc 2'):
tabs = list(doc.styles[name].paragraph_format.tab_stops)
right_tabs = [t for t in tabs if 'RIGHT' in str(t.alignment)]
assert len(right_tabs) == 1, (name, tabs)
assert abs(right_tabs[0].position.mm - usable) < 0.05, (name, right_tabs[0].position.mm, usable)
edge = section.left_margin.mm + right_tabs[0].position.mm
assert abs(edge - (section.page_width.mm - section.right_margin.mm)) < 0.05
SAMPLE_APID = """
# *СОДЕРЖАНИЕ
[TOC]
# *ВВЕДЕНИЕ
Текст введения без ссылок.
# 1 Теоретические аспекты разработки архитектуры приложений и данных: описание микросервисной архитектуры
Согласно [1] архитектура задаёт структуру системы. Далее [2].
# 2 Прикладные аспекты разработки архитектуры приложений и данных web-сервиса «SMEZHNO»
## 2.1 Описание проекта команды и разрабатываемого программного приложения «SMEZHNO»
Текст проекта [3].
## 2.2 Описание роли «Неавторизованный пользователь» web-сервиса «SMEZHNO»
Роль.
## 2.3 Описание архитектуры программного приложения и данных web-сервиса «SMEZHNO» для роли «Неавторизованный пользователь»
Архитектура [4].
## 2.4 Варианты развития архитектуры программного приложения «SMEZHNO»
Альтернативы [5].
# *ЗАКЛЮЧЕНИЕ
Выводы.
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1]: ISO/IEC/IEEE 42010:2022. Architecture description. — Geneva: ISO, 2022.
[2]: The Open Group. ArchiMate 3.2 Specification. — 2023.
[3]: Аншина М. Л. Материалы лекционного курса. — М.: РТУ МИРЭА, 2024.
[4]: Ньюмен С. Создание микросервисов. — СПб.: Питер, 2023. — 624 с.
[5]: The Open Group. The TOGAF Standard, 10th Edition. — 2022.
[6]: PostgreSQL 16 Documentation. URL: https://www.postgresql.org/docs/16/ (дата обращения: 01.05.2025).
[7]: Kubernetes Documentation. URL: https://kubernetes.io/docs/home/ (дата обращения: 01.05.2025).
"""
def test_checker_apid_ok():
issues = check_markdown(SAMPLE_APID, "APID_coursework")
errors = [i for i in issues if i.severity == "error"]
assert not errors, errors
def test_checker_apid_requires_sections():
text = SAMPLE_APID.replace("## 2.4 Варианты развития", "## 2.4 Прочее")
issues = check_markdown(text, "APID_coursework")
assert any(i.id == "structure.apid" for i in issues)
def test_apid_biblio_min_seven():
text = SAMPLE_APID
# drop last three sources -> 4 left
text = text.split("[5]:")[0] + "\n"
issues = check_markdown(text, "APID_coursework")
assert any(i.id == "biblio.count" for i in issues)