github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/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/golangci/go-tools/callgraph"
    24  	"github.com/golangci/go-tools/callgraph/cha"
    25  	"golang.org/x/tools/go/loader"
    26  	"github.com/golangci/go-tools/ssa/ssautil"
    27  )
    28  
    29  var inputs = []string{
    30  	"testdata/func.go",
    31  	"testdata/iface.go",
    32  	"testdata/recv.go",
    33  }
    34  
    35  func expectation(f *ast.File) (string, token.Pos) {
    36  	for _, c := range f.Comments {
    37  		text := strings.TrimSpace(c.Text())
    38  		if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
    39  			return t, c.Pos()
    40  		}
    41  	}
    42  	return "", token.NoPos
    43  }
    44  
    45  // TestCHA runs CHA on each file in inputs, prints the dynamic edges of
    46  // the call graph, and compares it with the golden results embedded in
    47  // the WANT comment at the end of the file.
    48  //
    49  func TestCHA(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 := ssautil.CreateProgram(iprog, 0)
    80  		mainPkg := prog.Package(iprog.Created[0].Pkg)
    81  		prog.Build()
    82  
    83  		cg := cha.CallGraph(prog)
    84  
    85  		if got := printGraph(cg, mainPkg.Pkg); got != want {
    86  			t.Errorf("%s: got:\n%s\nwant:\n%s",
    87  				prog.Fset.Position(pos), got, want)
    88  		}
    89  	}
    90  }
    91  
    92  func printGraph(cg *callgraph.Graph, from *types.Package) string {
    93  	var edges []string
    94  	callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
    95  		if strings.Contains(e.Description(), "dynamic") {
    96  			edges = append(edges, fmt.Sprintf("%s --> %s",
    97  				e.Caller.Func.RelString(from),
    98  				e.Callee.Func.RelString(from)))
    99  		}
   100  		return nil
   101  	})
   102  	sort.Strings(edges)
   103  
   104  	var buf bytes.Buffer
   105  	buf.WriteString("Dynamic calls\n")
   106  	for _, edge := range edges {
   107  		fmt.Fprintf(&buf, "  %s\n", edge)
   108  	}
   109  	return strings.TrimSpace(buf.String())
   110  }