Files
go-worm/lib/helpers/callback_once_test.go
T

77 lines
1.5 KiB
Go
Raw Normal View History

2026-09-02 00:00:24 +03:00
package helpers
import (
"go/ast"
"go/parser"
"go/token"
"io/fs"
"path/filepath"
"strings"
"testing"
)
// Go never frees syscall.NewCallback (hard cap ~2000). Creating one per
// screenshot frame is a fatal "too many callback functions".
func TestNewCallbackOnlyAtPackageScope(t *testing.T) {
root := filepath.Join("..", "..")
fset := token.NewFileSet()
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
name := d.Name()
if name == ".git" || name == "vendor" || name == "testdata" {
return filepath.SkipDir
}
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
file, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
t.Errorf("parse %s: %v", path, err)
return nil
}
for _, decl := range file.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
ast.Inspect(fn, func(n ast.Node) bool {
if !isNewCallbackCall(n) {
return true
}
name := fn.Name.Name
if fn.Recv != nil {
name = "method " + name
}
t.Errorf("%s: syscall.NewCallback inside %s; Go never frees these (cap ~2000)", fset.Position(n.Pos()), name)
return true
})
}
return nil
})
if err != nil {
t.Fatal(err)
}
}
func isNewCallbackCall(n ast.Node) bool {
call, ok := n.(*ast.CallExpr)
if !ok {
return false
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return false
}
switch sel.Sel.Name {
case "NewCallback", "NewCallbackCDecl":
return true
default:
return false
}
}