chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user