a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
81 lines
2.9 KiB
Go
81 lines
2.9 KiB
Go
package mcp
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"pgregory.net/rapid"
|
|
)
|
|
|
|
// TestFindEOLMatches_Property generates haystacks with randomly mixed
|
|
// CRLF/LF terminators, derives a needle from a random slice of the
|
|
// normalized haystack (so a tolerant match must exist), re-terminates it
|
|
// with a random EOL flavor, and checks the matcher's core invariants:
|
|
//
|
|
// 1. at least one match is found;
|
|
// 2. every span's bytes normalize to the normalized needle (the span
|
|
// covers exactly the on-disk text the caller asked for, CRs included);
|
|
// 3. spans are in-bounds, sorted, and non-overlapping;
|
|
// 4. replacing every span with the needle itself is a normalized no-op
|
|
// (splice offsets land on real bytes, terminators adapt per region).
|
|
func TestFindEOLMatches_Property(t *testing.T) {
|
|
rapid.Check(t, func(rt *rapid.T) {
|
|
nLines := rapid.IntRange(1, 8).Draw(rt, "nLines")
|
|
var hayB strings.Builder
|
|
for i := 0; i < nLines; i++ {
|
|
// Line content occasionally carries a lone CR — always followed
|
|
// by a letter so it stays lone (a trailing bare CR would fuse
|
|
// with an LF terminator into CRLF, and a needle derived from
|
|
// the once-normalized haystack would then correctly NOT match
|
|
// the \r\r\n bytes). normalizeCRLF must pass lone CRs through
|
|
// and the span/splice invariants must still hold around them.
|
|
hayB.WriteString(rapid.StringMatching(`[a-c]{0,3}(\r[a-c]{1,2})?`).Draw(rt, "line"))
|
|
hayB.WriteString(rapid.SampledFrom([]string{"\n", "\r\n"}).Draw(rt, "eol"))
|
|
}
|
|
hay := hayB.String()
|
|
|
|
normHay, _ := normalizeCRLF(hay)
|
|
start := rapid.IntRange(0, len(normHay)-1).Draw(rt, "start")
|
|
end := rapid.IntRange(start+1, len(normHay)).Draw(rt, "end")
|
|
needleNorm := normHay[start:end]
|
|
needle := needleNorm
|
|
if rapid.Bool().Draw(rt, "crlfNeedle") {
|
|
needle = strings.ReplaceAll(needleNorm, "\n", "\r\n")
|
|
}
|
|
|
|
m := findEOLMatches(hay, needle)
|
|
if m.count == 0 {
|
|
rt.Fatalf("needle %q derived from hay %q must match", needle, hay)
|
|
}
|
|
if len(m.spans) != m.count {
|
|
rt.Fatalf("count %d != len(spans) %d", m.count, len(m.spans))
|
|
}
|
|
|
|
prevEnd := 0
|
|
for i, sp := range m.spans {
|
|
if sp.start < 0 || sp.end > len(hay) || sp.start >= sp.end {
|
|
rt.Fatalf("span %d out of bounds: %+v (hay len %d)", i, sp, len(hay))
|
|
}
|
|
if sp.start < prevEnd {
|
|
rt.Fatalf("span %d overlaps previous (start %d < prev end %d)", i, sp.start, prevEnd)
|
|
}
|
|
prevEnd = sp.end
|
|
gotNorm, _ := normalizeCRLF(hay[sp.start:sp.end])
|
|
if gotNorm != needleNorm {
|
|
rt.Fatalf("span %d bytes %q normalize to %q, want %q",
|
|
i, hay[sp.start:sp.end], gotNorm, needleNorm)
|
|
}
|
|
}
|
|
if !sort.SliceIsSorted(m.spans, func(i, j int) bool { return m.spans[i].start < m.spans[j].start }) {
|
|
rt.Fatalf("spans not sorted: %+v", m.spans)
|
|
}
|
|
|
|
out := spliceSpansEOL(hay, m.spans, needleNorm, -1)
|
|
outNorm, _ := normalizeCRLF(out)
|
|
if outNorm != normHay {
|
|
rt.Fatalf("identity replacement changed normalized content:\nhay %q\nout %q", hay, out)
|
|
}
|
|
})
|
|
}
|