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

164 lines
4.5 KiB
Go

package forest
import (
"testing"
"unsafe"
"github.com/alexaandru/go-sitter-forest/elm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
func TestExtractor_TagsPath(t *testing.T) {
// Elm ships a tags.scm — exercises the tags-driven path.
e := New("elm", []string{".elm"}, elm.GetLanguage, elm.GetQuery)
require.Equal(t, "elm", e.Language())
require.Equal(t, []string{".elm"}, e.Extensions())
src := []byte(`module M exposing (..)
type Color = Red | Green | Blue
double : Int -> Int
double n = n * 2
`)
res, err := e.Extract("M.elm", src)
require.NoError(t, err)
require.NotEmpty(t, res.Nodes)
gotFile := false
gotDouble := false
gotColor := false
for _, n := range res.Nodes {
switch n.Kind {
case graph.KindFile:
gotFile = n.FilePath == "M.elm"
case graph.KindFunction:
if n.Name == "double" {
gotDouble = true
}
case graph.KindType:
if n.Name == "Color" {
gotColor = true
}
}
}
assert.True(t, gotFile, "missing file node")
assert.True(t, gotDouble, "missing `double` function")
assert.True(t, gotColor, "missing `Color` type")
}
func TestExtractor_ParseError(t *testing.T) {
// Garbage input still returns a tree (forest grammars produce a
// best-effort tree with ERROR nodes), so Extract should succeed
// with at least the file node and not panic.
e := New("elm", []string{".elm"}, elm.GetLanguage, elm.GetQuery)
res, err := e.Extract("bad.elm", []byte("@@@@@@@@@"))
require.NoError(t, err)
require.NotEmpty(t, res.Nodes)
assert.Equal(t, graph.KindFile, res.Nodes[0].Kind)
}
func TestExtractor_CallEdgesParentToEnclosingFunc(t *testing.T) {
// Two functions; calls inside `caller`'s body should land on
// `M.elm::caller`, not on the file. Confirms the second-pass
// attribution from buildFuncRanges/findEnclosingFunc.
//
// Note: elm's tags.scm also captures type-annotation references
// (`caller : Int -> Int`) as @reference.function — those land
// outside any function range and parent to the file, which is
// the documented module-level fallback. We only assert the
// in-body case here.
e := New("elm", []string{".elm"}, elm.GetLanguage, elm.GetQuery)
src := []byte(`module M exposing (..)
helper : Int -> Int
helper n = n + 1
caller : Int -> Int
caller x =
helper (helper x)
`)
res, err := e.Extract("M.elm", src)
require.NoError(t, err)
bodyCallToHelper := false
for _, ed := range res.Edges {
if ed.Kind != graph.EdgeCalls {
continue
}
if ed.To == "unresolved::helper" && ed.From == "M.elm::caller" {
bodyCallToHelper = true
break
}
}
assert.True(t, bodyCallToHelper,
"expected a call edge from M.elm::caller to unresolved::helper; got %+v",
callEdgeSummary(res.Edges))
}
// callEdgeSummary builds a compact list of (from, to) pairs for
// every EdgeCalls edge — only used when an assertion fails to give
// the failure message something diagnostic to chew on.
func callEdgeSummary(edges []*graph.Edge) []string {
var out []string
for _, ed := range edges {
if ed.Kind == graph.EdgeCalls {
out = append(out, ed.From+" -> "+ed.To)
}
}
return out
}
func TestExtractor_ModuleLevelCallsParentToFile(t *testing.T) {
// A call at module top-level (no enclosing function) should
// fall back to the file node — better than dropping the edge.
e := New("elm", []string{".elm"}, elm.GetLanguage, elm.GetQuery)
src := []byte(`module M exposing (..)
import X
result =
foo bar
`)
res, err := e.Extract("M.elm", src)
require.NoError(t, err)
// `result = foo bar` is a value declaration, treated as a function
// in elm tags.scm — so its calls would parent to it. We pick a
// file-level expression test that's harder: the assignment itself
// is the def, but if any @reference.call is captured outside any
// function, it should land on the file.
for _, ed := range res.Edges {
if ed.Kind == graph.EdgeCalls {
assert.True(t,
ed.From == "M.elm" || ed.From == "M.elm::result",
"unexpected call parent %q", ed.From)
}
}
}
func TestExtractor_NilGetLanguage(t *testing.T) {
// Sticky-error path: a grammar that returns a nil pointer fails
// the first call and every subsequent call (cached error)
// without panicking.
e := New("broken", []string{".x"}, func() unsafe.Pointer { return nil }, nil)
_, err := e.Extract("a.x", []byte("anything"))
require.Error(t, err)
require.ErrorIs(t, err, errNilLanguage)
// Sticky cache — second call returns same error without
// re-invoking the grammar.
_, err2 := e.Extract("b.x", []byte("again"))
require.ErrorIs(t, err2, errNilLanguage)
}