github.com/jd-ly/tools@v0.5.7/go/callgraph/cha/cha_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // No testdata on Android. 6 7 // +build !android 8 9 package cha_test 10 11 import ( 12 "bytes" 13 "fmt" 14 "go/ast" 15 "go/parser" 16 "go/token" 17 "go/types" 18 "io/ioutil" 19 "sort" 20 "strings" 21 "testing" 22 23 "github.com/jd-ly/tools/go/callgraph" 24 "github.com/jd-ly/tools/go/callgraph/cha" 25 "github.com/jd-ly/tools/go/loader" 26 "github.com/jd-ly/tools/go/ssa/ssautil" 27 ) 28 29 var inputs = []string{ 30 "testdata/func.go", 31 "testdata/iface.go", 32 "testdata/recv.go", 33 "testdata/issue23925.go", 34 } 35 36 func expectation(f *ast.File) (string, token.Pos) { 37 for _, c := range f.Comments { 38 text := strings.TrimSpace(c.Text()) 39 if t := strings.TrimPrefix(text, "WANT:\n"); t != text { 40 return t, c.Pos() 41 } 42 } 43 return "", token.NoPos 44 } 45 46 // TestCHA runs CHA on each file in inputs, prints the dynamic edges of 47 // the call graph, and compares it with the golden results embedded in 48 // the WANT comment at the end of the file. 49 // 50 func TestCHA(t *testing.T) { 51 for _, filename := range inputs { 52 content, err := ioutil.ReadFile(filename) 53 if err != nil { 54 t.Errorf("couldn't read file '%s': %s", filename, err) 55 continue 56 } 57 58 conf := loader.Config{ 59 ParserMode: parser.ParseComments, 60 } 61 f, err := conf.ParseFile(filename, content) 62 if err != nil { 63 t.Error(err) 64 continue 65 } 66 67 want, pos := expectation(f) 68 if pos == token.NoPos { 69 t.Errorf("No WANT: comment in %s", filename) 70 continue 71 } 72 73 conf.CreateFromFiles("main", f) 74 iprog, err := conf.Load() 75 if err != nil { 76 t.Error(err) 77 continue 78 } 79 80 prog := ssautil.CreateProgram(iprog, 0) 81 mainPkg := prog.Package(iprog.Created[0].Pkg) 82 prog.Build() 83 84 cg := cha.CallGraph(prog) 85 86 if got := printGraph(cg, mainPkg.Pkg); got != want { 87 t.Errorf("%s: got:\n%s\nwant:\n%s", 88 prog.Fset.Position(pos), got, want) 89 } 90 } 91 } 92 93 func printGraph(cg *callgraph.Graph, from *types.Package) string { 94 var edges []string 95 callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { 96 if strings.Contains(e.Description(), "dynamic") { 97 edges = append(edges, fmt.Sprintf("%s --> %s", 98 e.Caller.Func.RelString(from), 99 e.Callee.Func.RelString(from))) 100 } 101 return nil 102 }) 103 sort.Strings(edges) 104 105 var buf bytes.Buffer 106 buf.WriteString("Dynamic calls\n") 107 for _, edge := range edges { 108 fmt.Fprintf(&buf, " %s\n", edge) 109 } 110 return strings.TrimSpace(buf.String()) 111 }