test(links): mention linearity tests compare n with 4n, not an absolute bound (BUG-3995) (#2262)

* test(links): mention linearity tests compare n with 4n, not an absolute bound (BUG-3995)

TestExtractMentionsLinearAfterCodeSpans failed CI under -race at 3.08 s
against its 3 s bound (#2259). Both mention linearity tests, it and its
sibling TestExtractMentionsLinearOnSlashRuns, now share
assertMentionsLinear: best of five runs at n and 4n, each after a
runtime.GC(), and the ratio must stay under 8x (about 4x when linear,
about 16x when quadratic). A 30 s per-run backstop stops a regressed scan
from running the suite for minutes. No absolute bound remains.

Claude-Session: https://claude.ai/code/session_01EZ6hZzQiytCeBxXrtCWcVd

* test(links): the mention linearity helper's comment bounds its claim (BUG-3995 r1)

It said a slow runner "cannot fail" the check and that the backstop stops
a regressed scan "running the suite for minutes". The 30 s per-run
backstop is absolute, and a regressed run measured 1.5 to 3 minutes before
failing. The comment now says what is true: the ratio is independent of
runner speed, and the backstop is about 16x the slowest linear run measured.

Claude-Session: https://claude.ai/code/session_01EZ6hZzQiytCeBxXrtCWcVd
This commit is contained in:
xarmian
2026-10-20 15:58:18 -04:00
committed by GitHub
parent dd412a022d
commit 24833331ff
+43 -15
View File
@@ -2,6 +2,7 @@ package links
import (
"reflect"
"runtime"
"strings"
"testing"
"time"
@@ -75,26 +76,53 @@ func TestParseMentions(t *testing.T) {
}
}
// ExtractMentions stays linear in the body: a backward scan per "@" made
// "/@a" repeated quadratic (TASK-3917 r3 F1; 240 KB took ~10 s). The bound is
// ~60x the linear cost, so only a superlinear walk can trip it.
func TestExtractMentionsLinearOnSlashRuns(t *testing.T) {
body := strings.Repeat("/@a", 100000)
start := time.Now()
ExtractMentions(body)
if d := time.Since(start); d > 3*time.Second {
t.Fatalf("ExtractMentions on %d bytes took %v; a per-match backward scan is back", len(body), d)
// assertMentionsLinear times ExtractMentions on body(n) and body(4n), best of
// five each, and fails if 4x the input costs 8x the time or more: about 4x
// when linear, about 16x when quadratic. A slow runner (-race, a loaded box)
// slows both sizes alike, so the ratio does not depend on its speed, which a
// 3 s absolute bound did (BUG-3995). The one absolute check is a 30 s per-run
// backstop, about 16x the slowest linear run measured (-race, 4n): it caps how
// long a regressed scan runs before failing, at a few minutes for the test.
func assertMentionsLinear(t *testing.T, n int, body func(int) string, regression string) {
t.Helper()
timeIt := func(n int) time.Duration {
b := body(n)
best := time.Duration(0)
for i := 0; i < 5; i++ {
runtime.GC() // a collection left over from the last run is not this run's cost
start := time.Now()
ExtractMentions(b)
d := time.Since(start)
if d > 30*time.Second {
t.Fatalf("n=%d: one run took %v; %s", n, d, regression)
}
if best == 0 || d < best {
best = d
}
}
return best
}
small, large := timeIt(n), timeIt(4*n)
ratio := float64(large) / float64(small)
t.Logf("n=%d: %v; n=%d: %v; ratio %.1f", n, small, 4*n, large, ratio)
if ratio >= 8 {
t.Fatalf("4x the input took %.1fx the time (%v -> %v); %s", ratio, small, large, regression)
}
}
// ExtractMentions stays linear in the body: a backward scan per "@" made
// "/@a" repeated quadratic (TASK-3917 r3 F1; 240 KB took ~10 s).
func TestExtractMentionsLinearOnSlashRuns(t *testing.T) {
assertMentionsLinear(t, 40000, func(n int) string {
return strings.Repeat("/@a", n)
}, "a per-match backward scan is back")
}
// The code-range check walks the spans once per call, not once per match: a
// body of many inline code spans followed by many mentions was quadratic
// (TASK-3917 r4; 360 KB took ~1.1 s, so a 2 MiB comment body ~40 s).
func TestExtractMentionsLinearAfterCodeSpans(t *testing.T) {
body := strings.Repeat("`a` ", 160000) + strings.Repeat("@aaa ", 160000)
start := time.Now()
ExtractMentions(body)
if d := time.Since(start); d > 3*time.Second {
t.Fatalf("ExtractMentions on %d bytes took %v; a per-match range scan is back", len(body), d)
}
assertMentionsLinear(t, 40000, func(n int) string {
return strings.Repeat("`a` ", n) + strings.Repeat("@aaa ", n)
}, "a per-match range scan is back")
}