Files
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

108 lines
3.2 KiB
Go

package analysis
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
// seedComponentTestGraph builds a hub-and-spoke graph: two SCC
// triangles + one hub every node points at. Gives predictable
// WCC + SCC answers.
func seedComponentTestGraph() *graph.Graph {
g := graph.New()
for _, id := range []string{"a", "b", "c", "d", "e", "f", "hub"} {
g.AddNode(&graph.Node{ID: id, Kind: graph.KindFunction, Name: id, FilePath: id + ".go"})
}
edges := [][2]string{
{"a", "b"}, {"b", "c"}, {"c", "a"}, // triangle 1
{"d", "e"}, {"e", "f"}, {"f", "d"}, // triangle 2
{"c", "d"}, // bridge
{"a", "hub"}, {"b", "hub"}, {"c", "hub"},
{"d", "hub"}, {"e", "hub"}, {"f", "hub"},
}
for _, e := range edges {
g.AddEdge(&graph.Edge{From: e[0], To: e[1], Kind: graph.EdgeCalls, FilePath: "x.go"})
}
return g
}
func TestComputeWCC_OneComponent(t *testing.T) {
g := seedComponentTestGraph()
res := ComputeWCC(g, ComponentOptions{})
require.Len(t, res, 1, "all 7 nodes form one WCC; got %v", res)
assert.Equal(t, 7, res[0].Size)
}
func TestComputeWCC_HonoursEdgeFilter(t *testing.T) {
g := seedComponentTestGraph()
// Filter out the call edges entirely → no surviving edges → every node
// becomes its own singleton component.
res := ComputeWCC(g, ComponentOptions{
EdgeKinds: []graph.EdgeKind{graph.EdgeReferences},
})
assert.Len(t, res, 7,
"with no surviving edges every node should be a singleton; got %v", res)
}
func TestComputeSCC_ThreeComponents(t *testing.T) {
g := seedComponentTestGraph()
res := ComputeSCC(g, ComponentOptions{})
// 7 SCCs: {a,b,c}, {d,e,f}, {hub} (singleton). But the hub is
// trivial — without MinSize, expect 3 with sizes [3, 3, 1].
require.GreaterOrEqual(t, len(res), 3)
bySize := map[int]int{}
for _, r := range res {
bySize[r.Size]++
}
assert.Equal(t, 2, bySize[3], "should find two 3-node SCCs (the triangles); got %v", res)
}
func TestComputeSCC_MinSize_DropsSingletons(t *testing.T) {
g := seedComponentTestGraph()
res := ComputeSCC(g, ComponentOptions{MinSize: 2})
for _, r := range res {
assert.GreaterOrEqual(t, r.Size, 2,
"MinSize=2 should drop singleton SCCs; got %v", r)
}
}
// TestComputeSCC_Iterative_NoStackOverflow constructs a deep
// straight-line graph (1 -> 2 -> 3 -> ... -> N) to make sure the
// iterative Tarjan stays in heap and doesn't blow the goroutine
// call stack. N = 10k; recursive Tarjan would fall over.
func TestComputeSCC_Iterative_NoStackOverflow(t *testing.T) {
const n = 10000
g := graph.New()
for i := 0; i < n; i++ {
id := charID(i)
g.AddNode(&graph.Node{ID: id, Kind: graph.KindFunction, Name: id, FilePath: "x.go"})
}
for i := 0; i < n-1; i++ {
g.AddEdge(&graph.Edge{
From: charID(i), To: charID(i + 1), Kind: graph.EdgeCalls, FilePath: "x.go",
})
}
res := ComputeSCC(g, ComponentOptions{})
// A DAG of N nodes has N singleton SCCs.
assert.Equal(t, n, len(res))
}
func charID(i int) string {
// fmt.Sprintf is fine but we want zero allocs in the loop body — just
// build a deterministic string ID.
const hex = "0123456789abcdef"
out := make([]byte, 0, 8)
for x := i; ; x /= 16 {
out = append([]byte{hex[x%16]}, out...)
if x < 16 {
break
}
}
return "n_" + string(out)
}