// 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. // No testdata on Android. // +build !android package cha_test import ( "bytes" "fmt" "go/ast" "go/parser" "go/token" "go/types" "io/ioutil" "sort" "strings" "testing" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa/ssautil" ) var inputs = []string{ "testdata/func.go", "testdata/iface.go", "testdata/recv.go", "testdata/issue23925.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 } // TestCHA runs CHA on each file in inputs, prints the dynamic edges of // the call graph, and compares it with the golden results embedded in // the WANT comment at the end of the file. // func TestCHA(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 := ssautil.CreateProgram(iprog, 0) mainPkg := prog.Package(iprog.Created[0].Pkg) prog.Build() cg := cha.CallGraph(prog) if got := printGraph(cg, mainPkg.Pkg); got != want { t.Errorf("%s: got:\n%s\nwant:\n%s", prog.Fset.Position(pos), got, want) } } } func printGraph(cg *callgraph.Graph, from *types.Package) string { var edges []string callgraph.GraphVisitEdges(cg, 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 }) sort.Strings(edges) var buf bytes.Buffer buf.WriteString("Dynamic calls\n") for _, edge := range edges { fmt.Fprintf(&buf, " %s\n", edge) } return strings.TrimSpace(buf.String()) }