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
198 lines
4.8 KiB
Go
198 lines
4.8 KiB
Go
package languages
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
func runSwiftExtract(t *testing.T, path, src string) ([]*graph.Node, []*graph.Edge) {
|
|
t.Helper()
|
|
ext := NewSwiftExtractor()
|
|
result, err := ext.Extract(path, []byte(src))
|
|
if err != nil {
|
|
t.Fatalf("extract: %v", err)
|
|
}
|
|
return result.Nodes, result.Edges
|
|
}
|
|
|
|
func swiftAnnotationTargets(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 TestSwiftAnnotations_ClassObjc(t *testing.T) {
|
|
src := `import Foundation
|
|
@objc
|
|
class MyViewController: NSObject {
|
|
}
|
|
`
|
|
_, edges := runSwiftExtract(t, "src/Vc.swift", src)
|
|
pairs := swiftAnnotationTargets(edges)
|
|
got := pairs["src/Vc.swift::MyViewController"]
|
|
if len(got) != 1 || got[0] != "annotation::swift::objc" {
|
|
t.Errorf("expected @objc on MyViewController, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestSwiftAnnotations_MultipleOnClass(t *testing.T) {
|
|
src := `import Foundation
|
|
@objc
|
|
@objcMembers
|
|
class Wrapper: NSObject {
|
|
}
|
|
`
|
|
_, edges := runSwiftExtract(t, "src/W.swift", src)
|
|
pairs := swiftAnnotationTargets(edges)
|
|
got := pairs["src/W.swift::Wrapper"]
|
|
if len(got) != 2 {
|
|
t.Fatalf("expected 2 annotations on Wrapper, got %d: %v", len(got), got)
|
|
}
|
|
set := map[string]bool{}
|
|
for _, to := range got {
|
|
set[to] = true
|
|
}
|
|
if !set["annotation::swift::objc"] || !set["annotation::swift::objcMembers"] {
|
|
t.Errorf("missing expected annotations: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestSwiftAnnotations_FunctionAttribute(t *testing.T) {
|
|
src := `@available(iOS 13.0, *)
|
|
func render() {
|
|
}
|
|
`
|
|
_, edges := runSwiftExtract(t, "src/r.swift", src)
|
|
pairs := swiftAnnotationTargets(edges)
|
|
got := pairs["src/r.swift::render"]
|
|
if len(got) != 1 || got[0] != "annotation::swift::available" {
|
|
t.Errorf("expected @available on render, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestSwiftAnnotations_MethodInsideClass(t *testing.T) {
|
|
src := `import Foundation
|
|
class Vm: NSObject {
|
|
@MainActor
|
|
func update() async {
|
|
}
|
|
}
|
|
`
|
|
_, edges := runSwiftExtract(t, "src/Vm.swift", src)
|
|
pairs := swiftAnnotationTargets(edges)
|
|
got := pairs["src/Vm.swift::Vm.update"]
|
|
if len(got) != 1 || got[0] != "annotation::swift::MainActor" {
|
|
t.Errorf("expected @MainActor on Vm.update, got %v", got)
|
|
}
|
|
// Class itself must NOT inherit method's annotation.
|
|
classGot := pairs["src/Vm.swift::Vm"]
|
|
for _, to := range classGot {
|
|
if to == "annotation::swift::MainActor" {
|
|
t.Errorf("class Vm should NOT see method's @MainActor")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSwiftAnnotations_SyntheticNodeDedupedAcrossUses(t *testing.T) {
|
|
src := `import Foundation
|
|
@objc class A: NSObject {}
|
|
@objc class B: NSObject {}
|
|
@objc class C: NSObject {}
|
|
`
|
|
nodes, _ := runSwiftExtract(t, "src/many.swift", src)
|
|
count := 0
|
|
for _, n := range nodes {
|
|
if n.ID == "annotation::swift::objc" {
|
|
count++
|
|
}
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("expected single synthetic annotation node, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestSwiftAnnotations_NoModifiers_NoEdges(t *testing.T) {
|
|
src := `import Foundation
|
|
class Plain: NSObject {
|
|
func x() {
|
|
}
|
|
}
|
|
`
|
|
_, edges := runSwiftExtract(t, "src/Plain.swift", src)
|
|
for _, e := range edges {
|
|
if e.Kind == graph.EdgeAnnotated {
|
|
t.Errorf("plain class/method must not emit EdgeAnnotated: %+v", e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSwift_ObjCMembersPropagation(t *testing.T) {
|
|
src := `@objcMembers
|
|
class C {
|
|
func a() {}
|
|
@nonobjc func b() {}
|
|
func move(from x: Int, to y: Int) {}
|
|
var title: String = ""
|
|
@nonobjc var secret: String = ""
|
|
}
|
|
`
|
|
nodes, _ := runSwiftExtract(t, "C.swift", src)
|
|
sel := map[string]any{}
|
|
for _, n := range nodes {
|
|
if n.Meta == nil {
|
|
continue
|
|
}
|
|
if s, ok := n.Meta["objc_selector"]; ok {
|
|
sel[n.Name] = s
|
|
}
|
|
}
|
|
if sel["a"] != "a" {
|
|
t.Errorf("@objcMembers should expose a with selector a, got %v", sel["a"])
|
|
}
|
|
if sel["move"] != "moveFrom:to:" {
|
|
t.Errorf("@objcMembers should derive move(from:to:) selector moveFrom:to:, got %v", sel["move"])
|
|
}
|
|
if sel["title"] != "title" {
|
|
t.Errorf("@objcMembers should expose property title, got %v", sel["title"])
|
|
}
|
|
if _, ok := sel["b"]; ok {
|
|
t.Errorf("@nonobjc func b must not be exposed, got selector %v", sel["b"])
|
|
}
|
|
if _, ok := sel["secret"]; ok {
|
|
t.Errorf("@nonobjc var secret must not be exposed, got selector %v", sel["secret"])
|
|
}
|
|
}
|
|
|
|
func TestSwift_ObjCProtocolFlag(t *testing.T) {
|
|
src := `@objc protocol Drawable {
|
|
func draw()
|
|
}
|
|
protocol Plain {
|
|
func x()
|
|
}
|
|
`
|
|
nodes, _ := runSwiftExtract(t, "P.swift", src)
|
|
flag := map[string]any{}
|
|
for _, n := range nodes {
|
|
if n.Kind == graph.KindInterface {
|
|
if n.Meta != nil {
|
|
flag[n.Name] = n.Meta["objc"]
|
|
} else {
|
|
flag[n.Name] = nil
|
|
}
|
|
}
|
|
}
|
|
if flag["Drawable"] != true {
|
|
t.Errorf("@objc protocol Drawable should carry Meta[objc]=true, got %v", flag["Drawable"])
|
|
}
|
|
if flag["Plain"] == true {
|
|
t.Errorf("plain protocol Plain must not carry Meta[objc]=true")
|
|
}
|
|
}
|