A language-neutral conformance corpus for org-mode to HTML renderers.

conformance html org-mode testing

README.md

78 lines · 3725 bytes

 1# org-conformance
 2
 3A language-neutral conformance corpus for org-mode → HTML renderers.
 4
 5The problem it solves: the same `.org` file is rendered by several independent
 6codebases — a Go forge (gitbay via go-org), a Rust static-site generator (orgo), a Swift
 7package (OrgSwift, shared by the iOS clients), a browser previewer (org-live). They drift.
 8A footnote renders in one and vanishes in another; a `:tag:` styles here and prints raw
 9there. Nobody notices until they look at the same document on two surfaces.
10
11This corpus is the shared answer to "what is the *right* output". Each implementation runs
12it in its own test suite and asserts against the same goldens.
13
14## What's here
15
16```
17cases/
18  <name>.org        the input
19  <name>.html       orgo's rendered HTML — the reference rendering, for humans
20  <name>.skeleton   the golden semantic skeleton — the assertion target
21manifest.json       every case, the constructs it exercises, and its scope (in/out)
22SKELETON.md         the exact reduction each implementation must port
23tools/generate.sh   regenerate .html and .skeleton from orgo
24```
25
26## The reference
27
28The goldens are produced by **orgo**, and orgo alone. orgo is not self-appointed: it
29carries an oracle test that exports every fixture with **Emacs's own `ox-html`** under
30`emacs --batch` and diffs the two semantic skeletons, so its output is anchored to org's
31reference exporter rather than to itself. When orgo and Emacs deliberately differ (orgo
32emits `<time datetime>` for a timestamp where Emacs writes plain text, `<em>` where Emacs
33writes `<i>`, and so on), those divergences are enumerated and reviewed in orgo's oracle.
34Everything downstream conforms to orgo.
35
36## How an implementation conforms
37
38Comparing raw HTML across languages is pointless — every renderer wraps and classes things
39differently. Instead, both sides are reduced to a **skeleton**: the ordered sequence of
40element opens, closes, and text runs, with `div`/`span` and all attributes except
41`href`/`src` dropped, whitespace collapsed, and entities decoded. See
42[SKELETON.md](SKELETON.md) for the exact algorithm.
43
44Each implementation:
45
461. Ports the ~150-line skeleton reduction. Verify the port first by feeding each
47   `cases/<name>.html` through it and checking it reproduces `cases/<name>.skeleton`
48   exactly — this separates a skeleton-port bug from a renderer bug.
492. Renders each `cases/<name>.org` with its own renderer, reduces the output with its
50   port, and asserts equality against `cases/<name>.skeleton`.
513. For constructs it does not yet support, keeps an explicit allowlist of expected
52   divergences (mirroring orgo's own "deliberate divergence" list against Emacs). A
53   green suite with a documented allowlist beats a red suite nobody acts on — the point
54   is that *new, unintended* drift shows up as a diff.
55
56Cases with `"scope": "out"` in the manifest are deliberately-unsupported constructs
57(babel, LaTeX, macros, arbitrary drawers). The contract there is only that they degrade
58predictably and never crash; an implementation may legitimately differ from orgo, so treat
59those as documentation rather than hard assertions.
60
61## Consumers
62
63| Implementation | Language | Engine | Status |
64|---|---|---|---|
65| orgo | Rust | own element-tree renderer | reference (generates the goldens) |
66| OrgSwift | Swift | extracted from hutch | conformance test in `swift test` |
67| gitbay | Go | go-org | planned |
68| org-live | JS | (to migrate off vendored org-js) | planned |
69
70## Regenerating
71
72The goldens are derived, not hand-written. After an intended change to orgo's renderer:
73
74```
75ORGO_DIR=../orgo tools/generate.sh
76```
77
78Review the diff, and only commit it once orgo's own oracle agrees the change is right.