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