Spaces:
Running
Running
| from pathlib import Path | |
| from qalmsw.document import Document | |
| from qalmsw.parse import resolve_includes | |
| def _write(path: Path, content: str) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| path.write_text(content, encoding="utf-8") | |
| def test_no_includes_is_passthrough(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| body = "\\begin{document}\nHello world.\n\\end{document}\n" | |
| _write(main, body) | |
| source, line_map = resolve_includes(main) | |
| assert source == body | |
| assert len(line_map) == body.count("\n") | |
| assert all(entry.file == main for entry in line_map) | |
| assert [e.line for e in line_map] == list(range(1, len(line_map) + 1)) | |
| def test_input_inlined(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| intro = tmp_path / "sections" / "intro.tex" | |
| _write(intro, "Intro line one.\nIntro line two.\n") | |
| _write(main, "\\begin{document}\n\\input{sections/intro}\n\\end{document}\n") | |
| source, line_map = resolve_includes(main) | |
| assert "Intro line one." in source | |
| assert "Intro line two." in source | |
| # The line containing "Intro line one." should map back to intro.tex:1. | |
| lines = source.split("\n") | |
| idx = lines.index("Intro line one.") | |
| assert line_map[idx].file == intro | |
| assert line_map[idx].line == 1 | |
| idx2 = lines.index("Intro line two.") | |
| assert line_map[idx2].file == intro | |
| assert line_map[idx2].line == 2 | |
| def test_include_adds_tex_suffix(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| sub = tmp_path / "part.tex" | |
| _write(sub, "Sub content.\n") | |
| _write(main, "\\input{part}\n") | |
| source, line_map = resolve_includes(main) | |
| assert "Sub content." in source | |
| lines = source.split("\n") | |
| idx = lines.index("Sub content.") | |
| assert line_map[idx].file == sub | |
| def test_missing_include_is_tolerated(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| _write(main, "Before\n\\input{does_not_exist}\nAfter\n") | |
| source, line_map = resolve_includes(main) | |
| assert "Before" in source | |
| assert "After" in source | |
| assert "qalmsw" in source # placeholder comment emitted | |
| def test_cycle_detected(tmp_path: Path): | |
| a = tmp_path / "a.tex" | |
| b = tmp_path / "b.tex" | |
| _write(a, "A\n\\input{b}\n") | |
| _write(b, "B\n\\input{a}\n") | |
| source, _ = resolve_includes(a) | |
| # Should not infinite-loop; cycle back to a is refused. | |
| assert source.count("cycle detected") >= 1 | |
| def test_nested_includes(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| mid = tmp_path / "mid.tex" | |
| leaf = tmp_path / "leaf.tex" | |
| _write(leaf, "Leaf content.\n") | |
| _write(mid, "Mid content.\n\\input{leaf}\n") | |
| _write(main, "\\input{mid}\n") | |
| source, line_map = resolve_includes(main) | |
| assert "Mid content." in source | |
| assert "Leaf content." in source | |
| lines = source.split("\n") | |
| assert line_map[lines.index("Leaf content.")].file == leaf | |
| assert line_map[lines.index("Mid content.")].file == mid | |
| def test_input_in_comment_not_followed(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| _write(main, "Body\n% \\input{ignored}\n") | |
| source, _ = resolve_includes(main) | |
| # The commented line is kept verbatim (we preserve it as-is); we just don't follow it. | |
| assert "ignored" in source # text preserved | |
| # But no separate file was created, so no expansion occurred; line count matches input. | |
| assert source.count("\n") == 2 | |
| def test_document_load_attributes_paragraphs_to_origin(tmp_path: Path): | |
| main = tmp_path / "main.tex" | |
| intro = tmp_path / "intro.tex" | |
| _write(intro, "Paragraph from intro with enough words.\n") | |
| _write( | |
| main, | |
| "\\begin{document}\n" | |
| "\\input{intro}\n" | |
| "\n" | |
| "Paragraph in main.\n" | |
| "\\end{document}\n", | |
| ) | |
| doc = Document.load(main) | |
| texts = [p.text for p in doc.paragraphs] | |
| assert "Paragraph from intro with enough words." in texts | |
| assert "Paragraph in main." in texts | |
| from_intro = next(p for p in doc.paragraphs if "from intro" in p.text) | |
| from_main = next(p for p in doc.paragraphs if "in main" in p.text) | |
| assert from_intro.file == intro | |
| assert from_intro.start_line == 1 | |
| assert from_main.file == main | |