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,167 @@
|
||||
package languages
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/zzet/gortex/internal/graph"
|
||||
)
|
||||
|
||||
func runScalaExtract(t *testing.T, path, src string) ([]*graph.Node, []*graph.Edge) {
|
||||
t.Helper()
|
||||
ext := NewScalaExtractor()
|
||||
result, err := ext.Extract(path, []byte(src))
|
||||
if err != nil {
|
||||
t.Fatalf("extract: %v", err)
|
||||
}
|
||||
return result.Nodes, result.Edges
|
||||
}
|
||||
|
||||
// scalaAnnotationTargets returns the (from, to) pairs of EdgeAnnotated
|
||||
// edges, keyed by from-symbol-ID, so tests can assert "this declaration
|
||||
// is annotated with these names".
|
||||
func scalaAnnotationTargets(edges []*graph.Edge) map[string][]string {
|
||||
out := map[string][]string{}
|
||||
for _, e := range edges {
|
||||
if e.Kind != graph.EdgeAnnotated {
|
||||
continue
|
||||
}
|
||||
out[e.From] = append(out[e.From], e.To)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_ClassWithMultiple(t *testing.T) {
|
||||
src := `@deprecated("use newF", "1.5")
|
||||
@inline
|
||||
class Foo {
|
||||
def bar(n: Int): Int = n
|
||||
}
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Foo.scala", src)
|
||||
pairs := scalaAnnotationTargets(edges)
|
||||
got := pairs["src/Foo.scala::Foo"]
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected 2 annotations on Foo, got %d: %v", len(got), got)
|
||||
}
|
||||
wantSet := map[string]bool{
|
||||
"annotation::scala::deprecated": true,
|
||||
"annotation::scala::inline": true,
|
||||
}
|
||||
for _, to := range got {
|
||||
if !wantSet[to] {
|
||||
t.Errorf("unexpected annotation target: %s", to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_PreservesArgs(t *testing.T) {
|
||||
src := `@deprecated("use newF", "1.5")
|
||||
class Foo
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Foo.scala", src)
|
||||
for _, e := range edges {
|
||||
if e.Kind != graph.EdgeAnnotated {
|
||||
continue
|
||||
}
|
||||
if e.To != "annotation::scala::deprecated" {
|
||||
continue
|
||||
}
|
||||
args, _ := e.Meta["args"].(string)
|
||||
if !strings.Contains(args, "use newF") {
|
||||
t.Errorf("args should preserve verbatim text, got %q", args)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatal("no EdgeAnnotated emitted for @deprecated")
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_MethodInsideClass(t *testing.T) {
|
||||
src := `class Foo {
|
||||
@tailrec
|
||||
def bar(n: Int): Int = if (n == 0) 0 else bar(n - 1)
|
||||
}
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Foo.scala", src)
|
||||
pairs := scalaAnnotationTargets(edges)
|
||||
got := pairs["src/Foo.scala::Foo.bar"]
|
||||
if len(got) != 1 || got[0] != "annotation::scala::tailrec" {
|
||||
t.Errorf("method bar should have @tailrec annotation, got %v", got)
|
||||
}
|
||||
// The class itself must NOT inherit the method's annotation.
|
||||
classGot := pairs["src/Foo.scala::Foo"]
|
||||
for _, to := range classGot {
|
||||
if to == "annotation::scala::tailrec" {
|
||||
t.Errorf("class Foo should NOT see method's @tailrec annotation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_TopLevelFunction(t *testing.T) {
|
||||
src := `@main def app(): Unit = println("hi")
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Main.scala", src)
|
||||
pairs := scalaAnnotationTargets(edges)
|
||||
got := pairs["src/Main.scala::app"]
|
||||
if len(got) != 1 || got[0] != "annotation::scala::main" {
|
||||
t.Errorf("top-level app should have @main annotation, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_TraitWithAnnotation(t *testing.T) {
|
||||
src := `@deprecated("use NewTrait", "2.0")
|
||||
trait OldTrait {
|
||||
def foo(): Int
|
||||
}
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Old.scala", src)
|
||||
pairs := scalaAnnotationTargets(edges)
|
||||
got := pairs["src/Old.scala::OldTrait"]
|
||||
if len(got) != 1 || got[0] != "annotation::scala::deprecated" {
|
||||
t.Errorf("trait OldTrait should have @deprecated, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_ObjectWithAnnotation(t *testing.T) {
|
||||
src := `@SerialVersionUID(1L)
|
||||
object MyObj {
|
||||
def thing(): Int = 1
|
||||
}
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Obj.scala", src)
|
||||
pairs := scalaAnnotationTargets(edges)
|
||||
got := pairs["src/Obj.scala::MyObj"]
|
||||
if len(got) != 1 || got[0] != "annotation::scala::SerialVersionUID" {
|
||||
t.Errorf("object MyObj should have @SerialVersionUID, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_SyntheticNodeDedupedAcrossUses(t *testing.T) {
|
||||
src := `@inline class A
|
||||
@inline class B
|
||||
@inline class C
|
||||
`
|
||||
nodes, _ := runScalaExtract(t, "src/many.scala", src)
|
||||
count := 0
|
||||
for _, n := range nodes {
|
||||
if n.ID == "annotation::scala::inline" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("expected single synthetic annotation node, got %d copies", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalaAnnotations_NoAnnotationsLeavesGraphCleanly(t *testing.T) {
|
||||
src := `class Plain {
|
||||
def x(): Int = 1
|
||||
}
|
||||
`
|
||||
_, edges := runScalaExtract(t, "src/Plain.scala", src)
|
||||
for _, e := range edges {
|
||||
if e.Kind == graph.EdgeAnnotated {
|
||||
t.Errorf("unannotated class should NOT emit EdgeAnnotated, got: %+v", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user