|
| 1 | +"""Regression: the HTML reader must not collapse bold-lead-in lists into a paragraph. |
| 2 | +
|
| 3 | +The book style writes a bold label immediately followed by a list, with no blank |
| 4 | +line between them: |
| 5 | +
|
| 6 | + **触发分离的条件**: |
| 7 | + - 输入序列长度 > 某阈值 |
| 8 | +
|
| 9 | +Strict CommonMark (pandoc's default `-f markdown`) requires a blank line before a |
| 10 | +list, so it folds all three bullets into one <p>. build_html_reader.py therefore |
| 11 | +must pass `markdown+lists_without_preceding_blankline`. This test feeds that exact |
| 12 | +pattern through the *real* pandoc invocation the reader uses and asserts the list |
| 13 | +survives — the check that would have caught the 640 collapsed lists that shipped. |
| 14 | +""" |
| 15 | + |
| 16 | +import re |
| 17 | +import shutil |
| 18 | +import subprocess |
| 19 | +import unittest |
| 20 | +from pathlib import Path |
| 21 | + |
| 22 | +ROOT = Path(__file__).resolve().parents[1] |
| 23 | +READER = ROOT / "tools" / "build_html_reader.py" |
| 24 | + |
| 25 | +# The pattern the book uses everywhere: bold lead-in, then a list, no blank line. |
| 26 | +SAMPLE = "**触发分离的条件**:\n- 输入序列长度 > 某阈值\n- 输入序列未命中前缀缓存\n" |
| 27 | + |
| 28 | + |
| 29 | +def _reader_pandoc_format() -> str: |
| 30 | + """Extract the pandoc `-f` value the reader uses, across every fork shape. |
| 31 | +
|
| 32 | + Forks differ: an inline "-f", "markdown..." literal, a `reader = "markdown..."` |
| 33 | + variable, a `context="markdown..."` kwarg, or a PANDOC_MARKDOWN_READER constant |
| 34 | + living in tools/publication_sources.py (possibly as an implicitly-joined |
| 35 | + multiline string). Search both files and stitch adjacent string fragments. |
| 36 | + """ |
| 37 | + texts = [READER.read_text(encoding="utf-8")] |
| 38 | + sources = READER.parent / "publication_sources.py" |
| 39 | + if sources.is_file(): |
| 40 | + texts.append(sources.read_text(encoding="utf-8")) |
| 41 | + for src in texts: |
| 42 | + for pat in ( |
| 43 | + r'"-f",\s*"(markdown[^"]*)"', |
| 44 | + r'reader\s*=\s*"(markdown[^"]*)"', |
| 45 | + r'context="(markdown[^"]*)"', |
| 46 | + # constant, one or more adjacent "..." fragments (implicit join) |
| 47 | + r'PANDOC_MARKDOWN_READER\s*=\s*\(?\s*((?:"[^"]*"\s*)+)\)?', |
| 48 | + ): |
| 49 | + m = re.search(pat, src) |
| 50 | + if m: |
| 51 | + raw = m.group(1) |
| 52 | + # collapse implicit string concatenation into one value |
| 53 | + return "".join(re.findall(r'"([^"]*)"', raw)) or raw |
| 54 | + raise AssertionError("could not find the reader's pandoc -f markdown format") |
| 55 | + |
| 56 | + |
| 57 | +class HtmlListRenderingTests(unittest.TestCase): |
| 58 | + def test_reader_declares_the_no_blank_line_list_extension(self): |
| 59 | + fmt = _reader_pandoc_format() |
| 60 | + self.assertIn( |
| 61 | + "lists_without_preceding_blankline", |
| 62 | + fmt, |
| 63 | + "build_html_reader.py must enable lists_without_preceding_blankline; " |
| 64 | + f"pandoc format is currently {fmt!r}. Without it, every bold-lead-in " |
| 65 | + "list in the book collapses into a paragraph in the shipped HTML.", |
| 66 | + ) |
| 67 | + |
| 68 | + def test_bold_lead_in_list_renders_as_ul_under_real_pandoc(self): |
| 69 | + pandoc = shutil.which("pandoc") |
| 70 | + if pandoc is None: |
| 71 | + self.skipTest("pandoc not installed; CI installs it and runs this check") |
| 72 | + fmt = _reader_pandoc_format() |
| 73 | + out = subprocess.run( |
| 74 | + [pandoc, "-f", fmt, "-t", "html5"], |
| 75 | + input=SAMPLE, |
| 76 | + capture_output=True, |
| 77 | + text=True, |
| 78 | + check=True, |
| 79 | + ).stdout |
| 80 | + self.assertIn("<ul>", out, f"list collapsed; pandoc -f {fmt} produced:\n{out}") |
| 81 | + self.assertEqual(out.count("<li>"), 2, f"expected 2 <li>; got:\n{out}") |
| 82 | + |
| 83 | + |
| 84 | +if __name__ == "__main__": |
| 85 | + unittest.main() |
0 commit comments