github.com/jujuyuki/gospal@v1.0.1-0.20210215170718-af79fae13b20/ssa/ssa_test.go (about)

     1  package ssa_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/jujuyuki/gospal/ssa"
    11  	"github.com/jujuyuki/gospal/ssa/build"
    12  )
    13  
    14  // This tests basic build.
    15  func TestBuild(t *testing.T) {
    16  	s := `package main
    17  	import "fmt"
    18  	func main() {
    19  		fmt.Println("Hello World")
    20  	}`
    21  
    22  	conf := build.FromReader(strings.NewReader(s))
    23  	info, err := conf.Build()
    24  	if err != nil {
    25  		t.Errorf("SSA build failed: %v", err)
    26  	}
    27  	if info.Prog == nil {
    28  		t.Errorf("SSA Program missing")
    29  	}
    30  	mains, err := ssa.MainPkgs(info.Prog, false)
    31  	if err != nil {
    32  		t.Errorf("cannot find main packages: %v", err)
    33  	}
    34  	for _, main := range mains {
    35  		if main.Func("main") == nil {
    36  			t.Error("expects main.main() but not found")
    37  		}
    38  	}
    39  }
    40  
    41  // This tests building with non-main package.
    42  func TestBuildNonMainPkg(t *testing.T) {
    43  	s := `package pkg
    44  	import "fmt"
    45  	func main() {
    46  		fmt.Println("Hello World")
    47  	}`
    48  
    49  	conf := build.FromReader(strings.NewReader(s))
    50  	info, err := conf.Build()
    51  	if _, err = ssa.MainPkgs(info.Prog, false); err != ssa.ErrNoMainPkgs {
    52  		t.Errorf("unexpected main package")
    53  	}
    54  }
    55  
    56  // This tests building of callgraph.
    57  func TestCallGraph(t *testing.T) {
    58  	s := `package main
    59  	import "fmt"
    60  	func main() {
    61  		foo("Hello")
    62  	}
    63  	func foo(s string) {
    64  		fmt.Println(s, "World")
    65  	}
    66  	func bar() {
    67  		fmt.Println("doesn't reach here")
    68  	}`
    69  
    70  	conf := build.FromReader(strings.NewReader(s))
    71  	info, err := conf.Build()
    72  	if err != nil {
    73  		t.Errorf("SSA build failed: %v", err)
    74  	}
    75  	if info.Prog == nil {
    76  		t.Errorf("SSA Program missing")
    77  	}
    78  	mains, err := ssa.MainPkgs(info.Prog, false)
    79  	if err != nil {
    80  		t.Errorf("cannot find main packages: %v", err)
    81  	}
    82  	for _, main := range mains {
    83  		if main.Func("main") == nil {
    84  			t.Error("expects main.main() but not found")
    85  		}
    86  	}
    87  	graph, err := info.BuildCallGraph("pta", false)
    88  	if err != nil {
    89  		t.Errorf("build callgraph failed: %v", err)
    90  	}
    91  	fns, err := graph.UsedFunctions()
    92  	if err != nil {
    93  		t.Errorf("cannot filter unused functions in callgraph: %v", err)
    94  	}
    95  	for _, fn := range fns {
    96  		if fn.Pkg.Pkg.Name() == "main" {
    97  			if fn.Name() != "foo" && fn.Name() != "main" && fn.Name() != "init" {
    98  				t.Errorf("expecting main.{init, main, foo}, but got main.%s", fn.Name())
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  // This tests building of callgraph and retrieving of all functions in callgraph.
   105  func TestCallGraphAllFunc(t *testing.T) {
   106  	s := `package main
   107  	import "fmt"
   108  	func main() {
   109  		foo("Hello")
   110  	}
   111  	func foo(s string) {
   112  		fmt.Println(s, "World")
   113  	}
   114  	func bar() {
   115  		fmt.Println("doesn't reach here")
   116  	}`
   117  
   118  	conf := build.FromReader(strings.NewReader(s))
   119  	info, err := conf.Build()
   120  	if err != nil {
   121  		t.Errorf("SSA build failed: %v", err)
   122  	}
   123  	if info.Prog == nil {
   124  		t.Errorf("SSA Program missing")
   125  	}
   126  	mains, err := ssa.MainPkgs(info.Prog, false)
   127  	if err != nil {
   128  		t.Errorf("cannot find main packages: %v", err)
   129  	}
   130  	for _, main := range mains {
   131  		if main.Func("main") == nil {
   132  			t.Error("expects main.main() but not found")
   133  		}
   134  	}
   135  	graph, err := info.BuildCallGraph("pta", false)
   136  	if err != nil {
   137  		t.Errorf("build callgraph failed: %v", err)
   138  	}
   139  
   140  	allFuncs, err := graph.AllFunctions()
   141  	if err != nil {
   142  		t.Errorf("cannot get functions in callgraph: %v", err)
   143  	}
   144  	usedFuncs, err := graph.UsedFunctions()
   145  	if err != nil {
   146  		t.Errorf("cannot filter unused functions in callgraph: %v", err)
   147  	}
   148  	if len(allFuncs) < len(usedFuncs) {
   149  		t.Errorf("callgraph has %d functions, %d are used. Expect used < all",
   150  			len(allFuncs), len(usedFuncs))
   151  	}
   152  }
   153  
   154  func ExampleInfo_WriteTo() {
   155  	s := `package main
   156  	func main() { }`
   157  
   158  	conf := build.FromReader(strings.NewReader(s))
   159  	info, err := conf.Build()
   160  	if err != nil {
   161  		log.Fatalf("SSA build failed: %v", err)
   162  	}
   163  	var buf bytes.Buffer
   164  	info.WriteTo(&buf)
   165  	fmt.Println(buf.String())
   166  	// output:
   167  	// # Name: main.init
   168  	// # Package: main
   169  	// # Synthetic: package initializer
   170  	// func init():
   171  	// 0:                                                                entry P:0 S:0
   172  	// 	return
   173  	//
   174  	// # Name: main.main
   175  	// # Package: main
   176  	// # Location: tmp:2:7
   177  	// func main():
   178  	// 0:                                                                entry P:0 S:0
   179  	// 	return
   180  }
   181  
   182  func ExampleCallGraph_WriteGraphviz() {
   183  	s := `package main
   184  	func main() { }`
   185  
   186  	conf := build.FromReader(strings.NewReader(s))
   187  	info, err := conf.Build()
   188  	if err != nil {
   189  		log.Fatalf("SSA build failed: %v", err)
   190  	}
   191  	var buf bytes.Buffer
   192  	cg, err := info.BuildCallGraph("pta", false) // Pointer analysis, no tests.
   193  	if err != nil {
   194  		log.Fatalf("Cannot build callgraph: %v", err)
   195  	}
   196  	cg.WriteGraphviz(&buf)
   197  	fmt.Println(buf.String())
   198  	// output:
   199  	// digraph callgraph {
   200  	//   "<root>" -> "main.init"
   201  	//   "<root>" -> "main.main"
   202  	// }
   203  }