github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/callgraph/rta/rta_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 rta_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/golangci/go-tools/callgraph" 24 "github.com/golangci/go-tools/callgraph/rta" 25 "golang.org/x/tools/go/loader" 26 "github.com/golangci/go-tools/ssa" 27 "github.com/golangci/go-tools/ssa/ssautil" 28 ) 29 30 var inputs = []string{ 31 "testdata/func.go", 32 "testdata/rtype.go", 33 "testdata/iface.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 // TestRTA runs RTA on each file in inputs, prints the results, and 47 // compares it with the golden results embedded in the WANT comment at 48 // the end of the file. 49 // 50 // The results string consists of two parts: the set of dynamic call 51 // edges, "f --> g", one per line, and the set of reachable functions, 52 // one per line. Each set is sorted. 53 // 54 func TestRTA(t *testing.T) { 55 for _, filename := range inputs { 56 content, err := ioutil.ReadFile(filename) 57 if err != nil { 58 t.Errorf("couldn't read file '%s': %s", filename, err) 59 continue 60 } 61 62 conf := loader.Config{ 63 ParserMode: parser.ParseComments, 64 } 65 f, err := conf.ParseFile(filename, content) 66 if err != nil { 67 t.Error(err) 68 continue 69 } 70 71 want, pos := expectation(f) 72 if pos == token.NoPos { 73 t.Errorf("No WANT: comment in %s", filename) 74 continue 75 } 76 77 conf.CreateFromFiles("main", f) 78 iprog, err := conf.Load() 79 if err != nil { 80 t.Error(err) 81 continue 82 } 83 84 prog := ssautil.CreateProgram(iprog, 0) 85 mainPkg := prog.Package(iprog.Created[0].Pkg) 86 prog.Build() 87 88 res := rta.Analyze([]*ssa.Function{ 89 mainPkg.Func("main"), 90 mainPkg.Func("init"), 91 }, true) 92 93 if got := printResult(res, mainPkg.Pkg); got != want { 94 t.Errorf("%s: got:\n%s\nwant:\n%s", 95 prog.Fset.Position(pos), got, want) 96 } 97 } 98 } 99 100 func printResult(res *rta.Result, from *types.Package) string { 101 var buf bytes.Buffer 102 103 writeSorted := func(ss []string) { 104 sort.Strings(ss) 105 for _, s := range ss { 106 fmt.Fprintf(&buf, " %s\n", s) 107 } 108 } 109 110 buf.WriteString("Dynamic calls\n") 111 var edges []string 112 callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error { 113 if strings.Contains(e.Description(), "dynamic") { 114 edges = append(edges, fmt.Sprintf("%s --> %s", 115 e.Caller.Func.RelString(from), 116 e.Callee.Func.RelString(from))) 117 } 118 return nil 119 }) 120 writeSorted(edges) 121 122 buf.WriteString("Reachable functions\n") 123 var reachable []string 124 for f := range res.Reachable { 125 reachable = append(reachable, f.RelString(from)) 126 } 127 writeSorted(reachable) 128 129 buf.WriteString("Reflect types\n") 130 var rtypes []string 131 res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) { 132 if value == false { // accessible to reflection 133 rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from))) 134 } 135 }) 136 writeSorted(rtypes) 137 138 return strings.TrimSpace(buf.String()) 139 }