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