From 9ae43a3ee1b35dc55b15e737fe50d2e1f7d38cdd Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 26 Oct 2026 23:24:06 -0400 Subject: [PATCH] test(config): make TestSchemelessLinear robust to coarse timers The test divided work for 10x input by a 4 KB reading that is 0s on Windows (coarse timer), so any nonzero large reading failed. Time batches of at least 3 ms per size, take the best of five, compare per-call cost with a 40x bound plus a 200 us floor. A 100x quadratic regression still fails. --- internal/credmask/linear_test.go | 47 ++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/internal/credmask/linear_test.go b/internal/credmask/linear_test.go index c0c78a1b7..6eac22473 100644 --- a/internal/credmask/linear_test.go +++ b/internal/credmask/linear_test.go @@ -6,16 +6,34 @@ import ( "time" ) -// bestOf returns the fastest of n runs of Schemeless over unit repeated to -// about size bytes; the minimum filters scheduler and GC noise on a loaded CI -// runner. -func bestOf(unit string, size, n int) time.Duration { +// sink keeps the compiler from discarding the measured call. +var sink string + +// minBatch is how long one timed batch must run. Windows timers tick at about +// 0.5 to 15 ms, so a single 4 KB call reads as 0s there; a batch this long +// reads as a real number on every platform. +const minBatch = 3 * time.Millisecond + +// perCall returns the cost of one Schemeless call over unit repeated to about +// size bytes. It doubles the batch until the batch lasts minBatch, then takes +// the fastest of n batches, so a scheduler or GC stall on a loaded runner does +// not read as super-linear growth and a zero reading never reaches a ratio. +func perCall(unit string, size, n int) time.Duration { v := strings.Repeat(unit, size/len(unit)) + run := func(reps int) time.Duration { + start := time.Now() + for i := 0; i < reps; i++ { + sink = Schemeless(v) + } + return time.Since(start) + } + reps := 1 + for run(reps) < minBatch && reps < 1<<20 { + reps *= 2 + } best := time.Duration(1<<63 - 1) for i := 0; i < n; i++ { - start := time.Now() - _ = Schemeless(v) - if d := time.Since(start); d < best { + if d := run(reps) / time.Duration(reps); d < best { best = d } } @@ -23,17 +41,18 @@ func bestOf(unit string, size, n int) time.Duration { } // TestSchemelessLinear: adversarial shapes must scale linearly. 10x the input -// may take about 10x as long; a quadratic scan takes about 100x, so the bound -// is 50x plus a 2 ms floor for timer noise. The 40 KB to 400 KB step is where -// a quadratic rescan stands clear of that noise. +// may take about 10x as long; a quadratic scan takes about 100x. The bound is +// 40x plus a 200 µs per-call floor for noise, so a 10x blow-up to 100x still +// fails. Costs are per call over batches of at least minBatch, never a single +// call, so a coarse timer cannot make the small case read as 0s. func TestSchemelessLinear(t *testing.T) { units := []string{"@a(", ":@a(", "a:@", "a:b@c(", "x=y@a(", "password=", "aaaaaaaa", "::::", "@@@@"} for _, unit := range units { - prev := bestOf(unit, 4_000, 7) + prev := perCall(unit, 4_000, 5) for _, size := range []int{40_000, 400_000} { - cur := bestOf(unit, size, 7) - if cur > 50*prev+2*time.Millisecond { - t.Errorf("unit %q: %d B took %v, %d B took %v: not linear", unit, size/10, prev, size, cur) + cur := perCall(unit, size, 5) + if cur > 40*prev+200*time.Microsecond { + t.Errorf("unit %q: %d B took %v/call, %d B took %v/call: not linear", unit, size/10, prev, size, cur) } prev = cur }