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