conformance

package
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package conformance generates the conformance summary that docs/conformance-gaps.md publishes, from the measured counts recorded in tests/conformance/results.json.

It exists because the document used to state its own total. It said 168 while its own rows summed to 104, and nothing failed: a prose number is guarded by whoever last read the paragraph. So the total is no longer written anywhere a human can write it. This package adds the rows up and rewrites one marked region of the document; the arithmetic has exactly one implementation, and a stale total is unreachable rather than merely discouraged.

Only the region between the BEGIN and END markers is generated. The rest of docs/conformance-gaps.md is hand-written analysis -- the per-case verdicts, the spec citations, the measured costs -- and is never touched here.

Index

Constants

View Source
const (
	BeginMarker = "<!-- BEGIN GENERATED CONFORMANCE SUMMARY -->"
	EndMarker   = "<!-- END GENERATED CONFORMANCE SUMMARY -->"
)

The markers delimiting the generated region. They are HTML comments so that they are invisible in rendered Markdown but survive every Markdown tool.

Variables

View Source
var TreeKinds = []string{"func-test", "func-fuzz", "limits-boundary"}

TreeKinds is the closed set of counting methods Count knows how to run. A kind outside it is a validation error rather than a skipped figure: a generator that silently publishes a zero for a method it does not understand is worse than one that refuses to run.

View Source
var Verdicts = []string{
	"implementation",
	"fixture",
	"implementation-defined",
	"optional",
	"deliberate-divergence",
	"not-run",
}

Verdicts is the closed vocabulary a case ID may carry. A verdict outside it is a validation error rather than a passthrough, because the whole value of the file is that the categories mean the same thing in every suite.

Functions

func Apply

func Apply(docPath string, r *Results) (changed bool, err error)

Apply rewrites the marked region of doc in place, leaving every other byte alone, and reports whether the file changed.

func DefaultPaths

func DefaultPaths(root string) (results, doc string)

DefaultPaths resolves the two files relative to the repository root, so that the generator behaves the same from any working directory.

func Replace

func Replace(doc string, r *Results) (string, error)

Replace swaps the generated region of doc for a freshly rendered one.

A CRLF document is handled rather than rewritten: on Windows a checkout with core.autocrlf=true gives every line a \r, and a generator that emitted LF into it would report a diff on every run for ever. The region is rendered in the line ending the document already uses, and no line outside the region is touched, so the file's convention survives whichever platform regenerates it.

func ReplaceAll

func ReplaceAll(doc string, regions []Region) (string, error)

ReplaceAll applies every region belonging to one file.

func ReplaceRegion

func ReplaceRegion(doc string, g Region) (string, error)

ReplaceRegion swaps one named region of doc, by the same rules Replace uses for the conformance summary: only the span between the markers moves, and the document's own line ending is preserved so that a Windows checkout with core.autocrlf=true does not report a diff on every run for ever.

Types

type Breakdown

type Breakdown struct {
	ID    string `json:"id"`
	Label string `json:"label"`
	Suite string `json:"suite"`
	Of    int    `json:"of"`
	Note  string `json:"note,omitempty"`

	// Overlap is how many of this breakdown's cases are ALSO enumerated in
	// the suite's Cases list. It exists because the two halves were checked
	// only against the suite total and never against each other, and so
	// summed to 35 of 34 for a year without a gate firing: merge-097sf was
	// enumerated while actually being skipped, and su-ascent-902 was counted
	// both individually and in the block. Two errors in opposite directions
	// left a plausible-looking total, which is the hardest kind to see.
	//
	// A real overlap is legitimate -- su-ascent-902 belongs to the XTSE3430
	// block by symptom and is argued individually because its verdict is not
	// the block's -- but it must be DECLARED, so that "the parts do not add
	// up" and "the parts overlap on purpose" cannot be confused.
	Overlap int `json:"overlap,omitempty"`
}

Breakdown is a named subset of one suite's disagreements: "14 of the 34 XSLT 3.0 failures want an XTSE3430". Both halves are published, so both are recorded, and the whole must not exceed the suite's disagreement count.

type Case

