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