github.com/nozzle/golangci-lint@v1.49.0-nz3/test/run_test.go (about)

     1  package test
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	_ "github.com/valyala/quicktemplate"
     9  
    10  	"github.com/golangci/golangci-lint/pkg/exitcodes"
    11  	"github.com/golangci/golangci-lint/test/testshared"
    12  )
    13  
    14  const minimalPkg = "minimalpkg"
    15  
    16  func TestAutogeneratedNoIssues(t *testing.T) {
    17  	testshared.NewRunnerBuilder(t).
    18  		WithTargetPath(testdataDir, "autogenerated").
    19  		Runner().
    20  		Install().
    21  		Run().
    22  		ExpectNoIssues()
    23  }
    24  
    25  func TestEmptyDirRun(t *testing.T) {
    26  	testshared.NewRunnerBuilder(t).
    27  		WithEnviron("GO111MODULE=off").
    28  		WithTargetPath(testdataDir, "nogofiles").
    29  		Runner().
    30  		Install().
    31  		Run().
    32  		ExpectExitCode(exitcodes.NoGoFiles).
    33  		ExpectOutputContains(": no go files to analyze")
    34  }
    35  
    36  func TestNotExistingDirRun(t *testing.T) {
    37  	testshared.NewRunnerBuilder(t).
    38  		WithEnviron("GO111MODULE=off").
    39  		WithTargetPath(testdataDir, "no_such_dir").
    40  		Runner().
    41  		Install().
    42  		Run().
    43  		ExpectExitCode(exitcodes.Failure).
    44  		ExpectOutputContains("cannot find package").
    45  		ExpectOutputContains(testshared.NormalizeFileInString("/testdata/no_such_dir"))
    46  }
    47  
    48  func TestSymlinkLoop(t *testing.T) {
    49  	testshared.NewRunnerBuilder(t).
    50  		WithTargetPath(testdataDir, "symlink_loop", "...").
    51  		Runner().
    52  		Install().
    53  		Run().
    54  		ExpectNoIssues()
    55  }
    56  
    57  // TODO(ldez): remove this in v2.
    58  func TestDeadline(t *testing.T) {
    59  	projectRoot := filepath.Join("..", "...")
    60  
    61  	testshared.NewRunnerBuilder(t).
    62  		WithArgs("--deadline=1ms").
    63  		WithTargetPath(projectRoot).
    64  		Runner().
    65  		Install().
    66  		Run().
    67  		ExpectExitCode(exitcodes.Timeout).
    68  		ExpectOutputContains(`Timeout exceeded: try increasing it by passing --timeout option`)
    69  }
    70  
    71  func TestTimeout(t *testing.T) {
    72  	projectRoot := filepath.Join("..", "...")
    73  
    74  	testshared.NewRunnerBuilder(t).
    75  		WithArgs("--timeout=1ms").
    76  		WithTargetPath(projectRoot).
    77  		Runner().
    78  		Install().
    79  		Run().
    80  		ExpectExitCode(exitcodes.Timeout).
    81  		ExpectOutputContains(`Timeout exceeded: try increasing it by passing --timeout option`)
    82  }
    83  
    84  func TestTimeoutInConfig(t *testing.T) {
    85  	cases := []struct {
    86  		cfg string
    87  	}{
    88  		{
    89  			cfg: `
    90  				run:
    91  					deadline: 1ms
    92  			`,
    93  		},
    94  		{
    95  			cfg: `
    96  				run:
    97  					timeout: 1ms
    98  			`,
    99  		},
   100  		{
   101  			// timeout should override deadline
   102  			cfg: `
   103  				run:
   104  					deadline: 100s
   105  					timeout: 1ms
   106  			`,
   107  		},
   108  	}
   109  
   110  	testshared.InstallGolangciLint(t)
   111  
   112  	for _, c := range cases {
   113  		// Run with disallowed option set only in config
   114  		testshared.NewRunnerBuilder(t).
   115  			WithConfig(c.cfg).
   116  			WithTargetPath(testdataDir, minimalPkg).
   117  			Runner().
   118  			Run().
   119  			ExpectExitCode(exitcodes.Timeout).
   120  			ExpectOutputContains(`Timeout exceeded: try increasing it by passing --timeout option`)
   121  	}
   122  }
   123  
   124  func TestTestsAreLintedByDefault(t *testing.T) {
   125  	testshared.NewRunnerBuilder(t).
   126  		WithTargetPath(testdataDir, "withtests").
   127  		Runner().
   128  		Install().
   129  		Run().
   130  		ExpectHasIssue("don't use `init` function")
   131  }
   132  
   133  func TestCgoOk(t *testing.T) {
   134  	testshared.NewRunnerBuilder(t).
   135  		WithNoConfig().
   136  		WithArgs(
   137  			"--timeout=3m",
   138  			"--enable-all",
   139  			"-D",
   140  			"nosnakecase,gci",
   141  		).
   142  		WithTargetPath(testdataDir, "cgo").
   143  		Runner().
   144  		Install().
   145  		Run().
   146  		ExpectNoIssues()
   147  }
   148  
   149  func TestCgoWithIssues(t *testing.T) {
   150  	testshared.InstallGolangciLint(t)
   151  
   152  	testCases := []struct {
   153  		desc     string
   154  		args     []string
   155  		dir      string
   156  		expected string
   157  	}{
   158  		{
   159  			desc:     "govet",
   160  			args:     []string{"--no-config", "--disable-all", "-Egovet"},
   161  			dir:      "cgo_with_issues",
   162  			expected: "Printf format %t has arg cs of wrong type",
   163  		},
   164  		{
   165  			desc:     "staticcheck",
   166  			args:     []string{"--no-config", "--disable-all", "-Estaticcheck"},
   167  			dir:      "cgo_with_issues",
   168  			expected: "SA5009: Printf format %t has arg #1 of wrong type",
   169  		},
   170  		{
   171  			desc:     "gofmt",
   172  			args:     []string{"--no-config", "--disable-all", "-Egofmt"},
   173  			dir:      "cgo_with_issues",
   174  			expected: "File is not `gofmt`-ed with `-s` (gofmt)",
   175  		},
   176  		{
   177  			desc:     "revive",
   178  			args:     []string{"--no-config", "--disable-all", "-Erevive"},
   179  			dir:      "cgo_with_issues",
   180  			expected: "indent-error-flow: if block ends with a return statement",
   181  		},
   182  	}
   183  
   184  	for _, test := range testCases {
   185  		test := test
   186  		t.Run(test.desc, func(t *testing.T) {
   187  			t.Parallel()
   188  
   189  			testshared.NewRunnerBuilder(t).
   190  				WithArgs(test.args...).
   191  				WithTargetPath(testdataDir, test.dir).
   192  				Runner().
   193  				Run().
   194  				ExpectHasIssue(test.expected)
   195  		})
   196  	}
   197  }
   198  
   199  // https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
   200  func TestLineDirective(t *testing.T) {
   201  	testshared.InstallGolangciLint(t)
   202  
   203  	testCases := []struct {
   204  		desc       string
   205  		args       []string
   206  		configPath string
   207  		targetPath string
   208  		expected   string
   209  	}{
   210  		{
   211  			desc: "dupl",
   212  			args: []string{
   213  				"-Edupl",
   214  				"--disable-all",
   215  			},
   216  			configPath: "testdata/linedirective/dupl.yml",
   217  			targetPath: "linedirective",
   218  			expected:   "21-23 lines are duplicate of `testdata/linedirective/hello.go:25-27` (dupl)",
   219  		},
   220  		{
   221  			desc: "gofmt",
   222  			args: []string{
   223  				"-Egofmt",
   224  				"--disable-all",
   225  			},
   226  			targetPath: "linedirective",
   227  			expected:   "File is not `gofmt`-ed with `-s` (gofmt)",
   228  		},
   229  		{
   230  			desc: "goimports",
   231  			args: []string{
   232  				"-Egoimports",
   233  				"--disable-all",
   234  			},
   235  			targetPath: "linedirective",
   236  			expected:   "File is not `goimports`-ed (goimports)",
   237  		},
   238  		{
   239  			desc: "gomodguard",
   240  			args: []string{
   241  				"-Egomodguard",
   242  				"--disable-all",
   243  			},
   244  			configPath: "testdata/linedirective/gomodguard.yml",
   245  			targetPath: "linedirective",
   246  			expected: "import of package `github.com/ryancurrah/gomodguard` is blocked because the module is not " +
   247  				"in the allowed modules list. (gomodguard)",
   248  		},
   249  		{
   250  			desc: "lll",
   251  			args: []string{
   252  				"-Elll",
   253  				"--disable-all",
   254  			},
   255  			configPath: "testdata/linedirective/lll.yml",
   256  			targetPath: "linedirective",
   257  			expected:   "line is 57 characters (lll)",
   258  		},
   259  		{
   260  			desc: "misspell",
   261  			args: []string{
   262  				"-Emisspell",
   263  				"--disable-all",
   264  			},
   265  			configPath: "",
   266  			targetPath: "linedirective",
   267  			expected:   "is a misspelling of `language` (misspell)",
   268  		},
   269  		{
   270  			desc: "wsl",
   271  			args: []string{
   272  				"-Ewsl",
   273  				"--disable-all",
   274  			},
   275  			configPath: "",
   276  			targetPath: "linedirective",
   277  			expected:   "block should not start with a whitespace (wsl)",
   278  		},
   279  	}
   280  
   281  	for _, test := range testCases {
   282  		test := test
   283  		t.Run(test.desc, func(t *testing.T) {
   284  			t.Parallel()
   285  
   286  			testshared.NewRunnerBuilder(t).
   287  				WithArgs(test.args...).
   288  				WithTargetPath(testdataDir, test.targetPath).
   289  				WithConfigFile(test.configPath).
   290  				Runner().
   291  				Run().
   292  				ExpectHasIssue(test.expected)
   293  		})
   294  	}
   295  }
   296  
   297  // https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
   298  func TestLineDirectiveProcessedFiles(t *testing.T) {
   299  	testCases := []struct {
   300  		desc     string
   301  		args     []string
   302  		target   string
   303  		expected []string
   304  	}{
   305  		{
   306  			desc: "lite loading",
   307  			args: []string{
   308  				"--print-issued-lines=false",
   309  				"--exclude-use-default=false",
   310  				"-Erevive",
   311  			},
   312  			target: "quicktemplate",
   313  			expected: []string{
   314  				"testdata/quicktemplate/hello.qtpl.go:10:1: package-comments: should have a package comment (revive)",
   315  				"testdata/quicktemplate/hello.qtpl.go:26:1: exported: exported function StreamHello should have comment or be unexported (revive)",
   316  				"testdata/quicktemplate/hello.qtpl.go:39:1: exported: exported function WriteHello should have comment or be unexported (revive)",
   317  				"testdata/quicktemplate/hello.qtpl.go:50:1: exported: exported function Hello should have comment or be unexported (revive)",
   318  			},
   319  		},
   320  		{
   321  			desc: "full loading",
   322  			args: []string{
   323  				"--print-issued-lines=false",
   324  				"--exclude-use-default=false",
   325  				"-Erevive,govet",
   326  			},
   327  			target: "quicktemplate",
   328  			expected: []string{
   329  				"testdata/quicktemplate/hello.qtpl.go:10:1: package-comments: should have a package comment (revive)",
   330  				"testdata/quicktemplate/hello.qtpl.go:26:1: exported: exported function StreamHello should have comment or be unexported (revive)",
   331  				"testdata/quicktemplate/hello.qtpl.go:39:1: exported: exported function WriteHello should have comment or be unexported (revive)",
   332  				"testdata/quicktemplate/hello.qtpl.go:50:1: exported: exported function Hello should have comment or be unexported (revive)",
   333  			},
   334  		},
   335  	}
   336  
   337  	for _, test := range testCases {
   338  		test := test
   339  		t.Run(test.desc, func(t *testing.T) {
   340  			t.Parallel()
   341  
   342  			testshared.NewRunnerBuilder(t).
   343  				WithNoConfig().
   344  				WithArgs(test.args...).
   345  				WithTargetPath(testdataDir, test.target).
   346  				Runner().
   347  				Install().
   348  				Run().
   349  				ExpectExitCode(exitcodes.IssuesFound).
   350  				ExpectOutputContains(test.expected...)
   351  		})
   352  	}
   353  }
   354  
   355  func TestUnsafeOk(t *testing.T) {
   356  	testshared.NewRunnerBuilder(t).
   357  		WithNoConfig().
   358  		WithArgs("--enable-all").
   359  		WithTargetPath(testdataDir, "unsafe").
   360  		Runner().
   361  		Install().
   362  		Run().
   363  		ExpectNoIssues()
   364  }
   365  
   366  func TestSortedResults(t *testing.T) {
   367  	testCases := []struct {
   368  		opt  string
   369  		want string
   370  	}{
   371  		{
   372  			opt: "--sort-results=false",
   373  			want: "testdata/sort_results/main.go:15:13: Error return value is not checked (errcheck)" + "\n" +
   374  				"testdata/sort_results/main.go:12:5: var `db` is unused (unused)",
   375  		},
   376  		{
   377  			opt: "--sort-results=true",
   378  			want: "testdata/sort_results/main.go:12:5: var `db` is unused (unused)" + "\n" +
   379  				"testdata/sort_results/main.go:15:13: Error return value is not checked (errcheck)",
   380  		},
   381  	}
   382  
   383  	testshared.InstallGolangciLint(t)
   384  
   385  	for _, test := range testCases {
   386  		test := test
   387  		t.Run(test.opt, func(t *testing.T) {
   388  			t.Parallel()
   389  
   390  			testshared.NewRunnerBuilder(t).
   391  				WithNoConfig().
   392  				WithArgs("--print-issued-lines=false", test.opt).
   393  				WithTargetPath(testdataDir, "sort_results").
   394  				Runner().
   395  				Run().
   396  				ExpectExitCode(exitcodes.IssuesFound).ExpectOutputEq(test.want + "\n")
   397  		})
   398  	}
   399  }
   400  
   401  func TestSkippedDirsNoMatchArg(t *testing.T) {
   402  	dir := filepath.Join(testdataDir, "skipdirs", "skip_me", "nested")
   403  
   404  	testshared.NewRunnerBuilder(t).
   405  		WithNoConfig().
   406  		WithArgs(
   407  			"--print-issued-lines=false",
   408  			"--skip-dirs", dir,
   409  			"-Erevive",
   410  		).
   411  		WithTargetPath(dir).
   412  		Runner().
   413  		Install().
   414  		Run().
   415  		ExpectExitCode(exitcodes.IssuesFound).
   416  		ExpectOutputEq("testdata/skipdirs/skip_me/nested/with_issue.go:8:9: " +
   417  			"indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)\n")
   418  }
   419  
   420  func TestSkippedDirsTestdata(t *testing.T) {
   421  	testshared.NewRunnerBuilder(t).
   422  		WithNoConfig().
   423  		WithArgs(
   424  			"--print-issued-lines=false",
   425  			"-Erevive",
   426  		).
   427  		WithTargetPath(testdataDir, "skipdirs", "...").
   428  		Runner().
   429  		Install().
   430  		Run().
   431  		ExpectNoIssues() // all was skipped because in testdata
   432  }
   433  
   434  func TestIdentifierUsedOnlyInTests(t *testing.T) {
   435  	testshared.NewRunnerBuilder(t).
   436  		WithNoConfig().
   437  		WithArgs("--disable-all", "-Eunused").
   438  		WithTargetPath(testdataDir, "used_only_in_tests").
   439  		Runner().
   440  		Install().
   441  		Run().
   442  		ExpectNoIssues()
   443  }
   444  
   445  func TestUnusedCheckExported(t *testing.T) {
   446  	testshared.NewRunnerBuilder(t).
   447  		WithConfigFile("testdata_etc/unused_exported/golangci.yml").
   448  		WithTargetPath("testdata_etc/unused_exported/...").
   449  		Runner().
   450  		Install().
   451  		Run().
   452  		ExpectNoIssues()
   453  }
   454  
   455  func TestConfigFileIsDetected(t *testing.T) {
   456  	testshared.InstallGolangciLint(t)
   457  
   458  	testCases := []struct {
   459  		desc       string
   460  		targetPath string
   461  	}{
   462  		{
   463  			desc:       "explicit",
   464  			targetPath: filepath.Join(testdataDir, "withconfig", "pkg"),
   465  		},
   466  		{
   467  			desc:       "recursive",
   468  			targetPath: filepath.Join(testdataDir, "withconfig", "..."),
   469  		},
   470  	}
   471  
   472  	for _, test := range testCases {
   473  		test := test
   474  		t.Run(test.desc, func(t *testing.T) {
   475  			t.Parallel()
   476  
   477  			testshared.NewRunnerBuilder(t).
   478  				// WithNoConfig().
   479  				WithTargetPath(test.targetPath).
   480  				Runner().
   481  				Run().
   482  				ExpectExitCode(exitcodes.Success).
   483  				// test config contains InternalTest: true, it triggers such output
   484  				ExpectOutputEq("test\n")
   485  		})
   486  	}
   487  }
   488  
   489  func TestEnableAllFastAndEnableCanCoexist(t *testing.T) {
   490  	testshared.InstallGolangciLint(t)
   491  
   492  	testCases := []struct {
   493  		desc     string
   494  		args     []string
   495  		expected []int
   496  	}{
   497  		{
   498  			desc:     "fast",
   499  			args:     []string{"--fast", "--enable-all", "--enable=typecheck"},
   500  			expected: []int{exitcodes.Success, exitcodes.IssuesFound},
   501  		},
   502  		{
   503  			desc:     "all",
   504  			args:     []string{"--enable-all", "--enable=typecheck"},
   505  			expected: []int{exitcodes.Failure},
   506  		},
   507  	}
   508  
   509  	for _, test := range testCases {
   510  		test := test
   511  		t.Run(test.desc, func(t *testing.T) {
   512  			t.Parallel()
   513  
   514  			testshared.NewRunnerBuilder(t).
   515  				WithNoConfig().
   516  				WithArgs(test.args...).
   517  				WithTargetPath(testdataDir, minimalPkg).
   518  				Runner().
   519  				Run().
   520  				ExpectExitCode(test.expected...)
   521  		})
   522  	}
   523  }
   524  
   525  func TestEnabledPresetsAreNotDuplicated(t *testing.T) {
   526  	testshared.NewRunnerBuilder(t).
   527  		WithNoConfig().
   528  		WithArgs("-v", "-p", "style,bugs").
   529  		WithTargetPath(testdataDir, minimalPkg).
   530  		Runner().
   531  		Install().
   532  		Run().
   533  		ExpectOutputContains("Active presets: [bugs style]")
   534  }
   535  
   536  func TestAbsPathDirAnalysis(t *testing.T) {
   537  	dir := filepath.Join("testdata_etc", "abspath") // abs paths don't work with testdata dir
   538  	absDir, err := filepath.Abs(dir)
   539  	assert.NoError(t, err)
   540  
   541  	testshared.NewRunnerBuilder(t).
   542  		WithNoConfig().
   543  		WithArgs(
   544  			"--print-issued-lines=false",
   545  			"-Erevive",
   546  		).
   547  		WithTargetPath(absDir).
   548  		Runner().
   549  		Install().
   550  		Run().
   551  		ExpectHasIssue("testdata_etc/abspath/with_issue.go:8:9: " +
   552  			"indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)")
   553  }
   554  
   555  func TestAbsPathFileAnalysis(t *testing.T) {
   556  	dir := filepath.Join("testdata_etc", "abspath", "with_issue.go") // abs paths don't work with testdata dir
   557  	absDir, err := filepath.Abs(dir)
   558  	assert.NoError(t, err)
   559  
   560  	testshared.NewRunnerBuilder(t).
   561  		WithNoConfig().
   562  		WithArgs(
   563  			"--print-issued-lines=false",
   564  			"-Erevive",
   565  		).
   566  		WithTargetPath(absDir).
   567  		Runner().
   568  		Install().
   569  		Run().
   570  		ExpectHasIssue("indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)")
   571  }
   572  
   573  func TestDisallowedOptionsInConfig(t *testing.T) {
   574  	cases := []struct {
   575  		cfg    string
   576  		option string
   577  	}{
   578  		{
   579  			cfg: `
   580  				ruN:
   581  					Args:
   582  						- 1
   583  			`,
   584  		},
   585  		{
   586  			cfg: `
   587  				run:
   588  					CPUProfilePath: path
   589  			`,
   590  			option: "--cpu-profile-path=path",
   591  		},
   592  		{
   593  			cfg: `
   594  				run:
   595  					MemProfilePath: path
   596  			`,
   597  			option: "--mem-profile-path=path",
   598  		},
   599  		{
   600  			cfg: `
   601  				run:
   602  					TracePath: path
   603  			`,
   604  			option: "--trace-path=path",
   605  		},
   606  		{
   607  			cfg: `
   608  				run:
   609  					Verbose: true
   610  			`,
   611  			option: "-v",
   612  		},
   613  	}
   614  
   615  	testshared.InstallGolangciLint(t)
   616  
   617  	for _, c := range cases {
   618  		// Run with disallowed option set only in config
   619  		testshared.NewRunnerBuilder(t).
   620  			WithConfig(c.cfg).
   621  			WithTargetPath(testdataDir, minimalPkg).
   622  			Runner().
   623  			Run().
   624  			ExpectExitCode(exitcodes.Failure)
   625  
   626  		if c.option == "" {
   627  			continue
   628  		}
   629  
   630  		args := []string{c.option, "--fast"}
   631  
   632  		// Run with disallowed option set only in command-line
   633  		testshared.NewRunnerBuilder(t).
   634  			WithNoConfig().
   635  			WithArgs(args...).
   636  			WithTargetPath(testdataDir, minimalPkg).
   637  			Runner().
   638  			Run().
   639  			ExpectExitCode(exitcodes.Success)
   640  
   641  		// Run with disallowed option set both in command-line and in config
   642  
   643  		testshared.NewRunnerBuilder(t).
   644  			WithConfig(c.cfg).
   645  			WithArgs(args...).
   646  			WithTargetPath(testdataDir, minimalPkg).
   647  			Runner().
   648  			Run().
   649  			ExpectExitCode(exitcodes.Failure)
   650  	}
   651  }
   652  
   653  func TestPathPrefix(t *testing.T) {
   654  	testCases := []struct {
   655  		desc    string
   656  		args    []string
   657  		pattern string
   658  	}{
   659  		{
   660  			desc:    "empty",
   661  			pattern: "^testdata/withtests/",
   662  		},
   663  		{
   664  			desc:    "prefixed",
   665  			args:    []string{"--path-prefix=cool"},
   666  			pattern: "^cool/testdata/withtests",
   667  		},
   668  	}
   669  
   670  	testshared.InstallGolangciLint(t)
   671  
   672  	for _, test := range testCases {
   673  		test := test
   674  		t.Run(test.desc, func(t *testing.T) {
   675  			testshared.NewRunnerBuilder(t).
   676  				WithArgs(test.args...).
   677  				WithTargetPath(testdataDir, "withtests").
   678  				Runner().
   679  				Run().
   680  				ExpectOutputRegexp(test.pattern)
   681  		})
   682  	}
   683  }