Files
zzet--gortex/internal/indexer/node_field_intern_test.go
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

274 lines
10 KiB
Go

package indexer
import (
"fmt"
"runtime"
"strings"
"testing"
"unsafe"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/intern"
)
// backingPtr returns the address of a string's backing array so two
// strings can be checked for *sharing storage* rather than merely being
// byte-equal. The empty string has no backing array.
func backingPtr(s string) uintptr {
if len(s) == 0 {
return 0
}
return uintptr(unsafe.Pointer(unsafe.StringData(s)))
}
// freshString returns a heap copy of s with its own backing array, so
// equal values never share storage by accident. It models un-interned
// parse output, where every node holds a separate allocation of the
// same repetitive field value.
func freshString(s string) string {
b := make([]byte, len(s))
copy(b, s)
return string(b)
}
// buildFreshNodes constructs files*perFile nodes (and one edge per node)
// where every repetitive field — file_path, language, kind — is a
// distinct backing array, as if freshly minted by the parser. The file
// path is identical within a file but a separate allocation on each node.
func buildFreshNodes(files, perFile int) ([]*graph.Node, []*graph.Edge) {
nodes := make([]*graph.Node, 0, files*perFile)
edges := make([]*graph.Edge, 0, files*perFile)
for f := 0; f < files; f++ {
path := fmt.Sprintf("internal/some/deeply/nested/pkg%d/source_file_%d.go", f, f)
for k := 0; k < perFile; k++ {
id := fmt.Sprintf("pkg%d/source_file_%d.go::Sym%d", f, f, k)
next := fmt.Sprintf("pkg%d/source_file_%d.go::Sym%d", f, f, (k+1)%perFile)
nodes = append(nodes, &graph.Node{
ID: id,
Kind: graph.NodeKind(freshString("function")),
Name: fmt.Sprintf("Sym%d", k),
FilePath: freshString(path),
Language: freshString("go"),
})
edges = append(edges, &graph.Edge{
From: id,
To: next,
Kind: graph.EdgeCalls,
FilePath: freshString(path),
})
}
}
return nodes, edges
}
// internRepetitiveFields routes only the four repetitive node fields
// through the interner, mirroring what applyRepoPrefix does to them in
// isolation from per-node ID prefixing — used to measure the live-heap
// effect of the field interning without the ID table as a confound.
func internRepetitiveFields(nodes []*graph.Node, edges []*graph.Edge) {
for _, n := range nodes {
n.FilePath = intern.String(n.FilePath)
n.Language = intern.String(n.Language)
n.Kind = graph.NodeKind(intern.String(string(n.Kind)))
}
for _, e := range edges {
e.FilePath = intern.String(e.FilePath)
}
}
// applyRepoPrefixPerNodeBaseline reproduces the pre-cache behaviour:
// it concatenates prefix+FilePath for every node and edge, minting a
// throwaway string per reference. It exists only as the benchmark
// baseline the per-file cache improves on.
func applyRepoPrefixPerNodeBaseline(repoPrefix string, nodes []*graph.Node, edges []*graph.Edge) {
if repoPrefix == "" {
return
}
prefix := repoPrefix + "/"
const unresolvedMarker = "unresolved::"
for _, n := range nodes {
n.ID = intern.String(prefix + n.ID)
n.FilePath = intern.String(prefix + n.FilePath)
n.RepoPrefix = repoPrefix
n.Name = intern.String(n.Name)
n.Language = intern.String(n.Language)
n.Kind = graph.NodeKind(intern.String(string(n.Kind)))
}
for _, e := range edges {
e.From = intern.String(prefix + e.From)
if strings.HasPrefix(e.To, unresolvedMarker) {
e.To = intern.String(e.To)
} else {
e.To = intern.String(prefix + e.To)
}
e.FilePath = intern.String(prefix + e.FilePath)
}
}
// TestApplyRepoPrefix_InternsRepetitiveFields verifies that after the
// node-emit stamping pass the four repetitive fields keep their exact
// values AND that two nodes sharing a value share one backing array —
// proving the interning actually collapses the duplicates.
func TestApplyRepoPrefix_InternsRepetitiveFields(t *testing.T) {
idx := &Indexer{}
idx.SetRepoPrefix("myrepo")
nodes := []*graph.Node{
{ID: "a.go::A", Kind: graph.NodeKind(freshString("function")), Name: "A", FilePath: freshString("pkg/a.go"), Language: freshString("go")},
{ID: "a.go::B", Kind: graph.NodeKind(freshString("function")), Name: "B", FilePath: freshString("pkg/a.go"), Language: freshString("go")},
}
edges := []*graph.Edge{
{From: "a.go::A", To: "a.go::B", Kind: graph.EdgeCalls, FilePath: freshString("pkg/a.go")},
}
// Sanity: the two nodes hold genuinely distinct backing arrays for
// the repetitive fields before interning, so a post-pass match is
// meaningful rather than an accident of allocation.
if backingPtr(nodes[0].FilePath) == backingPtr(nodes[1].FilePath) {
t.Fatal("setup: file paths already share a backing array before interning")
}
if backingPtr(string(nodes[0].Kind)) == backingPtr(string(nodes[1].Kind)) {
t.Fatal("setup: kinds already share a backing array before interning")
}
idx.applyRepoPrefix(nodes, edges)
// Values are byte-identical to the unchanged-behaviour expectation.
const wantPath = "myrepo/pkg/a.go"
wantID := []string{"myrepo/a.go::A", "myrepo/a.go::B"}
for i, n := range nodes {
if n.ID != wantID[i] {
t.Fatalf("node %d ID = %q, want %q", i, n.ID, wantID[i])
}
if n.FilePath != wantPath {
t.Fatalf("node %d FilePath = %q, want %q", i, n.FilePath, wantPath)
}
if string(n.Kind) != "function" {
t.Fatalf("node %d Kind = %q, want %q", i, n.Kind, "function")
}
if n.Language != "go" {
t.Fatalf("node %d Language = %q, want %q", i, n.Language, "go")
}
if n.RepoPrefix != "myrepo" {
t.Fatalf("node %d RepoPrefix = %q, want %q", i, n.RepoPrefix, "myrepo")
}
}
if edges[0].From != "myrepo/a.go::A" || edges[0].To != "myrepo/a.go::B" {
t.Fatalf("edge endpoints = %q -> %q, want myrepo/a.go::A -> myrepo/a.go::B", edges[0].From, edges[0].To)
}
if edges[0].FilePath != wantPath {
t.Fatalf("edge FilePath = %q, want %q", edges[0].FilePath, wantPath)
}
// Identity: equal values now share exactly one backing array.
if backingPtr(nodes[0].FilePath) != backingPtr(nodes[1].FilePath) {
t.Fatal("FilePath not interned: nodes hold distinct backing arrays after the pass")
}
if backingPtr(string(nodes[0].Kind)) != backingPtr(string(nodes[1].Kind)) {
t.Fatal("Kind not interned: nodes hold distinct backing arrays after the pass")
}
if backingPtr(nodes[0].Language) != backingPtr(nodes[1].Language) {
t.Fatal("Language not interned: nodes hold distinct backing arrays after the pass")
}
if backingPtr(nodes[0].RepoPrefix) != backingPtr(nodes[1].RepoPrefix) {
t.Fatal("RepoPrefix not shared: nodes hold distinct backing arrays after the pass")
}
// The edge's file path shares storage with the node's — the per-file
// cache hands both the same interned instance.
if backingPtr(edges[0].FilePath) != backingPtr(nodes[0].FilePath) {
t.Fatal("edge FilePath not interned to the same backing array as the node FilePath")
}
}
// TestApplyRepoPrefix_PerFileCacheMatchesPerNode pins the invariant that
// the per-file cache produces byte-identical results to the per-node
// concatenation it replaces, for both nodes and edges.
func TestApplyRepoPrefix_PerFileCacheMatchesPerNode(t *testing.T) {
cachedNodes, cachedEdges := buildFreshNodes(3, 4)
baseNodes, baseEdges := buildFreshNodes(3, 4)
idx := &Indexer{}
idx.SetRepoPrefix("repo")
idx.applyRepoPrefix(cachedNodes, cachedEdges)
applyRepoPrefixPerNodeBaseline("repo", baseNodes, baseEdges)
if len(cachedNodes) != len(baseNodes) {
t.Fatalf("node count drift: cached=%d base=%d", len(cachedNodes), len(baseNodes))
}
for i := range cachedNodes {
if cachedNodes[i].FilePath != baseNodes[i].FilePath {
t.Fatalf("node %d FilePath: cached=%q base=%q", i, cachedNodes[i].FilePath, baseNodes[i].FilePath)
}
if cachedNodes[i].ID != baseNodes[i].ID {
t.Fatalf("node %d ID: cached=%q base=%q", i, cachedNodes[i].ID, baseNodes[i].ID)
}
if cachedNodes[i].Kind != baseNodes[i].Kind {
t.Fatalf("node %d Kind: cached=%q base=%q", i, cachedNodes[i].Kind, baseNodes[i].Kind)
}
}
for i := range cachedEdges {
if cachedEdges[i].FilePath != baseEdges[i].FilePath {
t.Fatalf("edge %d FilePath: cached=%q base=%q", i, cachedEdges[i].FilePath, baseEdges[i].FilePath)
}
if cachedEdges[i].From != baseEdges[i].From || cachedEdges[i].To != baseEdges[i].To {
t.Fatalf("edge %d endpoints differ", i)
}
}
}
// BenchmarkApplyRepoPrefix_PerFileCache measures the production stamping
// pass; the file path is interned once per file via the cache.
func BenchmarkApplyRepoPrefix_PerFileCache(b *testing.B) {
idx := &Indexer{}
idx.SetRepoPrefix("benchrepo")
benchPrefixPass(b, func(nodes []*graph.Node, edges []*graph.Edge) {
idx.applyRepoPrefix(nodes, edges)
})
}
// BenchmarkApplyRepoPrefix_PerNodeConcat measures the pre-cache baseline
// that concatenates prefix+path for every node and edge.
func BenchmarkApplyRepoPrefix_PerNodeConcat(b *testing.B) {
benchPrefixPass(b, func(nodes []*graph.Node, edges []*graph.Edge) {
applyRepoPrefixPerNodeBaseline("benchrepo", nodes, edges)
})
}
func benchPrefixPass(b *testing.B, apply func([]*graph.Node, []*graph.Edge)) {
const files, perFile = 40, 500
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
nodes, edges := buildFreshNodes(files, perFile)
b.StartTimer()
apply(nodes, edges)
}
}
// BenchmarkNodeFields_LiveHeap_Interned and _NotInterned report the
// resident heap of a 100k-node corpus with vs without interning the
// repetitive fields, so the live-set reduction is directly observable
// as the live-heap-bytes custom metric.
func BenchmarkNodeFields_LiveHeap_Interned(b *testing.B) { benchLiveHeap(b, true) }
func BenchmarkNodeFields_LiveHeap_NotInterned(b *testing.B) { benchLiveHeap(b, false) }
func benchLiveHeap(b *testing.B, interned bool) {
const files, perFile = 50, 2000 // 100k nodes / 100k edges
var lastHeap float64
b.ResetTimer()
for i := 0; i < b.N; i++ {
nodes, edges := buildFreshNodes(files, perFile)
if interned {
internRepetitiveFields(nodes, edges)
}
runtime.GC()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
lastHeap = float64(ms.HeapAlloc)
runtime.KeepAlive(nodes)
runtime.KeepAlive(edges)
}
b.ReportMetric(lastHeap, "live-heap-bytes")
}