...

Source file src/golang.org/x/tools/go/callgraph/vta/vta_test.go

Documentation: golang.org/x/tools/go/callgraph/vta

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

View as plain text