github.com/jdhenke/godel@v0.0.0-20161213181855-abeb3861bf0d/apps/okgo/integration_test/integration_test.go (about)

     1  // Copyright 2016 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package integration_test
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"path"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/nmiyake/pkg/dirs"
    26  	"github.com/nmiyake/pkg/gofiles"
    27  	"github.com/palantir/amalgomate/amalgomated"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  
    31  	"github.com/palantir/godel/apps/okgo/checkoutput"
    32  	"github.com/palantir/godel/apps/okgo/checks"
    33  	"github.com/palantir/godel/apps/okgo/cmd/cmdlib"
    34  	"github.com/palantir/godel/apps/okgo/config"
    35  	"github.com/palantir/godel/apps/okgo/params"
    36  	"github.com/palantir/godel/pkg/products"
    37  )
    38  
    39  func TestCheckers(t *testing.T) {
    40  	cli, err := products.Bin("okgo")
    41  	require.NoError(t, err)
    42  
    43  	for i, currCase := range []struct {
    44  		check amalgomated.Cmd
    45  		want  []string
    46  	}{
    47  		{
    48  			check: cmdlib.Instance().MustNewCmd("deadcode"),
    49  			want: []string{
    50  				"pkg1/bad.go:27:1: deadcode is unused",
    51  				"pkg1/bad.go:40:1: varcheck is unused",
    52  				"pkg2/bad2.go:27:1: deadcode is unused",
    53  				"pkg2/bad2.go:40:1: varcheck is unused",
    54  			},
    55  		},
    56  		{
    57  			check: cmdlib.Instance().MustNewCmd("errcheck"),
    58  			want: []string{
    59  				"pkg1/bad.go:11:8: helper()",
    60  				"pkg2/bad2.go:11:8: helper()",
    61  			},
    62  		},
    63  		{
    64  			check: cmdlib.Instance().MustNewCmd("golint"),
    65  			want: []string{
    66  				`pkg1/bad.go:49:1: comment on exported function Lint should be of the form "Lint ..."`,
    67  				`pkg2/bad2.go:49:1: comment on exported function Lint should be of the form "Lint ..."`,
    68  			},
    69  		},
    70  		{
    71  			check: cmdlib.Instance().MustNewCmd("govet"),
    72  			want: []string{
    73  				"pkg1/bad.go:23: self-assignment of foo to foo",
    74  				"pkg2/bad2.go:23: self-assignment of foo to foo",
    75  			},
    76  		},
    77  		{
    78  			check: cmdlib.Instance().MustNewCmd("ineffassign"),
    79  			want: []string{
    80  				"pkg1/bad.go:34:2: ineffectual assignment to kvs",
    81  				"pkg1/bad.go:36:2: ineffectual assignment to kvs",
    82  				"pkg2/bad2.go:34:2: ineffectual assignment to kvs",
    83  				"pkg2/bad2.go:36:2: ineffectual assignment to kvs",
    84  			},
    85  		},
    86  		{
    87  			check: cmdlib.Instance().MustNewCmd("outparamcheck"),
    88  			want: []string{
    89  				`github.com/palantir/godel/apps/okgo/integration_test/testdata/standard/pkg1/bad.go:16:26: _ = json.Unmarshal(nil, "")  // 2nd argument of 'Unmarshal' requires '&'`,
    90  				`github.com/palantir/godel/apps/okgo/integration_test/testdata/standard/pkg2/bad2.go:16:26: _ = json.Unmarshal(nil, "")  // 2nd argument of 'Unmarshal' requires '&'`,
    91  			},
    92  		},
    93  		{
    94  			check: cmdlib.Instance().MustNewCmd("unconvert"),
    95  			want: []string{
    96  				"pkg1/bad.go:45:14: unnecessary conversion",
    97  				"pkg2/bad2.go:45:14: unnecessary conversion",
    98  			},
    99  		},
   100  		{
   101  			check: cmdlib.Instance().MustNewCmd("varcheck"),
   102  			want: []string{
   103  				"pkg1/bad.go:40:7: varcheck",
   104  				"pkg2/bad2.go:40:7: varcheck",
   105  			},
   106  		},
   107  	} {
   108  		checker, err := checks.GetChecker(currCase.check)
   109  		require.NoError(t, err)
   110  
   111  		runner := amalgomated.PathCmder(cli, amalgomated.ProxyCmdPrefix+currCase.check.Name())
   112  		lineInfo, err := checker.Check(runner, "./testdata/standard", params.OKGo{})
   113  		require.NoError(t, err, "Case %d", i)
   114  
   115  		assert.Equal(t, currCase.want, toStringSlice(lineInfo), "Case %d", i)
   116  	}
   117  }
   118  
   119  func TestCompilesChecker(t *testing.T) {
   120  	cli, err := products.Bin("okgo")
   121  	require.NoError(t, err)
   122  
   123  	wd, err := os.Getwd()
   124  	require.NoError(t, err)
   125  
   126  	tmpDir, cleanup, err := dirs.TempDir(wd, "")
   127  	defer cleanup()
   128  	require.NoError(t, err)
   129  
   130  	for i, currCase := range []struct {
   131  		check         amalgomated.Cmd
   132  		filesToWrite  []gofiles.GoFileSpec
   133  		pathToCheck   func(projectDir string) string
   134  		want          func(files map[string]gofiles.GoFile) []string
   135  		customMatcher func(caseNum int, expected, actual []string)
   136  	}{
   137  		{
   138  			check: cmdlib.Instance().MustNewCmd("compiles"),
   139  			filesToWrite: []gofiles.GoFileSpec{
   140  				{
   141  					RelPath: "foo/foo.go",
   142  					Src: `package foo
   143  func Foo() int {
   144  	return "foo"
   145  }`,
   146  				},
   147  			},
   148  			pathToCheck: func(projectDir string) string {
   149  				return path.Join(projectDir, "foo")
   150  			},
   151  			want: func(files map[string]gofiles.GoFile) []string {
   152  				return []string{
   153  					`foo.go:3:9: cannot convert "foo" (untyped string constant) to int`,
   154  				}
   155  			},
   156  		},
   157  		{
   158  			check: cmdlib.Instance().MustNewCmd("compiles"),
   159  			filesToWrite: []gofiles.GoFileSpec{
   160  				{
   161  					RelPath: "foo/foo.go",
   162  					Src: `package foo
   163  import "bar"
   164  func Foo() {
   165  	bar.Bar()
   166  }`,
   167  				},
   168  			},
   169  			pathToCheck: func(projectDir string) string {
   170  				return path.Join(projectDir, "foo")
   171  			},
   172  			want: func(files map[string]gofiles.GoFile) []string {
   173  				return []string{
   174  					`foo.go:2:8: could not import bar \(cannot find package "bar" in any of:
   175  .+ \(vendor tree\)
   176  .+ \(from \$GOROOT\)
   177  .+ \(from \$GOPATH\)\)`,
   178  					`foo.go:4:2: undeclared name: bar`,
   179  				}
   180  			},
   181  			customMatcher: func(caseNum int, want, got []string) {
   182  				assert.Equal(t, len(want), len(got), "Case %d: number of output lines do not match")
   183  
   184  				for i := range want {
   185  					assert.Regexp(t, want[i], got[i], "Case %d", i)
   186  				}
   187  			},
   188  		},
   189  		{
   190  			check: cmdlib.Instance().MustNewCmd("extimport"),
   191  			filesToWrite: []gofiles.GoFileSpec{
   192  				{
   193  					RelPath: "foo/foo.go",
   194  					Src: `package foo
   195  import "{{index . "bar/bar.go"}}"
   196  func Foo() {
   197  	bar.Bar()
   198  }
   199  `,
   200  				},
   201  				{
   202  					RelPath: "bar/bar.go",
   203  					Src:     `package bar; func Bar() {}`,
   204  				},
   205  			},
   206  			pathToCheck: func(projectDir string) string {
   207  				return path.Join(projectDir, "foo")
   208  			},
   209  			want: func(files map[string]gofiles.GoFile) []string {
   210  				return []string{
   211  					fmt.Sprintf(`foo.go:2:8: imports external package %s`, files["bar/bar.go"].ImportPath),
   212  				}
   213  			},
   214  		},
   215  	} {
   216  		currCaseProjectDir, err := ioutil.TempDir(tmpDir, "")
   217  		require.NoError(t, err, "Case %d", i)
   218  
   219  		files, err := gofiles.Write(currCaseProjectDir, currCase.filesToWrite)
   220  		require.NoError(t, err, "Case %d", i)
   221  
   222  		checker, err := checks.GetChecker(currCase.check)
   223  		require.NoError(t, err, "Case %d", i)
   224  
   225  		runner := amalgomated.PathCmder(cli, amalgomated.ProxyCmdPrefix+currCase.check.Name())
   226  		lineInfo, err := checker.Check(runner, currCase.pathToCheck(currCaseProjectDir), params.OKGo{})
   227  		require.NoError(t, err, "Case %d", i)
   228  
   229  		want := currCase.want(files)
   230  		got := toStringSlice(lineInfo)
   231  		if currCase.customMatcher == nil {
   232  			assert.Equal(t, want, got, "Case %d", i)
   233  		} else {
   234  			currCase.customMatcher(i, want, got)
   235  		}
   236  	}
   237  }
   238  
   239  func TestFilters(t *testing.T) {
   240  	cli, err := products.Bin("okgo")
   241  	require.NoError(t, err)
   242  
   243  	cmd := cmdlib.Instance().MustNewCmd("golint")
   244  	checker, err := checks.GetChecker(cmd)
   245  	require.NoError(t, err)
   246  	runner := amalgomated.PathCmder(cli, amalgomated.ProxyCmdPrefix+cmd.Name())
   247  
   248  	for i, currCase := range []struct {
   249  		filters []checkoutput.Filterer
   250  		want    []string
   251  	}{
   252  		{
   253  			filters: nil,
   254  			want: []string{
   255  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   256  				"mock/mock.go:3:1: exported function Mock should have comment or be unexported",
   257  				"nested/mock/nestedmock.go:3:1: exported function NestedMock should have comment or be unexported",
   258  			},
   259  		},
   260  		{
   261  			filters: []checkoutput.Filterer{
   262  				checkoutput.RelativePathFilter("mock"),
   263  			},
   264  			want: []string{
   265  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   266  				"nested/mock/nestedmock.go:3:1: exported function NestedMock should have comment or be unexported",
   267  			},
   268  		},
   269  		{
   270  			filters: []checkoutput.Filterer{
   271  				checkoutput.NamePathFilter("mock"),
   272  			},
   273  			want: []string{
   274  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   275  			},
   276  		},
   277  		{
   278  			filters: []checkoutput.Filterer{
   279  				checkoutput.MessageRegexpFilter("should have comment or be unexported"),
   280  			},
   281  			want: []string{},
   282  		},
   283  	} {
   284  		lineInfo, err := checker.Check(runner, "./testdata/filter", params.OKGo{})
   285  		require.NoError(t, err, "Case %d", i)
   286  
   287  		filteredLines, err := checkoutput.ApplyFilters(lineInfo, currCase.filters)
   288  		require.NoError(t, err, "Case %d", i)
   289  
   290  		assert.Equal(t, currCase.want, toStringSlice(filteredLines), "Case %d", i)
   291  	}
   292  }
   293  
   294  func TestCheckerUsesConfig(t *testing.T) {
   295  	cli, err := products.Bin("okgo")
   296  	require.NoError(t, err)
   297  
   298  	tmpDir, cleanup, err := dirs.TempDir("", "")
   299  	defer cleanup()
   300  	require.NoError(t, err)
   301  
   302  	for i, currCase := range []struct {
   303  		config string
   304  		want   []string
   305  	}{
   306  		{
   307  			config: "",
   308  			want: []string{
   309  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   310  				"mock/mock.go:3:1: exported function Mock should have comment or be unexported",
   311  				"nested/mock/nestedmock.go:3:1: exported function NestedMock should have comment or be unexported",
   312  			},
   313  		},
   314  		{
   315  			config: `
   316  			exclude:
   317  			  paths:
   318  			    - "mock"
   319  			`,
   320  			want: []string{
   321  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   322  				"nested/mock/nestedmock.go:3:1: exported function NestedMock should have comment or be unexported",
   323  			},
   324  		},
   325  		{
   326  			config: `
   327  			exclude:
   328  			  names:
   329  			    - "m.ck"
   330  			`,
   331  			want: []string{
   332  				"bad.go:3:1: exported function Bad should have comment or be unexported",
   333  			},
   334  		},
   335  		{
   336  			config: `
   337  			checks:
   338  			  golint:
   339  			    filters:
   340  			      - type: "message"
   341  			        value: "should have comment or be unexported"
   342  			`,
   343  			want: []string{},
   344  		},
   345  	} {
   346  		tmpFile, err := ioutil.TempFile(tmpDir, "")
   347  		require.NoError(t, err, "Case %d", i)
   348  		tmpFilePath := tmpFile.Name()
   349  		err = tmpFile.Close()
   350  		require.NoError(t, err, "Case %d", i)
   351  		err = ioutil.WriteFile(tmpFilePath, []byte(unindent(currCase.config)), 0644)
   352  		require.NoError(t, err, "Case %d", i)
   353  
   354  		cfg, err := config.Load(tmpFilePath, "")
   355  		require.NoError(t, err, "Case %d", i)
   356  
   357  		cmd := cmdlib.Instance().MustNewCmd("golint")
   358  		checker, err := checks.GetChecker(cmd)
   359  		require.NoError(t, err, "Case %d", i)
   360  
   361  		runner := amalgomated.PathCmder(cli, amalgomated.ProxyCmdPrefix+cmd.Name())
   362  		lineInfo, err := checker.Check(runner, "./testdata/filter", cfg)
   363  		require.NoError(t, err, "Case %d", i)
   364  
   365  		assert.Equal(t, currCase.want, toStringSlice(lineInfo), "Case %d", i)
   366  	}
   367  }
   368  
   369  func toStringSlice(input []checkoutput.Issue) []string {
   370  	output := make([]string, len(input))
   371  	for i, curr := range input {
   372  		output[i] = curr.String()
   373  	}
   374  	return output
   375  }
   376  
   377  func unindent(input string) string {
   378  	return strings.Replace(input, "\n\t\t\t", "\n", -1)
   379  }