krz/orgo

Lightning fast org-mode static site generator. fast go org-mode static-site-generator

tests/conformance.rs

73 lines · 2979 bytes

 1//! orgo against the shared conformance corpus (`krz/org-conformance`).
 2//!
 3//! orgo *generates* that corpus's goldens, so this test is not looking for orgo to be
 4//! wrong — it is the tripwire that fires when orgo's renderer changes and the checked-in
 5//! corpus was not regenerated to match. Without it the two drift silently: orgo's own
 6//! snapshots update with `cargo insta accept`, the corpus does not, and every other
 7//! language is then conforming to a stale reference.
 8//!
 9//! The corpus lives in a sibling repo, so the test is opt-in: point `ORG_CONFORMANCE_DIR`
10//! at a checkout and it runs; unset, it skips cleanly, exactly like the Emacs oracle.
11//!
12//!   ORG_CONFORMANCE_DIR=../org-conformance cargo test --test conformance
13
14use std::path::PathBuf;
15
16use camino::Utf8PathBuf;
17
18use orgo::parser::parse;
19use orgo::render::{render, Html, SyntectHighlighter};
20use orgo::resolve::ResolvedDoc;
21use orgo::skeleton::skeleton;
22
23fn corpus_dir() -> Option<PathBuf> {
24    let dir = PathBuf::from(std::env::var_os("ORG_CONFORMANCE_DIR")?);
25    dir.join("cases").is_dir().then_some(dir)
26}
27
28fn render_skeleton(org_path: &std::path::Path) -> Vec<String> {
29    let source = std::fs::read_to_string(org_path).expect("read case .org");
30    let rel = Utf8PathBuf::from(org_path.file_name().unwrap().to_string_lossy().into_owned());
31    let document = parse(rel.as_path(), &source).expect("parse case");
32    let Html(html) = render(&ResolvedDoc { document }, &SyntectHighlighter::new());
33    skeleton(&html)
34}
35
36#[test]
37fn orgo_matches_the_conformance_corpus() {
38    let Some(dir) = corpus_dir() else {
39        eprintln!("ORG_CONFORMANCE_DIR unset or has no cases/ — skipping conformance check");
40        return;
41    };
42    let cases = dir.join("cases");
43
44    let mut checked = 0;
45    let mut mismatches = Vec::new();
46    for entry in std::fs::read_dir(&cases).expect("read cases dir") {
47        let path = entry.expect("dir entry").path();
48        if path.extension().and_then(|e| e.to_str()) != Some("org") {
49            continue;
50        }
51        let name = path.file_stem().unwrap().to_string_lossy().into_owned();
52        let golden_path = cases.join(format!("{name}.skeleton"));
53        let golden = std::fs::read_to_string(&golden_path)
54            .unwrap_or_else(|_| panic!("missing golden skeleton for case {name}"));
55        let golden_lines: Vec<&str> = golden.lines().collect();
56
57        let ours = render_skeleton(&path);
58        if ours != golden_lines {
59            mismatches.push(name);
60        }
61        checked += 1;
62    }
63
64    assert!(checked > 0, "corpus at {} has no .org cases", cases.display());
65    assert!(
66        mismatches.is_empty(),
67        "orgo's skeleton no longer matches the corpus goldens for: {}. \
68         If the renderer change is intended, regenerate the corpus \
69         (ORGO_DIR=../orgo tools/generate.sh) and commit it.",
70        mismatches.join(", ")
71    );
72    eprintln!("conformance: {checked} cases match the corpus goldens");
73}