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

text/template exec tables (exec_test.go)

28

text/template execute/text cases

31

text/template value reflection — fields & methods (tval)

223

text/template/parse lexer token tables

40

html/template escaper / escaping-context text / errors / CSS decode

280

html/template end-to-end escaping (TestEscape)

131

html/template typed safe content (TestTypedContent)

207

Go engine subtotal

940

Sprig runt(tpl, expect) — no-data cases

283

Sprig runtv(tpl, expect, vars) — with-data cases

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, and html/template test 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/runtv test 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.regex engine 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.

4. Building

./mvnw clean install   # build + test + format/PMD/checkstyle gates
./mvnw test            # tests only

Requires JDK 17.