Conformance
Correctness is held to the originals — Go’s text/template and Masterminds/sprig — not to
hand-written expectations.
1. By the numbers
1,305 conformance cases are ported from the upstream Go and Sprig test suites and asserted byte-for-byte against gotmpl4j — each rendered through the real Go engine / Sprig funcmap to capture ground-truth output:
| Suite (upstream source) | Cases |
|---|---|
|
28 |
|
31 |
|
223 |
|
40 |
|
280 |
|
131 |
|
207 |
Go engine subtotal |
940 |
Sprig |
283 |
Sprig |
82 |
Sprig subtotal |
365 |
Total |
1,305 |
The html/template rows span the full contextual auto-escaper: the low-level escaper
primitives and parser state machine (280), the end-to-end TestEscape table that runs whole
templates through the public API (131), and the TestTypedContent matrix that drives nine
typed safe-content kinds (HTML, CSS, JS, JSStr, URL, Srcset, HTMLAttr, …) through
every escaping context and asserts gotmpl4j passes them through or emits the same ZgotmplZ
filter-failsafe as Go (207).
These are the portable subset: the extractor uses the Go compiler as an oracle and drops any
case whose data can’t cross to the JVM (Go-typed fixtures, pointer-receiver methods, complex
numbers, <no value> sentinels), so 967 is an honest denominator — not a claim of "100 % of
Go’s tests." Within it the only deviations are a handful of intentional, pinned divergences
(see below); everything else matches the original output exactly.
The counts are not hand-maintained: ConformanceCensusTest (core) and the Sprig census derive
them from the committed fixtures on every build and fail if coverage drops.
2. How it works
-
Engine — test tables are ported from Go’s
text/template,text/template/parse, andhtml/templatetest files (exec tables, the lexer token tables, the value-reflection cases, and the escaper/CSS suites) and rendered through the real Go engine to capture ground-truth output. -
Sprig — cases are ported from Masterminds/sprig’s own
runt/runtvtest tables and rendered through the real Sprig funcmap.
The extractor (.claude/scripts/conformance/runtv_extract.go) uses the Go compiler as a
portability oracle: any case whose data isn’t portable to the JVM (Go-typed fixtures, methods)
is dropped automatically. Surviving cases become base64 TSV fixtures under each module’s
src/test/resources/conformance/, asserted by the *ConformanceTest suites in core and
SprigConformanceTest (which runs both the runt and runtv tables) in sprig.
Regenerating the fixtures requires a Go toolchain; running the JUnit suites needs only the committed TSVs.
3. Known divergences
A small number of behaviors differ from the originals on purpose, because the JVM and Go are
not identical. These are pinned in *_known_divergences.txt next to the fixtures, for example:
-
Regular expressions use Java’s
java.util.regexengine rather than Go’s RE2 — syntax and worst-case performance differ. -
Some Go date layouts map approximately onto Java date formatting.
Pinning a divergence keeps it visible and intentional rather than silently drifting.