github.com/mmirolim/gtr@v0.3.0/parser_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"reflect"
    12  	"testing"
    13  
    14  	"github.com/kr/pretty"
    15  )
    16  
    17  func TestChangesFromGitDiff(t *testing.T) {
    18  	cases := []struct {
    19  		data   string
    20  		output []Change
    21  		err    error
    22  	}{
    23  		{data: `diff --git a/parser.go b/parser.go
    24  index 6452f09..de4ce2a 100644
    25  --- a/parser.go
    26  +++ b/parser.go
    27  @@ -32,0 +33,2 @@ func changesFromGitDiff(diff string) ([]Change, error) {
    28          // comment
    29          // comment
    30  +       fmt.Printf("All matches %+v\n", matches) // output for debug
    31  +
    32  }
    33           // comment
    34  diff --git a/parser_test.go b/parser_test.go
    35  index 7268a75..31a1203 100644
    36  --- a/parser_test.go
    37  +++ b/parser_test.go
    38  @@ -341 +341 @@ func TestGetDiff(t *testing.T) {
    39  //
    40  -                       desc: "Update file math.go, change package level const and add comment",
    41  +                       desc: "Multiple updates to file math.go",
    42  @@ -343 +343,2 @@ func TestGetDiff(t *testing.T) {
    43  -                               return ioutil.WriteFile(filePath("math.go"), mathgo_update_pkg_lvl_var_add_comment_change_func, 0600)
    44  +                               return ioutil.WriteFile(filePath("math.go"),
    45  +                                       mathgo_update_pkg_lvl_var_add_comment_change_func, 0600)
    46  @@ -349 +350 @@ func TestGetDiff(t *testing.T) {
    47  -                       output:      []Change{{"math.go", 1, 9}},
    48  +                       output:      []Change{{"math.go", 0, 0}, {"math.go", 1, 9}},
    49  diff --git a/process_go_file.go b/process_go_file.go
    50  index 95a1cdd..d32a8ba 100644
    51  --- a/process_go_file.go
    52  +++ b/process_go_file.go
    53  @@ -58 +57,0 @@ func parseTestFile(fname string) error {
    54  -       fmt.Printf("parseTestFile %+v\n", fname) // output for debug
    55  @@ -69,0 +69 @@ func parseTestFile(fname string) error {
    56  +       // testFiles LOCK
    57  @@ -72,0 +73 @@ func parseTestFile(fname string) error {
    58  +       // testFiles UNLOCK
    59  @@ -118,0 +120 @@ func indexFuncsInTestFile(fname string) {
    60  +// TODO handle other types of entities like Types, Interfaces
    61  @@ -166 +168 @@ func processFileChanges() (map[string]FileInfo, error) {
    62  -       //fmt.Printf("Process changes\n%+v\n", changes) // output for debug
    63  +       fmt.Printf("Process changes\n%+v\n", changes) // output for debug
    64  diff --git a/process_go_file_test.go b/process_go_file_test.go
    65  index d1f20de..c0bcbcd 100644
    66  --- a/process_go_file_test.go
    67  +++ b/process_go_file_test.go
    68  @@ -5,0 +6,2 @@ import (
    69  +
    70  +       "github.com/kr/pretty"
    71  @@ -69,0 +72,22 @@ func Test_fnNameFromCallExpr(t *testing.T) {
    72  +       }
    73  +}
    74  `, output: []Change{
    75  			{fpathOld: "parser.go", fpath: "parser.go", start: 33, count: 2},
    76  			{fpathOld: "parser_test.go", fpath: "parser_test.go", start: 341, count: 0},
    77  			{fpathOld: "parser_test.go", fpath: "parser_test.go", start: 343, count: 2},
    78  			{fpathOld: "parser_test.go", fpath: "parser_test.go", start: 350, count: 0},
    79  			{fpathOld: "process_go_file.go", fpath: "process_go_file.go", start: 57, count: 0},
    80  			{fpathOld: "process_go_file.go", fpath: "process_go_file.go", start: 69, count: 0},
    81  			{fpathOld: "process_go_file.go", fpath: "process_go_file.go", start: 73, count: 0},
    82  			{fpathOld: "process_go_file.go", fpath: "process_go_file.go", start: 120, count: 0},
    83  			{fpathOld: "process_go_file.go", fpath: "process_go_file.go", start: 168, count: 0},
    84  			{fpathOld: "process_go_file_test.go", fpath: "process_go_file_test.go", start: 6, count: 2},
    85  			{fpathOld: "process_go_file_test.go", fpath: "process_go_file_test.go", start: 72, count: 22}}},
    86  		// deleted file
    87  		{data: `diff --git a/main.go b/main.go
    88  deleted file mode 100644
    89  index 6e2c328..0000000
    90  --- a/main.go
    91  +++ /dev/null
    92  @@ -1,12 +0,0 @@
    93  -
    94  -package main
    95  -
    96  -var a, b int = 10, 20
    97  -
    98  -func main() {
    99  -	fmt.Printf("%+v\n", add(a, b))
   100  -}
   101  -
   102  -func add(a, b int) {
   103  -	return a + b
   104  -}
   105  `, output: nil},
   106  	}
   107  	var buffer bytes.Buffer
   108  	for i, tc := range cases {
   109  		buffer.Reset()
   110  		buffer.WriteString(tc.data)
   111  
   112  		changes, err := changesFromGitDiff(buffer)
   113  		if isUnexpectedErr(t, i, "", tc.err, err) {
   114  			continue
   115  		}
   116  
   117  		diffs := pretty.Diff(tc.output, changes)
   118  		if len(diffs) > 0 {
   119  			t.Errorf("%# v", pretty.Formatter(diffs))
   120  		}
   121  
   122  	}
   123  }
   124  
   125  var gofile = []byte(`
   126  package main
   127  
   128  import "github.com/pkg"
   129  
   130  type T1 struct{
   131       a int
   132  }
   133  
   134  func (t *T1) M1(a, b int) int {
   135  	z := a + b
   136  	a += b
   137  	return z + a + b
   138  }
   139  
   140  func (t T1) M2(a, b int) int {
   141  	return b - a
   142  }
   143  
   144  func F2() int {
   145  	return pkg.F()
   146  }
   147  
   148  func Perimeter(d, h int) int {
   149  	return 2*d + 2*h
   150  }
   151  
   152  func Area(d, h int) int {
   153  	return d * h
   154  }
   155  `)
   156  
   157  func TestGetFileBlocks(t *testing.T) {
   158  	cases := []struct {
   159  		fileName string
   160  		fileData []byte
   161  		output   FileInfo
   162  		err      error
   163  	}{
   164  		{
   165  			fileName: "gofile.go", fileData: gofile, output: FileInfo{
   166  				fname:   "gofile.go",
   167  				pkgName: "main", endLine: 30,
   168  				blocks: []FileBlock{
   169  					{typ: BlockType, name: "T1", start: 6, end: 8},
   170  					{typ: BlockMethod, name: "T1.M1", start: 10, end: 14},
   171  					{typ: BlockMethod, name: "T1.M2", start: 16, end: 18},
   172  					{typ: BlockFunc, name: "F2", start: 20, end: 22},
   173  					{typ: BlockFunc, name: "Perimeter", start: 24, end: 26},
   174  					{typ: BlockFunc, name: "Area", start: 28, end: 30},
   175  				},
   176  			},
   177  		},
   178  	}
   179  	for i, tc := range cases {
   180  		fileInfo, err := getFileInfo("gofile.go", gofile)
   181  		if isUnexpectedErr(t, i, "", tc.err, err) {
   182  			continue
   183  		}
   184  
   185  		diffs := pretty.Diff(tc.output, fileInfo)
   186  		if len(diffs) > 0 {
   187  			fmt.Printf("%# v\n", pretty.Formatter(fileInfo)) // output for debug
   188  			t.Errorf("%# v", pretty.Formatter(diffs))
   189  		}
   190  	}
   191  }
   192  
   193  func TestParseFlag(t *testing.T) {
   194  	cases := []struct {
   195  		desc   string
   196  		osArgs []string
   197  		out    config
   198  		err    error
   199  	}{
   200  		{
   201  			desc:   "no flags are defined",
   202  			osArgs: []string{"./binary"},
   203  			out:    newConfig(),
   204  			err:    nil,
   205  		},
   206  		{
   207  			desc: "All flags are correctly defined",
   208  			osArgs: []string{
   209  				"./binary", "-C", "/home/user/go", "-strategy", "coverage",
   210  				"-analysis", "cha", "-delay", "10", "-exclude-file-prefix", "h,v,#",
   211  				"-exclude-dirs", "vendor,node_modules", "-auto-commit", "t", "-run-init", "false", "-args",
   212  				"-tf1", "10", "-tf2", "20,30"},
   213  			out: config{
   214  				workDir:           "/home/user/go",
   215  				strategy:          "coverage",
   216  				analysis:          "cha",
   217  				runInit:           false,
   218  				delay:             10,
   219  				excludeFilePrefix: []string{"h", "v", "#"},
   220  				excludeDirs:       []string{"vendor", "node_modules"},
   221  				autoCommit:        true,
   222  				argsToTestBinary:  "-tf1 10 -tf2 20,30",
   223  			},
   224  			err: nil,
   225  		},
   226  		{
   227  			desc: "Delay flag invalid",
   228  			osArgs: []string{"./binary", "-delay", "10.1", "-args",
   229  				"-tf1", "10", "-tf2", "20,30"},
   230  			out: config{},
   231  			err: errors.New("-delay invalid value 10.1"),
   232  		},
   233  		{
   234  			desc:   "Flag value missing",
   235  			osArgs: []string{"./binary", "-auto-commit", "t", "-exclude-dirs"},
   236  			out:    config{},
   237  			err:    errors.New("-exclude-dirs value missing"),
   238  		},
   239  		{
   240  			desc:   "on help return usage",
   241  			osArgs: []string{"./binary", "help"},
   242  			out:    config{},
   243  			err:    errors.New(flagUsage()),
   244  		},
   245  		{
   246  			desc:   "test flag setting",
   247  			osArgs: []string{"./binary", "-strategy=\"coverage\"", "-analysis=cha"},
   248  			out: config{
   249  				workDir:           ".",
   250  				delay:             1000,
   251  				strategy:          "coverage",
   252  				runInit:           true,
   253  				analysis:          "cha",
   254  				excludeFilePrefix: []string{"#"},
   255  				excludeDirs:       []string{"vendor", "node_modules"},
   256  				autoCommit:        false,
   257  				argsToTestBinary:  "",
   258  			},
   259  			err: nil,
   260  		},
   261  		{
   262  			desc:   "invalid option",
   263  			osArgs: []string{"./binary", "-no-a-flag", "value"},
   264  			out:    config{},
   265  			err:    errors.New("invalid option -- -no-a-flag"),
   266  		},
   267  	}
   268  	for i, tc := range cases {
   269  		cfg, err := parseFlags(tc.osArgs)
   270  		if isUnexpectedErr(t, i, tc.desc, tc.err, err) {
   271  			continue
   272  		}
   273  		if err != nil {
   274  			continue // skip on valid errors
   275  		}
   276  		diffs := pretty.Diff(tc.out, cfg)
   277  		if len(diffs) > 0 {
   278  			t.Errorf("case [%d] %s\nunexpected result %# v", i, tc.desc, pretty.Formatter(diffs))
   279  		}
   280  	}
   281  
   282  }
   283  
   284  func TestGetModuleName(t *testing.T) {
   285  	gomod := []byte(`module rock.com/solid
   286  
   287  go 1.13
   288  
   289  require (
   290  	golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a
   291  )`)
   292  	// setup
   293  	testDir := filepath.Join(os.TempDir(), "test-get-module-name")
   294  	workDir := filepath.Join(testDir, "src", "rockcom", "solid")
   295  	err := os.MkdirAll(workDir, 0700)
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	dir, err := os.Getwd()
   300  	if err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	// change dir
   304  	err = os.Chdir(workDir)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	// teardown
   309  	defer func() {
   310  		// go back
   311  		os.Chdir(dir)
   312  		if !t.Failed() {
   313  			// clean tmp dir on test success
   314  			_ = os.RemoveAll(testDir)
   315  		}
   316  	}()
   317  
   318  	cases := []struct {
   319  		desc            string
   320  		gofile          []byte
   321  		module          string
   322  		err             error
   323  		setup, teardown func() error
   324  	}{
   325  		{
   326  			desc:   "Not a go module",
   327  			module: "rockcom/solid",
   328  			setup: func() error {
   329  				return os.Setenv("GOPATH", testDir)
   330  			},
   331  		},
   332  		{
   333  			desc:   "Go module",
   334  			gofile: gomod,
   335  			module: "rock.com/solid",
   336  			setup: func() error {
   337  				return ioutil.WriteFile(filepath.Join(workDir, "go.mod"), gomod, 0600)
   338  			},
   339  			teardown: func() error {
   340  				return os.Remove(filepath.Join(workDir, "go.mod"))
   341  			},
   342  		},
   343  	}
   344  
   345  	for i, tc := range cases {
   346  		// setup
   347  		execTestHelper(t, i, tc.desc, tc.setup)
   348  
   349  		module, err := getModuleName(workDir)
   350  
   351  		// teardown
   352  		execTestHelper(t, i, tc.desc, tc.teardown)
   353  		if isUnexpectedErr(t, i, tc.desc, tc.err, err) {
   354  			continue
   355  		}
   356  
   357  		if !reflect.DeepEqual(tc.module, module) {
   358  			t.Errorf("case [%d] %s\nexpected %s, got %s", i, tc.desc, tc.module, module)
   359  		}
   360  	}
   361  }
   362  
   363  func TestParseCoverProfile(t *testing.T) {
   364  	testRunnerFile := []byte(`mode: set
   365  mmirolim/gtr/strategy.go:329.38,331.15 1 0
   366  mmirolim/gtr/strategy.go:333.28,335.7 1 0
   367  mmirolim/gtr/testrunner.go:39.17,46.2 1 1
   368  mmirolim/gtr/testrunner.go:49.37,51.2 1 0
   369  mmirolim/gtr/testrunner.go:100.77,102.18 2 1
   370  mmirolim/gtr/testrunner.go:105.2,105.24 1 1
   371  mmirolim/gtr/testrunner.go:108.2,108.12 1 1
   372  mmirolim/gtr/testrunner.go:102.18,104.3 1 1
   373  mmirolim/gtr/testrunner.go:105.24,107.3 1 1
   374  mmirolim/gtr/testrunner.go:111.77,113.12 2 1
   375  mmirolim/gtr/testrunner.go:121.2,123.21 3 1
   376  mmirolim/gtr/testrunner.go:126.2,126.30 1 1
   377  mmirolim/gtr/testrunner.go:113.12,117.3 3 1
   378  mmirolim/gtr/testrunner.go:117.8,119.3 1 0
   379  mmirolim/gtr/testrunner.go:123.21,125.3 1 1
   380  mmirolim/gtr/watcher.go:51.21,64.2 2 0
   381  mmirolim/gtr/watcher.go:67.31,71.16 3 1
   382  `)
   383  	cases := []struct {
   384  		desc    string
   385  		data    []byte
   386  		infoMap map[string]*FileCoverInfo
   387  		err     error
   388  	}{
   389  		{
   390  			desc: "No data",
   391  			data: nil,
   392  			err:  io.EOF,
   393  		},
   394  		{
   395  			desc: "Cover profile data for multiple files",
   396  			data: testRunnerFile,
   397  			infoMap: map[string]*FileCoverInfo{
   398  				"mmirolim/gtr/watcher.go": &FileCoverInfo{
   399  					"mmirolim/gtr/watcher.go", [][2]int{{67, 71}},
   400  				},
   401  				"mmirolim/gtr/strategy.go": &FileCoverInfo{
   402  					File: "mmirolim/gtr/strategy.go",
   403  				},
   404  				"mmirolim/gtr/testrunner.go": &FileCoverInfo{
   405  					"mmirolim/gtr/testrunner.go",
   406  					[][2]int{{39, 46}, {100, 104}, {105, 107}, {108, 108},
   407  						{111, 117}, {121, 125}, {126, 126}},
   408  				},
   409  			},
   410  		},
   411  	}
   412  
   413  	for i, tc := range cases {
   414  		infos, err := ParseCoverProfile(tc.data)
   415  		if isUnexpectedErr(t, i, tc.desc, tc.err, err) {
   416  			continue
   417  		}
   418  		if err != nil {
   419  			continue
   420  		}
   421  
   422  		diffs := pretty.Diff(tc.infoMap, infos)
   423  		if len(diffs) > 0 {
   424  			t.Errorf("case [%d] %s\nexpected %+v, got %+v", i, tc.desc, tc.infoMap, infos)
   425  			fmt.Printf("Diffs %# v\n", pretty.Formatter(diffs)) // output for debug
   426  
   427  		}
   428  	}
   429  }