Skip to content

yaml conformance tests - #4

Merged
fredbi merged 5 commits into
masterfrom
worktree-yaml-conformance
Jul 31, 2026
Merged

yaml conformance tests#4
fredbi merged 5 commits into
masterfrom
worktree-yaml-conformance

Conversation

@fredbi

@fredbi fredbi commented Jul 31, 2026

Copy link
Copy Markdown
Member

Change type

Please select: 🆕 New feature or enhancement|🔧 Bug fix'|📃 Documentation update

Short description

Fixes

Full description

Checklist

  • I have signed all my commits with my name and email (see DCO. This does not require a PGP-signed commit
  • I have rebased and squashed my work, so only one commit remains
  • I have added tests to cover my changes.
  • I have properly enriched go doc comments in code.
  • I have properly documented any breaking change.

fredbi and others added 4 commits July 31, 2026 07:21
Vendors the community YAML Test Suite (351 files, 406 cases) and measures where
YL actually stands. This is a "see where we are" instrument, not a pass/fail
gate: very few YAML parsers reach 100%, and YL is not trying to — it reads YAML
AS JSON, so anything the JSON data model cannot express is out of scope by
construction rather than a bug.

That framing is what makes the suite usable for us: its `json` field is the JSON
a document is equivalent to, which is exactly our expectation. A case is lexed
twice — the YAML with YL, the expected JSON with the JSON lexer L — and
conformance means the two token streams are identical. Organised like the JSON
conformance test (buckets + xfail inventory + golden snapshot of the parts that
can only be observed).

Baseline
--------
  accept + token stream matches the expected JSON   204 / 272   (75%)
  invalid document rejected                          85 /  94   (90%)
  recorded only (no single-root JSON equivalent)     63
  known divergences                                  54

The 54 divergences group into five causes, listed per case in
conformanceXFail and analysed in CONFORMANCE.md:

  23  non-scalar mapping keys. Mostly keys that RESOLVE to a string (`*alias :
      v`, `&a k : v`, `!!str k : v`) — the suite's own JSON shows the resolved
      string as the key. A feature gap, not a scope boundary, and the biggest
      single win available: it would take the accept rate to ~82%.
  14  multiple documents. Out of scope until ND-JSON lands.
   9  invalid documents we ACCEPT. The only group where we are wrong in the
      dangerous direction; all inherited from goccy (flow collections, comment
      placement, tabs) rather than from our walk.
   5  accepted but the token stream differs — scalar resolution (!!binary,
      non-specific tags, trailing whitespace). Subtlest group: nothing errors.
   3  valid documents goccy will not parse at all.

Notes on the harness
--------------------
The suite renders invisible characters visibly and they MUST be translated
back — a fixture that hinges on a trailing space or a hard tab silently becomes
a different, usually valid, document otherwise. My first implementation matched
a fixed set of tab widths instead of upstream's /—*»/ and mangled any run of
four or more em-dashes; fixing it moved 7 cases from "divergent" to passing. The
rule is now a regexp and is documented in SOURCE.md.

The third bucket is not a weasel. A multi-document stream's `json` field holds
several values in sequence and an empty document's is empty; neither is a JSON
token stream, so there is nothing to compare and only the verdict is recorded.
testdata/conformance_recorded.golden pins those verdicts, so "out of scope"
cannot quietly become "unnoticed regression" — the same blind spot the JSON
suite had with its i_ cases, where ten documents carrying ill-formed UTF-8 were
accepted for months with the suite green.

Both drive modes (bytes and reader) run every case and must agree with each
other before either is compared to the suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
The YAML Test Matrix (matrix.yaml.info) publishes a "JSON output" comparison
over the same suite — is the loaded data equal to the `json` field — which is
the same question our harness asks, so the numbers are meaningfully comparable.

On its basis we score 289/366 = 79% (or 71% counting the 40 cases with no JSON
equivalent as misses), which puts us in the same band as the mainstream loaders
— PyYAML 75%, go-yaml 76%, ruby psych 76%, ruamel 78% — and clearly behind the
four leaders: libfyaml 93%, JS yaml 92%, YAML::PP 92%, HsYAML 91%.

Recorded with the caveats that stop it being a ranking: the matrix measures
LOADERS (parse, serialize, compare) while we measure a lexer's token stream; we
forfeit points by design on multi-document streams and complex keys; and our
floor is goccy's, since 12 of our 54 divergences are its behavior rather than
ours and goccy is not in the matrix at all (its `go-yaml` entry is a different
project).

The actionable part: closing group 1 alone — resolved-scalar mapping keys, 23
cases, one coherent feature — reaches 85%, above every implementation in the
middle band. Groups 3 and 4 on top reach 89%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Conformance fixtures are DATA: their exact bytes are the test. A checkout with
core.autocrlf=true (the Windows default) rewrites every LF to CRLF and silently
changes what is being measured — a YAML fixture that hinges on a trailing space
or a hard tab becomes a different document, and the failure looks like a lexer
bug rather than a checkout artefact.

The repo-wide fix already landed on master (.gitattributes marks testdata/**,
*.golden and generated *.s as -text, so git never translates them). This is the
other half, for the YAML harness: a suite that arrived some other way — an
unmarked clone, a zip, a copy through a Windows tool — must still yield the
documents upstream wrote, so the loader normalises CRLF to LF on read.

Ordering matters and is the subtle part: normalisation runs BEFORE unescaping,
so the suite's "←" (an intentional carriage return) is still the only way a CR
enters a case. Normalising after would have eaten it. No vendored fixture
contains a raw CR byte, which is what makes the normalisation safe.

Tested as a property rather than assumed: the whole suite is copied to CRLF into
a temp dir, re-loaded through the real loader, and every case asserted
byte-identical to its LF original — that would catch a leak anywhere in the
path, including inside the YAML parser the fixtures are read with. Plus unit
coverage for the narrow contract (CRLF collapses, a lone CR survives) and a
guard that the committed fixtures are still stored with LF.

Mutation-checked: stubbing normalizeLineEndings to the identity fails the
line-ending tests, so they have teeth.

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
@fredbi fredbi changed the title Worktree yaml conformance yaml conformance tests Jul 31, 2026
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
@fredbi
fredbi merged commit 49b8b98 into master Jul 31, 2026
16 of 17 checks passed
@fredbi
fredbi deleted the worktree-yaml-conformance branch July 31, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant