github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/go/callgraph/cha/cha_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 cha_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/jhump/golang-x-tools/go/callgraph"
    25  	"github.com/jhump/golang-x-tools/go/callgraph/cha"
    26  	"github.com/jhump/golang-x-tools/go/loader"
    27  	"github.com/jhump/golang-x-tools/go/ssa/ssautil"
    28  )
    29  
    30  var inputs = []string{
    31  	"testdata/func.go",
    32  	"testdata/iface.go",
    33  	"testdata/recv.go",
    34  	"testdata/issue23925.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  // TestCHA runs CHA on each file in inputs, prints the dynamic edges of
    48  // the call graph, and compares it with the golden results embedded in
    49  // the WANT comment at the end of the file.
    50  //
    51  func TestCHA(t *testing.T) {
    52  	for _, filename := range inputs {
    53  		content, err := ioutil.ReadFile(filename)
    54  		if err != nil {
    55  			t.Errorf("couldn't read file '%s': %s", filename, err)
    56  			continue
    57  		}
    58  
    59  		conf := loader.Config{
    60  			ParserMode: parser.ParseComments,
    61  		}
    62  		f, err := conf.ParseFile(filename, content)
    63  		if err != nil {
    64  			t.Error(err)
    65  			continue
    66  		}
    67  
    68  		want, pos := expectation(f)
    69  		if pos == token.NoPos {
    70  			t.Errorf("No WANT: comment in %s", filename)
    71  			continue
    72  		}
    73  
    74  		conf.CreateFromFiles("main", f)
    75  		iprog, err := conf.Load()
    76  		if err != nil {
    77  			t.Error(err)
    78  			continue
    79  		}
    80  
    81  		prog := ssautil.CreateProgram(iprog, 0)
    82  		mainPkg := prog.Package(iprog.Created[0].Pkg)
    83  		prog.Build()
    84  
    85  		cg := cha.CallGraph(prog)
    86  
    87  		if got := printGraph(cg, mainPkg.Pkg); got != want {
    88  			t.Errorf("%s: got:\n%s\nwant:\n%s",
    89  				prog.Fset.Position(pos), got, want)
    90  		}
    91  	}
    92  }
    93  
    94  func printGraph(cg *callgraph.Graph, from *types.Package) string {
    95  	var edges []string
    96  	callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
    97  		if strings.Contains(e.Description(), "dynamic") {
    98  			edges = append(edges, fmt.Sprintf("%s --> %s",
    99  				e.Caller.Func.RelString(from),
   100  				e.Callee.Func.RelString(from)))
   101  		}
   102  		return nil
   103  	})
   104  	sort.Strings(edges)
   105  
   106  	var buf bytes.Buffer
   107  	buf.WriteString("Dynamic calls\n")
   108  	for _, edge := range edges {
   109  		fmt.Fprintf(&buf, "  %s\n", edge)
   110  	}
   111  	return strings.TrimSpace(buf.String())
   112  }