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  }