package resolver import ( "fmt" "testing" "github.com/zzet/gortex/internal/graph" ) // buildResolverGraph creates a graph with unresolved edges for benchmarking. func buildResolverGraph(files, symsPerFile int) (graph.Store, *Resolver) { g := graph.New() // Create file nodes with functions, types, and methods. for f := range files { filePath := fmt.Sprintf("pkg%d/file%d.go", f/10, f) pkg := fmt.Sprintf("pkg%d", f/10) // Add a type per file. typeName := fmt.Sprintf("Type%d", f) g.AddNode(&graph.Node{ ID: fmt.Sprintf("%s::%s", filePath, typeName), Kind: graph.KindType, Name: typeName, QualName: fmt.Sprintf("%s.%s", pkg, typeName), FilePath: filePath, Language: "go", Meta: map[string]any{"receiver_type": typeName}, }) for s := range symsPerFile { funcName := fmt.Sprintf("Func%d_%d", f, s) nodeID := fmt.Sprintf("%s::%s", filePath, funcName) g.AddNode(&graph.Node{ ID: nodeID, Kind: graph.KindFunction, Name: funcName, QualName: fmt.Sprintf("%s.%s", pkg, funcName), FilePath: filePath, Language: "go", }) // Add unresolved call edges to functions in other files. targetFile := (f + 1) % files targetFunc := fmt.Sprintf("Func%d_%d", targetFile, s%symsPerFile) g.AddEdge(&graph.Edge{ From: nodeID, To: "unresolved::" + targetFunc, Kind: graph.EdgeCalls, FilePath: filePath, }) } } return g, New(g) } func BenchmarkResolver_ResolveAll(b *testing.B) { sizes := []struct { name string files int symsPerFile int }{ {"Small_50files", 50, 5}, {"Medium_200files", 200, 10}, {"Large_500files", 500, 10}, } for _, sz := range sizes { b.Run(sz.name, func(b *testing.B) { b.ReportAllocs() for b.Loop() { b.StopTimer() _, r := buildResolverGraph(sz.files, sz.symsPerFile) b.StartTimer() r.ResolveAll() } }) } } func BenchmarkResolver_ResolveFile(b *testing.B) { _, r := buildResolverGraph(200, 10) b.ReportAllocs() b.ResetTimer() for i := range b.N { r.ResolveFile(fmt.Sprintf("pkg%d/file%d.go", (i%200)/10, i%200)) } } func BenchmarkResolver_InferImplements(b *testing.B) { g := graph.New() // Create interfaces and implementing types. for i := range 20 { ifaceName := fmt.Sprintf("Interface%d", i) g.AddNode(&graph.Node{ ID: fmt.Sprintf("pkg/iface.go::%s", ifaceName), Kind: graph.KindInterface, Name: ifaceName, FilePath: "pkg/iface.go", Language: "go", Meta: map[string]any{ "methods": []string{fmt.Sprintf("Method%d", i), fmt.Sprintf("Other%d", i)}, }, }) // 5 types implement each interface. for j := range 5 { typeName := fmt.Sprintf("Impl%d_%d", i, j) filePath := fmt.Sprintf("pkg/impl%d.go", j) g.AddNode(&graph.Node{ ID: fmt.Sprintf("%s::%s", filePath, typeName), Kind: graph.KindType, Name: typeName, FilePath: filePath, Language: "go", Meta: map[string]any{"receiver_type": typeName}, }) // Add methods matching the interface. for _, mName := range []string{fmt.Sprintf("Method%d", i), fmt.Sprintf("Other%d", i)} { methodID := fmt.Sprintf("%s::%s.%s", filePath, typeName, mName) g.AddNode(&graph.Node{ ID: methodID, Kind: graph.KindMethod, Name: mName, FilePath: filePath, Language: "go", Meta: map[string]any{"receiver_type": typeName}, }) } } } r := New(g) b.ReportAllocs() b.ResetTimer() for b.Loop() { r.InferImplements() } }