golangci-lint/pkg/golinters/nolintlint/nolintlint_test.go

238 lines
5.7 KiB
Go

package nolintlint
import (
"go/parser"
"go/token"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/golangci/golangci-lint/pkg/result"
)
func TestLinter_Run(t *testing.T) {
type issueWithReplacement struct {
issue string
replacement *result.Replacement
}
testCases := []struct {
desc string
needs Needs
excludes []string
contents string
expected []issueWithReplacement
}{
{
desc: "when no explanation is provided",
needs: NeedsExplanation,
contents: `
package bar
// example
//nolint
func foo() {
bad() //nolint
bad() //nolint //
bad() //nolint //
good() //nolint // this is ok
other() //nolintother
}`,
expected: []issueWithReplacement{
{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:5:1"},
{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:7:9"},
{issue: "directive `//nolint //` should provide explanation such as `//nolint // this is why` at testing.go:8:9"},
{issue: "directive `//nolint // ` should provide explanation such as `//nolint // this is why` at testing.go:9:9"},
},
},
{
desc: "when multiple directives on multiple lines",
needs: NeedsExplanation,
contents: `
package bar
// example
//nolint // this is ok
//nolint:dupl
func foo() {}`,
expected: []issueWithReplacement{
{issue: "directive `//nolint:dupl` should provide explanation such as `//nolint:dupl // this is why` at testing.go:6:1"},
},
},
{
desc: "when no explanation is needed for a specific linter",
needs: NeedsExplanation,
excludes: []string{"lll"},
contents: `
package bar
func foo() {
thisIsAReallyLongLine() //nolint:lll
}`,
},
{
desc: "when no specific linter is mentioned",
needs: NeedsSpecific,
contents: `
package bar
func foo() {
good() //nolint:my-linter
bad() //nolint
bad() //nolint // because
}`,
expected: []issueWithReplacement{
{issue: "directive `//nolint` should mention specific linter such as `//nolint:my-linter` at testing.go:6:9"},
{issue: "directive `//nolint // because` should mention specific linter such as `//nolint:my-linter` at testing.go:7:9"},
},
},
{
desc: "when machine-readable style isn't used",
contents: `
package bar
func foo() {
bad() // nolint
bad() // nolint
good() //nolint
}`,
expected: []issueWithReplacement{
{
issue: "directive `// nolint` should be written without leading space as `//nolint` at testing.go:5:9",
replacement: &result.Replacement{
Inline: &result.InlineFix{
StartCol: 10,
Length: 1,
NewString: "",
},
},
},
{
issue: "directive `// nolint` should be written without leading space as `//nolint` at testing.go:6:9",
replacement: &result.Replacement{
Inline: &result.InlineFix{
StartCol: 10,
Length: 3,
NewString: "",
},
},
},
},
},
{
desc: "spaces are allowed in comma-separated list of linters",
contents: `
package bar
func foo() {
good() //nolint:linter1,linter-two
bad() //nolint:linter1 linter2
good() //nolint: linter1,linter2
good() //nolint: linter1, linter2
}`,
expected: []issueWithReplacement{
{issue: "directive `//nolint:linter1 linter2` should match `//nolint[:<comma-separated-linters>] [// <explanation>]` at testing.go:6:9"},
},
},
{
desc: "multi-line comments don't confuse parser",
contents: `
package bar
func foo() {
//nolint:test
// something else
}`,
},
{
desc: "needs unused without specific linter generates replacement",
needs: NeedsUnused,
contents: `
package bar
func foo() {
bad() //nolint
}`,
expected: []issueWithReplacement{
{
issue: "directive `//nolint` is unused at testing.go:5:9",
replacement: &result.Replacement{
Inline: &result.InlineFix{
StartCol: 8,
Length: 8,
NewString: "",
},
},
},
},
},
{
desc: "needs unused with one specific linter generates replacement",
needs: NeedsUnused,
contents: `
package bar
func foo() {
bad() //nolint:somelinter
}`,
expected: []issueWithReplacement{
{
issue: "directive `//nolint:somelinter` is unused for linter \"somelinter\" at testing.go:5:9",
replacement: &result.Replacement{
Inline: &result.InlineFix{
StartCol: 8,
Length: 19,
NewString: "",
},
},
},
},
},
{
desc: "needs unused with multiple specific linters does not generate replacements",
needs: NeedsUnused,
contents: `
package bar
func foo() {
bad() //nolint:linter1,linter2
}`,
expected: []issueWithReplacement{
{
issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter1\" at testing.go:5:9",
},
{
issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter2\" at testing.go:5:9",
},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
linter, _ := NewLinter(test.needs, test.excludes)
fset := token.NewFileSet()
expr, err := parser.ParseFile(fset, "testing.go", test.contents, parser.ParseComments)
require.NoError(t, err)
actualIssues, err := linter.Run(fset, expr)
require.NoError(t, err)
actualIssuesWithReplacements := make([]issueWithReplacement, 0, len(actualIssues))
for _, i := range actualIssues {
actualIssuesWithReplacements = append(actualIssuesWithReplacements, issueWithReplacement{
issue: i.String(),
replacement: i.Replacement(),
})
}
assert.ElementsMatch(t, test.expected, actualIssuesWithReplacements,
"expected %s \nbut got %s", test.expected, actualIssuesWithReplacements)
})
}
}