Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions carve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ func TestToHTML_Table(t *testing.T) {
if err != nil {
t.Fatalf("ToHTML error: %v", err)
}
if !strings.Contains(out, "<table>") || !strings.Contains(out, "<th>A</th>") || !strings.Contains(out, "<td>1</td>") {
// A header cell carries scope="col". The expectation here used to be a bare
// <th>, which is what the engine emitted before the corpus grew the
// attribute; a hand-written expectation like this one is the part of the
// suite that cannot notice the artifact moving under it, which is why the
// corpus job exists beside it.
if !strings.Contains(out, "<table>") || !strings.Contains(out, `<th scope="col">A</th>`) || !strings.Contains(out, "<td>1</td>") {
t.Fatalf("expected table markup, got %q", out)
}
}
Expand Down Expand Up @@ -116,29 +121,33 @@ type mismatchError struct{ out string }
func (e *mismatchError) Error() string { return "unexpected output: " + e.out }

// nativeCarveBin locates the native carve-rs CLI for byte-identical checks.
// It is skipped (not failed) when the binary is unavailable, so the suite
// still runs in environments without a carve-rs checkout.
// It is skipped (not failed) when no binary is named, so the suite still runs
// in environments without a carve-rs checkout - CI is one of them, and skips
// this check on every run.
//
// CARVE_BIN IS THE ONLY ROUTE, and the reason is what this check asserts. It
// says "the wasm renders what the native engine renders", which is a statement
// about ONE engine revision: the native side has to be the revision
// internal/wasm/REV names, or a mismatch says nothing about this module. The
// helper used to fall back to two hardcoded target/ paths and then to `carve`
// on PATH, none of which is that revision by construction - a long-lived
// checkout's target/ holds whatever it last built, on whatever branch it was
// on.
//
// It failed exactly that way while this rebuild was being prepared: a stale
// local build predating the header cell growing scope="col" reported a byte
// mismatch on tables against a wasm the corpus proved correct. A check that
// picks its own reference off the filesystem cannot tell "the wasm is wrong"
// from "the thing I found is old", and it reported the first. Skipping until a
// caller vouches for a binary is the honest default; the corpus job, whose
// reference is the spec, is the drift gate either way.
func nativeCarveBin(t *testing.T) string {
t.Helper()
if env := os.Getenv("CARVE_BIN"); env != "" {
return env
}
candidates := []string{
// Static-capable checkout (proto/div-label-fallback, the engine the
// committed wasm is built from) is preferred so the static byte-check
// can run; fall back to a plain main checkout for the interactive check.
"/tmp/carve-rs-static/target/release/carve",
"/media/mark/data/work/git/carve-rs/target/release/carve",
}
for _, c := range candidates {
if _, err := os.Stat(c); err == nil {
return c
}
}
if p, err := exec.LookPath("carve"); err == nil {
return p
}
t.Skip("native carve binary not found; set CARVE_BIN to enable byte-identical check")
t.Skip("CARVE_BIN not set; build the carve CLI from the carve-rs revision in " +
"internal/wasm/REV and point CARVE_BIN at it to enable the byte-identical check")
return ""
}

Expand Down
25 changes: 17 additions & 8 deletions corpus_population_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,21 @@ import (
// markup-carve/pandoc-carve.
//
// So the reference is the corpus's SOURCE, not the corpus. tests/corpus is
// generated from the `::: compare` blocks in docs/examples/{core,extensions,
// edge-cases}.md (see tests/corpus/README.md and scripts/generate-corpus.mjs in
// the spec repository); the generator refuses to write a corpus where the two
// disagree. Both live in the same spec checkout CI already clones, one
// directory away from CARVE_SPEC_CORPUS - the same route corpus_ast_test.go
// already uses to read resources/ast-schema.json.
// generated from the `::: compare` blocks in resources/examples/{core,
// extensions,edge-cases}.md (see tests/corpus/README.md and
// scripts/generate-corpus.mjs in the spec repository); the generator refuses to
// write a corpus where the two disagree. Both live in the same spec checkout CI
// already clones, one directory away from CARVE_SPEC_CORPUS - the same route
// corpus_ast_test.go already uses to read resources/ast-schema.json.
//
// THE PAGES MOVED, AND THIS HELPER DID NOT. They lived under docs/examples/
// until markup-carve/carve#1194 made them generated sources and filed them
// beside the other generator inputs in resources/. Because the path is read at
// run time and the miss is fatal, the corpus job stopped comparing anything at
// all rather than comparing against a stale count - the right failure, but it
// masked every real divergence behind it until the path moved too. The route
// corpus_ast_test.go takes to resources/ast-schema.json is now the same route
// this helper takes, which is one fewer place for the two to disagree.
//
// Counting the source rather than recording a number also means there is no
// literal left to go stale: adding an example moves the expectation on the next
Expand All @@ -59,7 +68,7 @@ var compareMarkerRun = regexp.MustCompile(`^:{3,}`)
// keeps the two counts equal by construction instead of by luck.
func declaredCorpusSize(t *testing.T, corpusDir string) int {
t.Helper()
examplesDir := filepath.Join(corpusDir, "..", "..", "docs", "examples")
examplesDir := filepath.Join(corpusDir, "..", "..", "resources", "examples")
declared := 0
for _, page := range specExamplePages {
path := filepath.Join(examplesDir, page)
Expand Down Expand Up @@ -110,7 +119,7 @@ func requireWholeCorpus(t *testing.T, corpusDir string, got int, what string) {
declared := declaredCorpusSize(t, corpusDir)
if got != declared {
t.Fatalf("%s: %d, but the spec's example pages declare %d. Every ::: compare block in "+
"docs/examples/{core,extensions,edge-cases}.md becomes one corpus pair, so a difference "+
"resources/examples/{core,extensions,edge-cases}.md becomes one corpus pair, so a difference "+
"means the corpus at %s is not the one those pages describe - a truncated or stale "+
"checkout, a wrong CARVE_SPEC_CORPUS, or a corpus that needs regenerating "+
"(npm run corpus:build in the spec repository). It does not mean this run was clean.",
Expand Down
2 changes: 1 addition & 1 deletion internal/wasm/REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9130a7c70249a44bc68bf39eab30b5f92ae1d1b7
98de7874ad2e81f69e57764562c83f4918522ac2
Binary file modified internal/wasm/carve.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion internal/wasm/carve.wasm.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14c52b3ce196ec36b1352ffadaaa4df07f67344ceb49c0a501ebb5fe266330bd carve.wasm
fd3691e29e6e841282a14cab1f2aa4ec419b52615e3f3422fcee23284fb583c9 carve.wasm