github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/callgraph/vta/vta_test.go (about)

     1  // Copyright 2021 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 vta
     6  
     7  import (
     8  	"testing"
     9  
    10  	"golang.org/x/tools/go/analysis"
    11  	"golang.org/x/tools/go/analysis/analysistest"
    12  	"golang.org/x/tools/go/analysis/passes/buildssa"
    13  	"golang.org/x/tools/go/callgraph/cha"
    14  	"golang.org/x/tools/go/ssa"
    15  	"golang.org/x/tools/go/ssa/ssautil"
    16  	"golang.org/x/tools/internal/typeparams"
    17  )
    18  
    19  func TestVTACallGraph(t *testing.T) {
    20  	for _, file := range []string{
    21  		"testdata/src/callgraph_static.go",
    22  		"testdata/src/callgraph_ho.go",
    23  		"testdata/src/callgraph_interfaces.go",
    24  		"testdata/src/callgraph_pointers.go",
    25  		"testdata/src/callgraph_collections.go",
    26  		"testdata/src/callgraph_fields.go",
    27  		"testdata/src/callgraph_field_funcs.go",
    28  		"testdata/src/callgraph_recursive_types.go",
    29  		"testdata/src/callgraph_issue_57756.go",
    30  	} {
    31  		t.Run(file, func(t *testing.T) {
    32  			prog, want, err := testProg(file, ssa.BuilderMode(0))
    33  			if err != nil {
    34  				t.Fatalf("couldn't load test file '%s': %s", file, err)
    35  			}
    36  			if len(want) == 0 {
    37  				t.Fatalf("couldn't find want in `%s`", file)
    38  			}
    39  
    40  			g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
    41  			if got := callGraphStr(g); !subGraph(want, got) {
    42  				t.Errorf("computed callgraph %v should contain %v", got, want)
    43  			}
    44  		})
    45  	}
    46  }
    47  
    48  // TestVTAProgVsFuncSet exemplifies and tests different possibilities
    49  // enabled by having an arbitrary function set as input to CallGraph
    50  // instead of the whole program (i.e., ssautil.AllFunctions(prog)).
    51  func TestVTAProgVsFuncSet(t *testing.T) {
    52  	prog, want, err := testProg("testdata/src/callgraph_nested_ptr.go", ssa.BuilderMode(0))
    53  	if err != nil {
    54  		t.Fatalf("couldn't load test `testdata/src/callgraph_nested_ptr.go`: %s", err)
    55  	}
    56  	if len(want) == 0 {
    57  		t.Fatal("couldn't find want in `testdata/src/callgraph_nested_ptr.go`")
    58  	}
    59  
    60  	allFuncs := ssautil.AllFunctions(prog)
    61  	g := CallGraph(allFuncs, cha.CallGraph(prog))
    62  	// VTA over the whole program will produce a call graph that
    63  	// includes Baz:(**i).Foo -> A.Foo, B.Foo.
    64  	if got := callGraphStr(g); !subGraph(want, got) {
    65  		t.Errorf("computed callgraph %v should contain %v", got, want)
    66  	}
    67  
    68  	// Prune the set of program functions to exclude Bar(). This should
    69  	// yield a call graph that includes different set of callees for Baz
    70  	// Baz:(**i).Foo -> A.Foo
    71  	//
    72  	// Note that the exclusion of Bar can happen, for instance, if Baz is
    73  	// considered an entry point of some data flow analysis and Bar is
    74  	// provably (e.g., using CHA forward reachability) unreachable from Baz.
    75  	noBarFuncs := make(map[*ssa.Function]bool)
    76  	for f, in := range allFuncs {
    77  		noBarFuncs[f] = in && (funcName(f) != "Bar")
    78  	}
    79  	want = []string{"Baz: Do(i) -> Do; invoke t2.Foo() -> A.Foo"}
    80  	g = CallGraph(noBarFuncs, cha.CallGraph(prog))
    81  	if got := callGraphStr(g); !subGraph(want, got) {
    82  		t.Errorf("pruned callgraph %v should contain %v", got, want)
    83  	}
    84  }
    85  
    86  // TestVTAPanicMissingDefinitions tests if VTA gracefully handles the case
    87  // where VTA panics when a definition of a function or method is not
    88  // available, which can happen when using analysis package. A successful
    89  // test simply does not panic.
    90  func TestVTAPanicMissingDefinitions(t *testing.T) {
    91  	run := func(pass *analysis.Pass) (interface{}, error) {
    92  		s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
    93  		CallGraph(ssautil.AllFunctions(s.Pkg.Prog), cha.CallGraph(s.Pkg.Prog))
    94  		return nil, nil
    95  	}
    96  
    97  	analyzer := &analysis.Analyzer{
    98  		Name: "test",
    99  		Doc:  "test",
   100  		Run:  run,
   101  		Requires: []*analysis.Analyzer{
   102  			buildssa.Analyzer,
   103  		},
   104  	}
   105  
   106  	testdata := analysistest.TestData()
   107  	res := analysistest.Run(t, testdata, analyzer, "t", "d")
   108  	if len(res) != 2 {
   109  		t.Errorf("want analysis results for 2 packages; got %v", len(res))
   110  	}
   111  	for _, r := range res {
   112  		if r.Err != nil {
   113  			t.Errorf("want no error for package %v; got %v", r.Pass.Pkg.Path(), r.Err)
   114  		}
   115  	}
   116  }
   117  
   118  func TestVTACallGraphGenerics(t *testing.T) {
   119  	if !typeparams.Enabled {
   120  		t.Skip("TestVTACallGraphGenerics requires type parameters")
   121  	}
   122  
   123  	// TODO(zpavlinovic): add more tests
   124  	file := "testdata/src/callgraph_generics.go"
   125  	prog, want, err := testProg(file, ssa.InstantiateGenerics)
   126  	if err != nil {
   127  		t.Fatalf("couldn't load test file '%s': %s", file, err)
   128  	}
   129  	if len(want) == 0 {
   130  		t.Fatalf("couldn't find want in `%s`", file)
   131  	}
   132  
   133  	g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
   134  	if got := callGraphStr(g); !subGraph(want, got) {
   135  		t.Errorf("computed callgraph %v should contain %v", got, want)
   136  	}
   137  }