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  }