github.com/jd-ly/tools@v0.5.7/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  // +build !android
     8  
     9  package cha_test
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"go/ast"
    15  	"go/parser"
    16  	"go/token"
    17  	"go/types"
    18  	"io/ioutil"
    19  	"sort"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/jd-ly/tools/go/callgraph"
    24  	"github.com/jd-ly/tools/go/callgraph/cha"
    25  	"github.com/jd-ly/tools/go/loader"
    26  	"github.com/jd-ly/tools/go/ssa/ssautil"
    27  )
    28  
    29  var inputs = []string{
    30  	"testdata/func.go",
    31  	"testdata/iface.go",
    32  	"testdata/recv.go",
    33  	"testdata/issue23925.go",
    34  }
    35  
    36  func expectation(f *ast.File) (string, token.Pos) {
    37  	for _, c := range f.Comments {
    38  		text := strings.TrimSpace(c.Text())
    39  		if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
    40  			return t, c.Pos()
    41  		}
    42  	}
    43  	return "", token.NoPos
    44  }
    45  
    46  // TestCHA runs CHA on each file in inputs, prints the dynamic edges of
    47  // the call graph, and compares it with the golden results embedded in
    48  // the WANT comment at the end of the file.
    49  //
    50  func TestCHA(t *testing.T) {
    51  	for _, filename := range inputs {
    52  		content, err := ioutil.ReadFile(filename)
    53  		if err != nil {
    54  			t.Errorf("couldn't read file '%s': %s", filename, err)
    55  			continue
    56  		}
    57  
    58  		conf := loader.Config{
    59  			ParserMode: parser.ParseComments,
    60  		}
    61  		f, err := conf.ParseFile(filename, content)
    62  		if err != nil {
    63  			t.Error(err)
    64  			continue
    65  		}
    66  
    67  		want, pos := expectation(f)
    68  		if pos == token.NoPos {
    69  			t.Errorf("No WANT: comment in %s", filename)
    70  			continue
    71  		}
    72  
    73  		conf.CreateFromFiles("main", f)
    74  		iprog, err := conf.Load()
    75  		if err != nil {
    76  			t.Error(err)
    77  			continue
    78  		}
    79  
    80  		prog := ssautil.CreateProgram(iprog, 0)
    81  		mainPkg := prog.Package(iprog.Created[0].Pkg)
    82  		prog.Build()
    83  
    84  		cg := cha.CallGraph(prog)
    85  
    86  		if got := printGraph(cg, mainPkg.Pkg); got != want {
    87  			t.Errorf("%s: got:\n%s\nwant:\n%s",
    88  				prog.Fset.Position(pos), got, want)
    89  		}
    90  	}
    91  }
    92  
    93  func printGraph(cg *callgraph.Graph, from *types.Package) string {
    94  	var edges []string
    95  	callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
    96  		if strings.Contains(e.Description(), "dynamic") {
    97  			edges = append(edges, fmt.Sprintf("%s --> %s",
    98  				e.Caller.Func.RelString(from),
    99  				e.Callee.Func.RelString(from)))
   100  		}
   101  		return nil
   102  	})
   103  	sort.Strings(edges)
   104  
   105  	var buf bytes.Buffer
   106  	buf.WriteString("Dynamic calls\n")
   107  	for _, edge := range edges {
   108  		fmt.Fprintf(&buf, "  %s\n", edge)
   109  	}
   110  	return strings.TrimSpace(buf.String())
   111  }