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

173 lines
4.5 KiB
Go

package languages
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
// lsExtractGo runs the Go extractor over src and returns the emitted
// nodes, so a test can read the loop-region signals StampLoopSignals
// stamps on a function/method node's Meta.
func lsExtractGo(t *testing.T, src string) []*graph.Node {
t.Helper()
res, err := NewGoExtractor().Extract("signals.go", []byte(src))
if err != nil {
t.Fatalf("extract: %v", err)
}
return res.Nodes
}
func lsNodeByName(t *testing.T, nodes []*graph.Node, name string) *graph.Node {
t.Helper()
for _, n := range nodes {
if n != nil && n.Name == name {
return n
}
}
t.Fatalf("node %q not found", name)
return nil
}
func lsMetaInt(n *graph.Node, key string) (int, bool) {
if n == nil || n.Meta == nil {
return 0, false
}
v, ok := n.Meta[key]
if !ok {
return 0, false
}
switch x := v.(type) {
case int:
return x, true
case int64:
return int(x), true
case float64:
return int(x), true
}
return 0, false
}
func lsMetaBool(n *graph.Node, key string) (val, present bool) {
if n == nil || n.Meta == nil {
return false, false
}
v, ok := n.Meta[key]
if !ok {
return false, false
}
b, _ := v.(bool)
return b, true
}
// TestLoopSignals_MaxAccessDepth pins the documented counting: the deepest
// member-access chain length is the number of identifier segments in it —
// a.b.c.d.e is 5 (four selector hops plus the base operand).
func TestLoopSignals_MaxAccessDepth(t *testing.T) {
nodes := lsExtractGo(t, `package p
func deep() {
_ = a.b.c.d.e
}
`)
got, ok := lsMetaInt(lsNodeByName(t, nodes, "deep"), "max_access_depth")
if !ok {
t.Fatal("max_access_depth not stamped")
}
if got != 5 {
t.Fatalf("max_access_depth = %d, want 5 (segments of a.b.c.d.e)", got)
}
}
// TestLoopSignals_LinearScanInLoop proves loop-region precision for a
// linear-scan call: the same Contains call is flagged inside a loop and
// not flagged outside one.
func TestLoopSignals_LinearScanInLoop(t *testing.T) {
nodes := lsExtractGo(t, `package p
func scanInLoop(xs []int, v int) bool {
for _, x := range xs {
_ = x
if slices.Contains(xs, v) {
return true
}
}
return false
}
func scanNoLoop(xs []int, v int) bool {
return slices.Contains(xs, v)
}
`)
if v, present := lsMetaBool(lsNodeByName(t, nodes, "scanInLoop"), "linear_scan_in_loop"); !present || !v {
t.Fatalf("scanInLoop: linear_scan_in_loop present=%v val=%v, want true", present, v)
}
if _, present := lsMetaBool(lsNodeByName(t, nodes, "scanNoLoop"), "linear_scan_in_loop"); present {
t.Fatal("scanNoLoop: linear_scan_in_loop must not be flagged outside a loop")
}
}
// TestLoopSignals_AllocInLoop proves loop-region precision for an
// allocation: an append inside a loop is flagged; a composite literal and
// append outside a loop are not.
func TestLoopSignals_AllocInLoop(t *testing.T) {
nodes := lsExtractGo(t, `package p
func allocInLoop(n int) []int {
var xs []int
for i := 0; i < n; i++ {
xs = append(xs, i)
}
return xs
}
func allocNoLoop() []int {
xs := []int{}
xs = append(xs, 1)
return xs
}
`)
if v, present := lsMetaBool(lsNodeByName(t, nodes, "allocInLoop"), "alloc_in_loop"); !present || !v {
t.Fatalf("allocInLoop: alloc_in_loop present=%v val=%v, want true", present, v)
}
if _, present := lsMetaBool(lsNodeByName(t, nodes, "allocNoLoop"), "alloc_in_loop"); present {
t.Fatal("allocNoLoop: alloc_in_loop must not be flagged outside a loop")
}
}
// TestLoopSignals_RecursionInLoop proves the self-recursion-in-loop signal
// distinguishes a self-call inside a loop (flagged) from a self-call
// outside a loop and a call to a different function inside a loop (both not
// flagged).
func TestLoopSignals_RecursionInLoop(t *testing.T) {
nodes := lsExtractGo(t, `package p
func recurInLoop(n int) {
for i := 0; i < n; i++ {
recurInLoop(i)
}
}
func recurNoLoop(n int) {
if n > 0 {
recurNoLoop(n - 1)
}
}
func callsOtherInLoop(n int) {
for i := 0; i < n; i++ {
other(i)
}
}
`)
if v, present := lsMetaBool(lsNodeByName(t, nodes, "recurInLoop"), "recursion_in_loop"); !present || !v {
t.Fatalf("recurInLoop: recursion_in_loop present=%v val=%v, want true", present, v)
}
if _, present := lsMetaBool(lsNodeByName(t, nodes, "recurNoLoop"), "recursion_in_loop"); present {
t.Fatal("recurNoLoop: recursion_in_loop must not be flagged outside a loop")
}
if _, present := lsMetaBool(lsNodeByName(t, nodes, "callsOtherInLoop"), "recursion_in_loop"); present {
t.Fatal("callsOtherInLoop: a call to a different function must not be flagged as recursion")
}
}