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

150 lines
5.2 KiB
Go

package languages
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
)
const pl1Source = `/* a tiny PL/I-ish program */
MAIN: PROC OPTIONS(MAIN);
PUT LIST('hi');
END MAIN;
HELPER: PROC;
RETURN;
END HELPER;
`
func pl1Spec() config.FallbackChunkerSpec {
return config.FallbackChunkerSpec{
Language: "pl1",
Extensions: []string{".pl1"},
Patterns: []config.ChunkPattern{
{Kind: "function", Regex: `(?m)^\s*([A-Z][A-Z0-9_]*):\s+PROC`, NameGroup: 1},
},
}
}
func TestRegisterFallbackChunkers_RegistersAndExtracts(t *testing.T) {
reg := parser.NewRegistry()
RegisterFallbackChunkers(reg, []config.FallbackChunkerSpec{pl1Spec()}, nil)
ext, ok := reg.GetByLanguage("pl1")
require.True(t, ok)
assert.Equal(t, "pl1", ext.Language())
assert.Equal(t, []string{".pl1"}, ext.Extensions())
res, err := ext.Extract("prog.pl1", []byte(pl1Source))
require.NoError(t, err)
require.NotNil(t, res)
// File node first, then MAIN + HELPER function nodes.
require.Len(t, res.Nodes, 3)
assert.Equal(t, graph.KindFile, res.Nodes[0].Kind)
assert.Equal(t, "prog.pl1", res.Nodes[0].ID)
main := res.Nodes[1]
assert.Equal(t, "prog.pl1::MAIN", main.ID)
assert.Equal(t, graph.KindFunction, main.Kind)
assert.Equal(t, "MAIN", main.Name)
assert.Equal(t, 2, main.StartLine) // line of `MAIN: PROC`
helper := res.Nodes[2]
assert.Equal(t, "prog.pl1::HELPER", helper.ID)
assert.Equal(t, "HELPER", helper.Name)
assert.Equal(t, 6, helper.StartLine) // line of `HELPER: PROC`
// One EdgeDefines per function, from the file node.
require.Len(t, res.Edges, 2)
for _, e := range res.Edges {
assert.Equal(t, "prog.pl1", e.From)
assert.Equal(t, graph.EdgeDefines, e.Kind)
}
assert.Equal(t, "prog.pl1::MAIN", res.Edges[0].To)
assert.Equal(t, "prog.pl1::HELPER", res.Edges[1].To)
}
func TestRegexChunker_DefaultsNameGroupToOne(t *testing.T) {
spec := pl1Spec()
spec.Patterns[0].NameGroup = 0 // unset -> defaults to group 1
reg := parser.NewRegistry()
RegisterFallbackChunkers(reg, []config.FallbackChunkerSpec{spec}, nil)
ext, ok := reg.GetByLanguage("pl1")
require.True(t, ok)
res, err := ext.Extract("p.pl1", []byte(pl1Source))
require.NoError(t, err)
require.Len(t, res.Nodes, 3)
assert.Equal(t, "MAIN", res.Nodes[1].Name)
}
func TestRegexChunker_DedupesByID(t *testing.T) {
// Two patterns matching the same name must yield only one node.
spec := config.FallbackChunkerSpec{
Language: "pl1",
Extensions: []string{".pl1"},
Patterns: []config.ChunkPattern{
{Kind: "function", Regex: `(?m)^\s*([A-Z][A-Z0-9_]*):\s+PROC`},
{Kind: "type", Regex: `(?m)^\s*([A-Z][A-Z0-9_]*):\s+PROC`},
},
}
reg := parser.NewRegistry()
RegisterFallbackChunkers(reg, []config.FallbackChunkerSpec{spec}, nil)
ext, _ := reg.GetByLanguage("pl1")
res, err := ext.Extract("d.pl1", []byte(pl1Source))
require.NoError(t, err)
require.Len(t, res.Nodes, 3) // file + MAIN + HELPER, no duplicates
}
func TestRegisterFallbackChunkers_SkipsInvalidAndCollisions(t *testing.T) {
reg := parser.NewRegistry()
reg.Register(NewGoExtractor()) // claims "go" and ".go"
RegisterFallbackChunkers(reg, []config.FallbackChunkerSpec{
{Language: "", Extensions: []string{".x"}, Patterns: []config.ChunkPattern{{Kind: "function", Regex: "(x)"}}}, // no language
{Language: "x", Extensions: nil, Patterns: []config.ChunkPattern{{Kind: "function", Regex: "(x)"}}}, // no extensions
{Language: "x", Extensions: []string{".x"}, Patterns: nil}, // no patterns
{Language: "go", Extensions: []string{".golang"}, Patterns: []config.ChunkPattern{{Kind: "function", Regex: "(x)"}}}, // language collision
{Language: "y", Extensions: []string{".go"}, Patterns: []config.ChunkPattern{{Kind: "function", Regex: "(x)"}}}, // extension collision
{Language: "bad", Extensions: []string{".b"}, Patterns: []config.ChunkPattern{{Kind: "function", Regex: "([a-z"}}}, // bad regex
{Language: "onlybadkind", Extensions: []string{".k"}, Patterns: []config.ChunkPattern{{Kind: "nope", Regex: "(x)"}}}, // only invalid kind
pl1Spec(), // valid
}, nil)
_, hasPL1 := reg.GetByLanguage("pl1")
assert.True(t, hasPL1)
for _, lang := range []string{"x", "y", "bad", "onlybadkind"} {
_, ok := reg.GetByLanguage(lang)
assert.False(t, ok, lang)
}
// Built-in Go extractor untouched.
e, ok := reg.GetByLanguage("go")
require.True(t, ok)
assert.Equal(t, "go", e.Language())
}
func TestRegexChunker_DropsInvalidKindKeepsValid(t *testing.T) {
spec := config.FallbackChunkerSpec{
Language: "pl1",
Extensions: []string{".pl1"},
Patterns: []config.ChunkPattern{
{Kind: "not_a_kind", Regex: `(?m)^\s*([A-Z][A-Z0-9_]*):\s+PROC`},
{Kind: "function", Regex: `(?m)^\s*([A-Z][A-Z0-9_]*):\s+PROC`},
},
}
reg := parser.NewRegistry()
RegisterFallbackChunkers(reg, []config.FallbackChunkerSpec{spec}, nil)
ext, ok := reg.GetByLanguage("pl1")
require.True(t, ok) // the valid pattern survives, so the spec registers
res, err := ext.Extract("k.pl1", []byte(pl1Source))
require.NoError(t, err)
require.Len(t, res.Nodes, 3)
assert.Equal(t, graph.KindFunction, res.Nodes[1].Kind)
}