github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/golinters/nolintlint/nolintlint_test.go (about)

     1  package nolintlint
     2  
     3  import (
     4  	"go/parser"
     5  	"go/token"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/chenfeining/golangci-lint/pkg/result"
    12  )
    13  
    14  func TestLinter_Run(t *testing.T) {
    15  	type issueWithReplacement struct {
    16  		issue       string
    17  		replacement *result.Replacement
    18  	}
    19  	testCases := []struct {
    20  		desc     string
    21  		needs    Needs
    22  		excludes []string
    23  		contents string
    24  		expected []issueWithReplacement
    25  	}{
    26  		{
    27  			desc:  "when no explanation is provided",
    28  			needs: NeedsExplanation,
    29  			contents: `
    30  package bar
    31  
    32  // example
    33  //nolint
    34  func foo() {
    35    bad() //nolint
    36    bad() //nolint //
    37    bad() //nolint // 
    38    good() //nolint // this is ok
    39  	other() //nolintother
    40  }`,
    41  			expected: []issueWithReplacement{
    42  				{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:5:1"},
    43  				{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:7:9"},
    44  				{issue: "directive `//nolint //` should provide explanation such as `//nolint // this is why` at testing.go:8:9"},
    45  				{issue: "directive `//nolint // ` should provide explanation such as `//nolint // this is why` at testing.go:9:9"},
    46  			},
    47  		},
    48  		{
    49  			desc:  "when multiple directives on multiple lines",
    50  			needs: NeedsExplanation,
    51  			contents: `
    52  package bar
    53  
    54  // example
    55  //nolint // this is ok
    56  //nolint:dupl
    57  func foo() {}`,
    58  			expected: []issueWithReplacement{
    59  				{issue: "directive `//nolint:dupl` should provide explanation such as `//nolint:dupl // this is why` at testing.go:6:1"},
    60  			},
    61  		},
    62  		{
    63  			desc:     "when no explanation is needed for a specific linter",
    64  			needs:    NeedsExplanation,
    65  			excludes: []string{"lll"},
    66  			contents: `
    67  package bar
    68  
    69  func foo() {
    70  	thisIsAReallyLongLine() //nolint:lll
    71  }`,
    72  		},
    73  		{
    74  			desc:  "when no specific linter is mentioned",
    75  			needs: NeedsSpecific,
    76  			contents: `
    77  package bar
    78  
    79  func foo() {
    80    good() //nolint:my-linter
    81    bad() //nolint
    82    bad() //nolint // because
    83  }`,
    84  			expected: []issueWithReplacement{
    85  				{issue: "directive `//nolint` should mention specific linter such as `//nolint:my-linter` at testing.go:6:9"},
    86  				{issue: "directive `//nolint // because` should mention specific linter such as `//nolint:my-linter` at testing.go:7:9"},
    87  			},
    88  		},
    89  		{
    90  			desc: "when machine-readable style isn't used",
    91  			contents: `
    92  package bar
    93  
    94  func foo() {
    95    bad() // nolint
    96    bad() //   nolint
    97    good() //nolint
    98  }`,
    99  			expected: []issueWithReplacement{
   100  				{
   101  					issue: "directive `// nolint` should be written without leading space as `//nolint` at testing.go:5:9",
   102  					replacement: &result.Replacement{
   103  						Inline: &result.InlineFix{
   104  							StartCol:  10,
   105  							Length:    1,
   106  							NewString: "",
   107  						},
   108  					},
   109  				},
   110  				{
   111  					issue: "directive `//   nolint` should be written without leading space as `//nolint` at testing.go:6:9",
   112  					replacement: &result.Replacement{
   113  						Inline: &result.InlineFix{
   114  							StartCol:  10,
   115  							Length:    3,
   116  							NewString: "",
   117  						},
   118  					},
   119  				},
   120  			},
   121  		},
   122  		{
   123  			desc: "spaces are allowed in comma-separated list of linters",
   124  			contents: `
   125  package bar
   126  
   127  func foo() {
   128    good() //nolint:linter1,linter-two
   129    bad() //nolint:linter1 linter2
   130    good() //nolint: linter1,linter2
   131    good() //nolint: linter1, linter2
   132  }`,
   133  			expected: []issueWithReplacement{
   134  				{issue: "directive `//nolint:linter1 linter2` should match `//nolint[:<comma-separated-linters>] [// <explanation>]` at testing.go:6:9"}, //nolint:lll // this is a string
   135  			},
   136  		},
   137  		{
   138  			desc: "multi-line comments don't confuse parser",
   139  			contents: `
   140  package bar
   141  
   142  func foo() {
   143    //nolint:test
   144    // something else
   145  }`,
   146  		},
   147  		{
   148  			desc:  "needs unused without specific linter generates replacement",
   149  			needs: NeedsUnused,
   150  			contents: `
   151  package bar
   152  
   153  func foo() {
   154    bad() //nolint
   155  }`,
   156  			expected: []issueWithReplacement{
   157  				{
   158  					issue: "directive `//nolint` is unused at testing.go:5:9",
   159  					replacement: &result.Replacement{
   160  						Inline: &result.InlineFix{
   161  							StartCol:  8,
   162  							Length:    8,
   163  							NewString: "",
   164  						},
   165  					},
   166  				},
   167  			},
   168  		},
   169  		{
   170  			desc:  "needs unused with one specific linter generates replacement",
   171  			needs: NeedsUnused,
   172  			contents: `
   173  package bar
   174  
   175  func foo() {
   176    bad() //nolint:somelinter
   177  }`,
   178  			expected: []issueWithReplacement{
   179  				{
   180  					issue: "directive `//nolint:somelinter` is unused for linter \"somelinter\" at testing.go:5:9",
   181  					replacement: &result.Replacement{
   182  						Inline: &result.InlineFix{
   183  							StartCol:  8,
   184  							Length:    19,
   185  							NewString: "",
   186  						},
   187  					},
   188  				},
   189  			},
   190  		},
   191  		{
   192  			desc:  "needs unused with multiple specific linters does not generate replacements",
   193  			needs: NeedsUnused,
   194  			contents: `
   195  package bar
   196  
   197  func foo() {
   198    bad() //nolint:linter1,linter2
   199  }`,
   200  			expected: []issueWithReplacement{
   201  				{
   202  					issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter1\" at testing.go:5:9",
   203  				},
   204  				{
   205  					issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter2\" at testing.go:5:9",
   206  				},
   207  			},
   208  		},
   209  	}
   210  
   211  	for _, test := range testCases {
   212  		test := test
   213  		t.Run(test.desc, func(t *testing.T) {
   214  			t.Parallel()
   215  
   216  			linter, _ := NewLinter(test.needs, test.excludes)
   217  
   218  			fset := token.NewFileSet()
   219  			expr, err := parser.ParseFile(fset, "testing.go", test.contents, parser.ParseComments)
   220  			require.NoError(t, err)
   221  
   222  			actualIssues, err := linter.Run(fset, expr)
   223  			require.NoError(t, err)
   224  
   225  			actualIssuesWithReplacements := make([]issueWithReplacement, 0, len(actualIssues))
   226  			for _, i := range actualIssues {
   227  				actualIssuesWithReplacements = append(actualIssuesWithReplacements, issueWithReplacement{
   228  					issue:       i.String(),
   229  					replacement: i.Replacement(),
   230  				})
   231  			}
   232  
   233  			assert.ElementsMatch(t, test.expected, actualIssuesWithReplacements,
   234  				"expected %s \nbut got %s", test.expected, actualIssuesWithReplacements)
   235  		})
   236  	}
   237  }