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 }