119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from pdf2md.math_render import MathJaxCommandResult, MathJaxRenderChecker
|
|
from pdf2md.quality import MathCheckerUnavailable, MathExpression
|
|
|
|
|
|
def test_mathjax_checker_batches_expressions_as_json(tmp_path: Path) -> None:
|
|
helper = make_helper(tmp_path)
|
|
calls = []
|
|
|
|
def runner(command: tuple[str, ...], stdin: str, timeout_seconds: int) -> MathJaxCommandResult:
|
|
calls.append((command, json.loads(stdin), timeout_seconds))
|
|
return MathJaxCommandResult(
|
|
command,
|
|
0,
|
|
stdout=json.dumps(
|
|
{
|
|
"results": [
|
|
{"index": 0, "ok": True},
|
|
{"index": 1, "ok": False, "message": "Undefined control sequence"},
|
|
]
|
|
}
|
|
),
|
|
)
|
|
|
|
checker = MathJaxRenderChecker(
|
|
helper_path=helper,
|
|
which=lambda executable: "C:/node/node.exe" if executable == "node" else None,
|
|
runner=runner,
|
|
timeout_seconds=7,
|
|
)
|
|
expressions = (
|
|
MathExpression(0, "x_i^2", False, (0, 7)),
|
|
MathExpression(1, "\\bad", True, (9, 18)),
|
|
)
|
|
|
|
results = checker.check_expressions(expressions)
|
|
|
|
assert [result.ok for result in results] == [True, False]
|
|
assert results[1].message == "Undefined control sequence"
|
|
assert calls == [
|
|
(
|
|
("C:/node/node.exe", str(helper)),
|
|
{
|
|
"expressions": [
|
|
{"index": 0, "body": "x_i^2", "display": False},
|
|
{"index": 1, "body": "\\bad", "display": True},
|
|
]
|
|
},
|
|
7,
|
|
)
|
|
]
|
|
|
|
|
|
def test_mathjax_checker_reports_missing_node_as_unavailable(tmp_path: Path) -> None:
|
|
checker = MathJaxRenderChecker(helper_path=make_helper(tmp_path), which=lambda _: None)
|
|
|
|
with pytest.raises(MathCheckerUnavailable, match="Node.js"):
|
|
checker.check_expressions((MathExpression(0, "x", False, (0, 3)),))
|
|
|
|
|
|
def test_mathjax_checker_reports_helper_failure_as_unavailable(tmp_path: Path) -> None:
|
|
helper = make_helper(tmp_path)
|
|
|
|
def runner(command: tuple[str, ...], stdin: str, timeout_seconds: int) -> MathJaxCommandResult:
|
|
return MathJaxCommandResult(command, 124, stderr="MathJax helper timed out")
|
|
|
|
checker = MathJaxRenderChecker(
|
|
helper_path=helper,
|
|
which=lambda _: "node",
|
|
runner=runner,
|
|
)
|
|
|
|
with pytest.raises(MathCheckerUnavailable, match="timed out"):
|
|
checker.check_expressions((MathExpression(0, "x", False, (0, 3)),))
|
|
|
|
|
|
def test_mathjax_checker_reports_invalid_json_as_unavailable(tmp_path: Path) -> None:
|
|
helper = make_helper(tmp_path)
|
|
|
|
def runner(command: tuple[str, ...], stdin: str, timeout_seconds: int) -> MathJaxCommandResult:
|
|
return MathJaxCommandResult(command, 0, stdout="not json")
|
|
|
|
checker = MathJaxRenderChecker(
|
|
helper_path=helper,
|
|
which=lambda _: "node",
|
|
runner=runner,
|
|
)
|
|
|
|
with pytest.raises(MathCheckerUnavailable, match="invalid JSON"):
|
|
checker.check_expressions((MathExpression(0, "x", False, (0, 3)),))
|
|
|
|
|
|
def test_mathjax_checker_rejects_mismatched_result_indexes(tmp_path: Path) -> None:
|
|
helper = make_helper(tmp_path)
|
|
|
|
def runner(command: tuple[str, ...], stdin: str, timeout_seconds: int) -> MathJaxCommandResult:
|
|
return MathJaxCommandResult(command, 0, stdout=json.dumps({"results": [{"index": 99, "ok": True}]}))
|
|
|
|
checker = MathJaxRenderChecker(
|
|
helper_path=helper,
|
|
which=lambda _: "node",
|
|
runner=runner,
|
|
)
|
|
|
|
with pytest.raises(MathCheckerUnavailable, match="indexes"):
|
|
checker.check_expressions((MathExpression(0, "x", False, (0, 3)),))
|
|
|
|
|
|
def make_helper(tmp_path: Path) -> Path:
|
|
helper = tmp_path / "check.mjs"
|
|
helper.write_text("// fake helper", encoding="utf-8")
|
|
return helper
|