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
122 lines
3.8 KiB
Go
122 lines
3.8 KiB
Go
package languages
|
|
|
|
import "testing"
|
|
|
|
// countByName returns how many extracted nodes carry the given name, and the
|
|
// set of their (distinct) IDs.
|
|
func countByName(nodes []*nodeForCount, name string) (int, []string) {
|
|
var ids []string
|
|
for _, n := range nodes {
|
|
if n.name == name {
|
|
ids = append(ids, n.id)
|
|
}
|
|
}
|
|
return len(ids), ids
|
|
}
|
|
|
|
type nodeForCount struct {
|
|
id string
|
|
name string
|
|
}
|
|
|
|
// TestDisambiguateID exercises the helper directly: first use keeps the base
|
|
// ID, a genuine collision on a different line gains a line suffix, and an exact
|
|
// re-match of the same declaration is reported as a drop.
|
|
func TestDisambiguateID(t *testing.T) {
|
|
seen := map[string]bool{}
|
|
|
|
id1, ok1 := disambiguateID(seen, "f.go::Foo", 10)
|
|
if id1 != "f.go::Foo" || !ok1 {
|
|
t.Fatalf("first use: got (%q,%v) want (f.go::Foo,true)", id1, ok1)
|
|
}
|
|
id2, ok2 := disambiguateID(seen, "f.go::Foo", 20)
|
|
if id2 != "f.go::Foo_L20" || !ok2 {
|
|
t.Fatalf("collision on a different line: got (%q,%v) want (f.go::Foo_L20,true)", id2, ok2)
|
|
}
|
|
if _, ok3 := disambiguateID(seen, "f.go::Foo", 10); ok3 {
|
|
t.Fatalf("exact re-match (same id + line) must be dropped, got ok=true")
|
|
}
|
|
id4, ok4 := disambiguateID(seen, "f.go::Foo", 30)
|
|
if id4 != "f.go::Foo_L30" || !ok4 {
|
|
t.Fatalf("third collision: got (%q,%v) want (f.go::Foo_L30,true)", id4, ok4)
|
|
}
|
|
// A different base ID is unaffected.
|
|
id5, ok5 := disambiguateID(seen, "f.go::Bar", 5)
|
|
if id5 != "f.go::Bar" || !ok5 {
|
|
t.Fatalf("unrelated base: got (%q,%v) want (f.go::Bar,true)", id5, ok5)
|
|
}
|
|
}
|
|
|
|
// TestInitCollisionSurvive proves the Go extractor no longer drops a second
|
|
// func init(): both survive as distinct nodes, the collider gaining a line
|
|
// suffix, instead of one silently overwriting the other in the graph.
|
|
func TestInitCollisionSurvive(t *testing.T) {
|
|
src := []byte("package p\n" +
|
|
"func init() { a() }\n" +
|
|
"func init() { b() }\n" +
|
|
"func a() {}\n" +
|
|
"func b() {}\n")
|
|
res, err := NewGoExtractor().Extract("p.go", src)
|
|
if err != nil {
|
|
t.Fatalf("extract: %v", err)
|
|
}
|
|
var nodes []*nodeForCount
|
|
for _, n := range res.Nodes {
|
|
nodes = append(nodes, &nodeForCount{id: n.ID, name: n.Name})
|
|
}
|
|
n, ids := countByName(nodes, "init")
|
|
if n != 2 {
|
|
t.Fatalf("expected 2 surviving init nodes, got %d (%v)", n, ids)
|
|
}
|
|
if ids[0] == ids[1] {
|
|
t.Fatalf("the two init nodes share an ID %q — one would overwrite the other", ids[0])
|
|
}
|
|
}
|
|
|
|
// TestOverloadSurvive proves redefinition/overload sets are no longer collapsed
|
|
// to a single node across the dynamic-language extractors: a Python @overload
|
|
// triple and a Ruby method redeclared in one class both keep every definition.
|
|
func TestOverloadSurvive(t *testing.T) {
|
|
t.Run("python_overload", func(t *testing.T) {
|
|
src := []byte("from typing import overload\n" +
|
|
"@overload\n" +
|
|
"def f(x: int) -> int: ...\n" +
|
|
"@overload\n" +
|
|
"def f(x: str) -> str: ...\n" +
|
|
"def f(x): return x\n")
|
|
res, err := NewPythonExtractor().Extract("m.py", src)
|
|
if err != nil {
|
|
t.Fatalf("extract: %v", err)
|
|
}
|
|
var nodes []*nodeForCount
|
|
for _, n := range res.Nodes {
|
|
nodes = append(nodes, &nodeForCount{id: n.ID, name: n.Name})
|
|
}
|
|
if n, ids := countByName(nodes, "f"); n != 3 {
|
|
t.Fatalf("expected 3 surviving overloads of f, got %d (%v)", n, ids)
|
|
}
|
|
})
|
|
|
|
t.Run("ruby_redefinition", func(t *testing.T) {
|
|
src := []byte("class Greeter\n" +
|
|
" def greet\n" +
|
|
" 'hi'\n" +
|
|
" end\n" +
|
|
" def greet\n" +
|
|
" 'hello'\n" +
|
|
" end\n" +
|
|
"end\n")
|
|
res, err := NewRubyExtractor().Extract("g.rb", src)
|
|
if err != nil {
|
|
t.Fatalf("extract: %v", err)
|
|
}
|
|
var nodes []*nodeForCount
|
|
for _, n := range res.Nodes {
|
|
nodes = append(nodes, &nodeForCount{id: n.ID, name: n.Name})
|
|
}
|
|
if n, ids := countByName(nodes, "greet"); n != 2 {
|
|
t.Fatalf("expected 2 surviving greet definitions, got %d (%v)", n, ids)
|
|
}
|
|
})
|
|
}
|