type Case struct {
	ID      string `json:"id"`
	Verdict string `json:"verdict"`
	Note    string `json:"note,omitempty"`
}

Case is one named disagreement. The list need not be exhaustive -- XSD's 61 are recorded set by set, and XSLT 3.0's XTSE3430 block as a block -- so the enumerated cases are checked against the disagreement count as an upper bound, never as the count itself. Deriving the total from the enumeration would silently under-report every suite whose cases are summarised.

type FileResult

type FileResult struct {
	Path    string
	Changed bool
}

FileResult is what one file's generation did.

type Region

type Region struct {
	Name string
	Body func() string

	// Whole says the file IS the region: it is generated end to end, with no
	// hand-written prose around it, so Replace writes the whole file rather
	// than looking for markers in it. docs/stats.md is the only one.
	Whole bool

	// Bare says the body already carries its own "this is generated" header,
	// so ReplaceRegion must not prepend the common one. Only the conformance
	// summary sets it: its header predates this file and is worded for the
	// one region whose defect -- a hand-written Total -- it was built to
	// close. Duplicating a header would be the generator adding noise to a
	// document on every run, which is the opposite of its contract.
	Bare bool

	// Inline says the markers sit on the prose line itself, around the
	// figure, with no header and no line break: an HTML comment on its own
	// line is a block, and a block inside a paragraph or a list item splits
	// it in two mid-sentence. A table row can carry the block form; "**577**
	// of its 593 test documents" cannot.
	Inline bool
}

Region is one generated span of one document.

func (Region) Begin

func (g Region) Begin() string

func (Region) End

func (g Region) End() string

type Results

type Results struct {
	// Decoding is strict (DisallowUnknownFields), so the JSON's own prose --
	// the note telling the next editor not to hand-edit the generated table --
	// needs a home here or the file will not load.
	Comment     string `json:"_comment,omitempty"`
	GeneratedBy string `json:"generated_by,omitempty"`

	Suites  []Suite `json:"suites"`
	Corpora []Suite `json:"corpora"`

	// Tree holds the figures that are DERIVED FROM THE SOURCE TREE rather
	// than from a suite run: how many unit tests, fuzz targets and limit
	// boundary tests there are. Their values are not in this file and cannot
	// be -- see TreeCount in stats.go -- only the counting method is.
	Tree []TreeCount `json:"tree"`

	// SuiteRevisions is the commit of each vendored W3C suite the figures
	// above were measured against, keyed by its directory under testdata/.
	//
	// Without it a figure in this file is not reproducible. The suites are
	// separate checkouts that CI clones at --depth 1 from their default
	// branch, so a suite update can move a count with no change to this
	// repository at all, and the ratchet would then fail on a commit that
	// changed nothing. tests/check.sh has always PRINTED these revisions into
	// its provenance, but that is a per-run artifact which expires; this is
	// the copy that travels with the numbers it explains.
	//
	// A suite that is not its own git checkout is absent rather than wrong:
	// testdata/relaxng is vendored files, and asking git about it answers
	// with THIS repository's HEAD, which would be a revision that means
	// nothing. suiterev in tests/check.sh applies the same containment test.
	SuiteRevisions map[string]string `json:"suite_revisions,omitempty"`

	// Breakdowns are named subsets of a suite's disagreements, such as "14 of
	// the 34 XSLT 3.0 failures want an XTSE3430". Both halves get published,
	// so both are checked: the subset may not exceed its suite's count.
	Breakdowns []Breakdown `json:"breakdowns"`
}

Results is the whole file. Corpora are kept separate from Suites rather than flagged inside one list: DocBook xslTNG and XSpec are real-world stylesheet collections, not W3C conformance suites, and counting them in the total would mean publishing a conformance figure against tests nobody wrote to a specification. Separate types make that impossible to do by accident.

func Load

func Load(path string) (*Results, error)

Load reads and validates a results file.

func (*Results) ApplyAll

func (r *Results) ApplyAll(root string, write bool) ([]FileResult, error)

