Skip to content

Commit b8f69f2

Browse files
authored
Implement tests for tei2json example processing
Add tests for tei2json processing against example fixtures, including JSON and HTML outputs.
1 parent d3dc9c9 commit b8f69f2

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

‎tests/test_tei2json_examples.py‎

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# tests/test_examples_against_fixtures.py
2+
import json
3+
import subprocess
4+
import os
5+
from pathlib import Path
6+
import difflib
7+
import pytest
8+
from lxml import etree, html as lxml_html
9+
10+
# adjust import path to your tei2json module location
11+
import sys
12+
repo_root = Path(__file__).resolve().parents[1]
13+
sys.path.insert(0, str(repo_root / ))
14+
15+
import tei2json # must expose process_file(Path) -> dict
16+
17+
# where to find examples and expected outputs (adjust if your layout differs)
18+
EXAMPLES_DIR = Path("exampleData/xml")
19+
EXPECTED_JSON_DIR = Path("exampleData/json")
20+
EXPECTED_HTML_DIR = Path("exampleData/ms")
21+
22+
# optional Saxon jar; tests that need Saxon will skip if not present
23+
SAXON_JAR = os.environ.get("SAXON_JAR") or "saxon.jar"
24+
25+
def normalize_json(obj):
26+
"""Return a canonical JSON string for comparison: sorted keys, no extra whitespace."""
27+
return json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
28+
29+
def normalize_html_string(s: str) -> str:
30+
"""Parse HTML-ish string and return a normalized serialization for stable comparison."""
31+
# parse as HTML fragment (lxml.html) to tolerate non-well-formedness
32+
try:
33+
doc = lxml_html.fromstring(s)
34+
# tostring with method='html' to preserve typical HTML serialization
35+
return lxml_html.tostring(doc, encoding="unicode", method="html").strip()
36+
except Exception:
37+
# fallback: collapse whitespace and strip
38+
return " ".join(s.split())
39+
40+
def run_saxon_transform(input_xml: Path, xsl_path: Path, out_path: Path):
41+
"""Run Saxon jar to transform input_xml with xsl_path -> out_path.
42+
Raises subprocess.CalledProcessError on failure.
43+
"""
44+
jar = Path(SAXON_JAR)
45+
if not jar.exists():
46+
raise FileNotFoundError(f"Saxon JAR not found at {jar}; set SAXON_JAR env or put saxon.jar at repo root.")
47+
cmd = [
48+
"java", "-jar", str(jar),
49+
"-s:" + str(input_xml),
50+
"-xsl:" + str(xsl_path),
51+
"-o:" + str(out_path)
52+
]
53+
subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
54+
55+
def all_example_xmls():
56+
"""Yield Path objects for all XML example files under EXAMPLES_DIR (recursive)."""
57+
if not EXAMPLES_DIR.exists():
58+
return []
59+
return sorted(EXAMPLES_DIR.rglob("*.xml"))
60+
61+
@pytest.mark.parametrize("xml_path", all_example_xmls())
62+
def test_example_json_matches_fixture(xml_path, tmp_path):
63+
"""
64+
For each example XML:
65+
- process with tei2json.process_file
66+
- compare produced JSON to exampleData/json/<stem>.json
67+
"""
68+
# process xml into dict
69+
data = tei2json.process_file(xml_path) # should return a dict
70+
produced = normalize_json(data)
71+
72+
expected_file = EXPECTED_JSON_DIR / (xml_path.stem + ".json")
73+
if not expected_file.exists():
74+
pytest.skip(f"Expected JSON fixture not found for {xml_path.stem}: {expected_file}")
75+
76+
expected_obj = json.loads(expected_file.read_text(encoding="utf8"))
77+
expected = normalize_json(expected_obj)
78+
79+
if produced != expected:
80+
# produce a readable diff (pretty-printed)
81+
pretty_prod = json.dumps(json.loads(produced), ensure_ascii=False, indent=2, sort_keys=True)
82+
pretty_exp = json.dumps(json.loads(expected), ensure_ascii=False, indent=2, sort_keys=True)
83+
diff = "\n".join(difflib.unified_diff(pretty_exp.splitlines(), pretty_prod.splitlines(),
84+
fromfile="expected", tofile="produced", lineterm=""))
85+
pytest.fail(f"JSON mismatch for {xml_path}:\n{diff}")
86+
87+
@pytest.mark.parametrize("xml_path", all_example_xmls())
88+
def test_example_html_matches_fixture(xml_path, tmp_path):
89+
"""
90+
For each example XML:
91+
- if expected HTML fixture exists, attempt to run XSLT (siteGenerator/xsl/tei2html.xsl)
92+
using Saxon (SAXON_JAR env or saxon.jar at repo root).
93+
- compare normalized HTML serialization to exampleData/ms/<stem>.html
94+
"""
95+
expected_html_file = EXPECTED_HTML_DIR / (xml_path.stem + ".html")
96+
if not expected_html_file.exists():
97+
pytest.skip(f"Expected HTML fixture not found for {xml_path.stem}: {expected_html_file}")
98+
99+
# find the XSL file (adjust this relative path if needed)
100+
xsl_path = Path("siteGenerator/xsl/tei2html.xsl")
101+
if not xsl_path.exists():
102+
pytest.skip(f"XSLT stylesheet not found at {xsl_path}; cannot produce HTML for comparison.")
103+
104+
# require saxon jar available
105+
jar = Path(SAXON_JAR)
106+
if not jar.exists():
107+
pytest.skip(f"Saxon JAR not found at {jar}; set SAXON_JAR env or place saxon.jar at repo root to run HTML tests.")
108+
109+
out_html = tmp_path / (xml_path.stem + ".html")
110+
try:
111+
run_saxon_transform(xml_path, xsl_path, out_html)
112+
except subprocess.CalledProcessError as e:
113+
# capture stderr for clearer test output
114+
stderr = e.stderr.decode() if hasattr(e, "stderr") else str(e)
115+
pytest.fail(f"Saxon transform failed for {xml_path} with error:\n{stderr}")
116+
117+
# compare normalized HTML
118+
produced_raw = out_html.read_text(encoding="utf8")
119+
expected_raw = expected_html_file.read_text(encoding="utf8")
120+
produced_norm = normalize_html_string(produced_raw)
121+
expected_norm = normalize_html_string(expected_raw)
122+
123+
if produced_norm != expected_norm:
124+
# produce diff of the textual serializations
125+
diff = "\n".join(difflib.unified_diff(expected_norm.splitlines(), produced_norm.splitlines(),
126+
fromfile="expected_html", tofile="produced_html", lineterm=""))
127+
pytest.fail(f"HTML mismatch for {xml_path}:\n{diff}")

0 commit comments

Comments
 (0)