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}