ApplyAll rewrites every generated region in every file, and reports which files moved. With write=false it changes nothing and reports what would move, which is what -check and the gate use: the tree is dirty in most runs, so comparing rather than regenerating-then-diffing is the only reading that does not report the user's work in progress as a failure.

func (*Results) Breakdown

func (r *Results) Breakdown(id string) Breakdown

Breakdown looks up one named subset, with the same rule.

func (*Results) Count

func (r *Results) Count(root string) error

Count fills in every TreeCount by running its method against root.

This is the step that makes the derived figures unforgeable. The generator cannot emit a unit-test count that the tree does not have, because there is nowhere to write one down.

func (*Results) Regions

func (r *Results) Regions() map[string][]Region

Regions is every generated region this generator owns, keyed by the file it lives in. Each renders from the loaded Results and nothing else.

A region is small on purpose. The documents around them are long, careful, hand-written English, and the rule is that generation replaces a figure, not a paragraph: the sentence stays the author's, the number stops being theirs.

func (*Results) Render

func (r *Results) Render() string

Render produces the body of the generated region, markers included.

func (*Results) RunDates

func (r *Results) RunDates() []string

RunDates lists the distinct measurement dates, sorted. A summary drawn from runs on different days is honest about it rather than printing one date.

func (*Results) Suite

func (r *Results) Suite(id string) Suite

Suite looks up one measured lane by ID, with the same rule.

func (*Results) Total

func (r *Results) Total() int

Total is the number of W3C disagreements: the sum of the suite rows, and nothing else. Corpora are excluded by construction -- they are a different field -- so no caller can pass a total in and no caller can add a corpus to it. This function is the only place the number exists.

func (*Results) TreeCount

func (r *Results) TreeCount(id string) TreeCount

TreeCount looks up one derived figure by ID. It panics on an unknown ID rather than returning a zero, because a template that silently prints 0 for a figure it misspelled is exactly the failure this file exists to prevent.

func (*Results) Validate

func (r *Results) Validate() error

Validate rejects a file whose arithmetic does not close.

The check that matters is passed + disagreements == total, per suite, named. That is the invariant the document lost: rows whose parts did not add up sat beside a total that agreed with none of them. An unnamed failure would be nearly as useless as no failure, so every message carries the suite ID.

type Suite

type Suite struct {
	ID            string `json:"id"`
	Component     string `json:"component"`
	Suite         string `json:"suite"`
	Edition       string `json:"edition"`
	Passed        int    `json:"passed"`
	Disagreements int    `json:"disagreements"`
	Total         int    `json:"total"`
	RunDate       string `json:"run_date"`
	Command       string `json:"command"`
	Cases         []Case `json:"cases"`

	// The skipped cases, split by class. Out of scope is what the suite says
	// a conforming processor may leave out -- the wrong spec version, a
	// Unicode version, an optional numbering language; unimplemented is a
	// feature this engine lacks or a dependency the harness does not model.
	// Only the XSLT lanes record them (tests/xslts/deps.go draws the line);
	// a lane that does not is rendered with an empty cell, not a zero.
	SkippedOutOfScope    int `json:"skipped_out_of_scope,omitempty"`
	SkippedUnimplemented int `json:"skipped_unimplemented,omitempty"`

	// Why a suite's cases are not all enumerated above. Rendered nowhere; it
	// is here so the JSON explains itself to whoever next edits it.
	UnenumeratedNote string `json:"unenumerated_note,omitempty"`
}

Suite is one measured lane: a full run of one suite at one specification edition, with the command and date that produced it.

type TreeCount

type TreeCount struct {
	ID     string `json:"id"`
	Label  string `json:"label"`
	Method string `json:"method"`
	Kind   string `json:"kind"`

	Value int `json:"-"`
}

TreeCount is a figure derived from the source tree rather than from a suite run: how many unit tests there are, how many fuzz targets, how many limit boundary tests.

The Value field is NOT decoded from JSON -- it has no struct tag -- so a number cannot be typed into results.json and reach a document. It is filled by Count below, which runs the recorded method against the tree. What the JSON carries is Method: the human-readable command, quoted beside every published copy of the number, because a figure whose method is not written down is merely asserted twice.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL