// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //lint:file-ignore SA1019 go/callgraph's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream. // No testdata on Android. // +build !android package rta_test import ( "bytes" "fmt" "go/ast" "go/parser" "go/token" "go/types" "io/ioutil" "sort" "strings" "testing" "golang.org/x/tools/go/loader" "honnef.co/go/tools/callgraph" "honnef.co/go/tools/callgraph/rta" "honnef.co/go/tools/ir" "honnef.co/go/tools/ir/irutil" ) var inputs = []string{ "testdata/func.go", "testdata/rtype.go", "testdata/iface.go", } func expectation(f *ast.File) (string, token.Pos) { for _, c := range f.Comments { text := strings.TrimSpace(c.Text()) if t := strings.TrimPrefix(text, "WANT:\n"); t != text { return t, c.Pos() } } return "", token.NoPos } // TestRTA runs RTA on each file in inputs, prints the results, and // compares it with the golden results embedded in the WANT comment at // the end of the file. // // The results string consists of two parts: the set of dynamic call // edges, "f --> g", one per line, and the set of reachable functions, // one per line. Each set is sorted. // func TestRTA(t *testing.T) { for _, filename := range inputs { content, err := ioutil.ReadFile(filename) if err != nil { t.Errorf("couldn't read file '%s': %s", filename, err) continue } conf := loader.Config{ ParserMode: parser.ParseComments, } f, err := conf.ParseFile(filename, content) if err != nil { t.Error(err) continue } want, pos := expectation(f) if pos == token.NoPos { t.Errorf("No WANT: comment in %s", filename) continue } conf.CreateFromFiles("main", f) iprog, err := conf.Load() if err != nil { t.Error(err) continue } prog := irutil.CreateProgram(iprog, 0) mainPkg := prog.Package(iprog.Created[0].Pkg) prog.Build() res := rta.Analyze([]*ir.Function{ mainPkg.Func("main"), mainPkg.Func("init"), }, true) if got := printResult(res, mainPkg.Pkg); got != want { t.Errorf("%s: got:\n%s\nwant:\n%s", prog.Fset.Position(pos), got, want) } } } func printResult(res *rta.Result, from *types.Package) string { var buf bytes.Buffer writeSorted := func(ss []string) { sort.Strings(ss) for _, s := range ss { fmt.Fprintf(&buf, " %s\n", s) } } buf.WriteString("Dynamic calls\n") var edges []string callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error { if strings.Contains(e.Description(), "dynamic") { edges = append(edges, fmt.Sprintf("%s --> %s", e.Caller.Func.RelString(from), e.Callee.Func.RelString(from))) } return nil }) writeSorted(edges) buf.WriteString("Reachable functions\n") var reachable []string for f := range res.Reachable { reachable = append(reachable, f.RelString(from)) } writeSorted(reachable) buf.WriteString("Reflect types\n") var rtypes []string res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) { if value == false { // accessible to reflection rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from))) } }) writeSorted(rtypes) return strings.TrimSpace(buf.String()) }