@@ -155,14 +155,19 @@ func TestMayContainFootnote(t *testing.T) {
155155 assert .True (t , mayContainFootnote ([]byte ("[^note]: a definition.\n " )))
156156}
157157
158- // TestCheckFootnotes_NoNeedle_SkipsBothRegexPasses benchmarks
159- // checkFootnotes on prose with no footnote syntax to demonstrate the
160- // gate's real effect: without it, footnoteRefRE and footnoteDefRE each
161- // run a full FindAllSubmatchIndex over the whole file on every Check
162- // call, unconditionally, even though this rule (MDS043) is opt-in and
163- // so only runs for workspaces that enabled it. b.Fatalf pins a budget
164- // so a future regression that removes the gate is caught in CI rather
165- // than by a human re-running benchstat.
158+ // footnoteCheckBudgetNs pins the gate's real effect: without it,
159+ // footnoteRefRE and footnoteDefRE each run a full FindAllSubmatchIndex
160+ // over the whole file on every Check call. Gated: ~1us (one
161+ // bytes.Contains scan). Ungated: ~580us (two full regex passes over
162+ // ~28KB). The budget keeps roughly the same ~15-20x headroom over the
163+ // gated baseline that BenchmarkCheckCorpusSmall/Large use (see
164+ // internal/engine/bench_test.go), well above measurement noise, while
165+ // staying two orders of magnitude below the ungated cost.
166+ const footnoteCheckBudgetNs = 50_000
167+
168+ // BenchmarkCheckFootnotes_NoNeedle exercises checkFootnotes on prose with
169+ // no footnote syntax; benchstat-friendly (no assertion), consumed by
170+ // TestCheckFootnotes_NoNeedleBudget below for the enforced gate.
166171func BenchmarkCheckFootnotes_NoNeedle (b * testing.B ) {
167172 var src []byte
168173 for i := 0 ; i < 200 ; i ++ {
@@ -175,18 +180,36 @@ func BenchmarkCheckFootnotes_NoNeedle(b *testing.B) {
175180 require .NoError (b , err )
176181 r := & Rule {}
177182
178- b .ResetTimer ()
179183 for i := 0 ; i < b .N ; i ++ {
180184 r .checkFootnotes (f )
181185 }
182- perOp := float64 (b .Elapsed ().Nanoseconds ()) / float64 (b .N )
183- // Gated: ~1µs (one bytes.Contains scan). Ungated: ~580µs (two full
184- // regex passes over ~28KB). 50µs stays far above measurement noise
185- // while catching a dropped gate by two orders of magnitude.
186- const budgetNsPerOp = 50_000
187- if perOp > budgetNsPerOp {
188- b .Fatalf ("checkFootnotes on a no-footnote file: %.0f ns/op, budget = %d; " +
186+ }
187+
188+ // TestCheckFootnotes_NoNeedleBudget pins the ns/op regression gate under
189+ // a normal `go test` run. CI's check-bench/markdown-bench jobs only run
190+ // `-bench` against internal/engine, pkg/markdown, internal/lsp, and
191+ // cue/cuelite (see .github/workflows/ci.yml) — a plain
192+ // BenchmarkCheckFootnotes_NoNeedle with an inline b.Fatalf would never
193+ // execute in CI and the assertion would be dead code. testing.Benchmark
194+ // runs the benchmark function programmatically so the assertion lands
195+ // in a Test that `go test ./...` (and therefore CI) actually runs,
196+ // matching paragraphstructure.TestCheckAllocBudget's rationale for its
197+ // own Benchmark/Test pair.
198+ func TestCheckFootnotes_NoNeedleBudget (t * testing.T ) {
199+ if testing .Short () {
200+ t .Skip ("perf gate skipped in -short mode" )
201+ }
202+ if raceEnabled {
203+ t .Skip ("perf gate skipped under -race; the race detector's " +
204+ "instrumentation overhead perturbs the ns/op measurement" )
205+ }
206+ result := testing .Benchmark (BenchmarkCheckFootnotes_NoNeedle )
207+ perOp := float64 (result .NsPerOp ())
208+ t .Logf ("checkFootnotes on a no-footnote file = %.0f ns/op (budget = %d)" ,
209+ perOp , footnoteCheckBudgetNs )
210+ if perOp > footnoteCheckBudgetNs {
211+ t .Fatalf ("checkFootnotes on a no-footnote file: %.0f ns/op, budget = %d; " +
189212 "the mayContainFootnote gate may have been removed or bypassed" ,
190- perOp , budgetNsPerOp )
213+ perOp , footnoteCheckBudgetNs )
191214 }
192215}
0 commit comments