Skip to content

Commit 2d5d29f

Browse files
authored
Add testifylint linter (#4103)
1 parent 823f02d commit 2d5d29f

File tree

7 files changed

+154
-0
lines changed

7 files changed

+154
-0
lines changed

Diff for: .golangci.reference.yml

+31
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,35 @@ linters-settings:
18861886
# Default: false
18871887
all: false
18881888

1889+
testifylint:
1890+
# Enable all checkers.
1891+
# Default: false
1892+
enable-all: true
1893+
# Enable specific checkers.
1894+
# https://github.com/Antonboom/testifylint#checkers
1895+
# Default: ["bool-compare", "compares", "empty", "error-is-as", "error-nil", "expected-actual", "float-compare", "len", "require-error", "suite-dont-use-pkg", "suite-extra-assert-call"]
1896+
enable:
1897+
- bool-compare
1898+
- compares
1899+
- empty
1900+
- error-is-as
1901+
- error-nil
1902+
- expected-actual
1903+
- float-compare
1904+
- len
1905+
- require-error
1906+
- suite-dont-use-pkg
1907+
- suite-extra-assert-call
1908+
- suite-thelper
1909+
expected-actual:
1910+
# Regexp for expected variable name.
1911+
# Default: (^(exp(ected)?|want(ed)?)([A-Z]\w*)?$)|(^(\w*[a-z])?(Exp(ected)?|Want(ed)?)$)
1912+
pattern: ^expected
1913+
suite-extra-assert-call:
1914+
# To require or remove extra Assert() call?
1915+
# Default: remove
1916+
mode: require
1917+
18891918
testpackage:
18901919
# Regexp pattern to skip files.
18911920
# Default: "(export|internal)_test\\.go"
@@ -2246,6 +2275,7 @@ linters:
22462275
- tagliatelle
22472276
- tenv
22482277
- testableexamples
2278+
- testifylint
22492279
- testpackage
22502280
- thelper
22512281
- tparallel
@@ -2360,6 +2390,7 @@ linters:
23602390
- tagliatelle
23612391
- tenv
23622392
- testableexamples
2393+
- testifylint
23632394
- testpackage
23642395
- thelper
23652396
- tparallel

Diff for: go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/Abirdcfly/dupword v0.0.13
1010
github.com/Antonboom/errname v0.1.12
1111
github.com/Antonboom/nilnil v0.1.7
12+
github.com/Antonboom/testifylint v0.2.3
1213
github.com/BurntSushi/toml v1.3.2
1314
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24
1415
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0

Diff for: go.sum

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pkg/config/linters_settings.go

+14
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ type LintersSettings struct {
218218
Stylecheck StaticCheckSettings
219219
TagAlign TagAlignSettings
220220
Tagliatelle TagliatelleSettings
221+
Testifylint TestifylintSettings
221222
Tenv TenvSettings
222223
Testpackage TestpackageSettings
223224
Thelper ThelperSettings
@@ -738,6 +739,19 @@ type TagliatelleSettings struct {
738739
}
739740
}
740741

742+
type TestifylintSettings struct {
743+
EnableAll bool `mapstructure:"enable-all"`
744+
EnabledCheckers []string `mapstructure:"enable"`
745+
746+
ExpectedActual struct {
747+
ExpVarPattern string `mapstructure:"pattern"`
748+
} `mapstructure:"expected-actual"`
749+
750+
SuiteExtraAssertCall struct {
751+
Mode string `mapstructure:"mode"`
752+
} `mapstructure:"suite-extra-assert-call"`
753+
}
754+
741755
type TestpackageSettings struct {
742756
SkipRegexp string `mapstructure:"skip-regexp"`
743757
AllowPackages []string `mapstructure:"allow-packages"`

Diff for: pkg/golinters/testifylint.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package golinters
2+
3+
import (
4+
"github.com/Antonboom/testifylint/analyzer"
5+
"golang.org/x/tools/go/analysis"
6+
7+
"github.com/golangci/golangci-lint/pkg/config"
8+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
9+
)
10+
11+
func NewTestifylint(settings *config.TestifylintSettings) *goanalysis.Linter {
12+
a := analyzer.New()
13+
14+
cfg := make(map[string]map[string]any)
15+
if settings != nil {
16+
cfg[a.Name] = map[string]any{
17+
"enable-all": settings.EnableAll,
18+
}
19+
if len(settings.EnabledCheckers) > 0 {
20+
cfg[a.Name]["enable"] = settings.EnabledCheckers
21+
}
22+
if p := settings.ExpectedActual.ExpVarPattern; p != "" {
23+
cfg[a.Name]["expected-actual.pattern"] = p
24+
}
25+
if m := settings.SuiteExtraAssertCall.Mode; m != "" {
26+
cfg[a.Name]["suite-extra-assert-call.mode"] = m
27+
}
28+
}
29+
30+
return goanalysis.NewLinter(
31+
a.Name,
32+
a.Doc,
33+
[]*analysis.Analyzer{a},
34+
cfg,
35+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
36+
}

Diff for: pkg/lint/lintersdb/manager.go

+8
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
133133
tagalignCfg *config.TagAlignSettings
134134
tagliatelleCfg *config.TagliatelleSettings
135135
tenvCfg *config.TenvSettings
136+
testifylintCfg *config.TestifylintSettings
136137
testpackageCfg *config.TestpackageSettings
137138
thelperCfg *config.ThelperSettings
138139
unparamCfg *config.UnparamSettings
@@ -213,6 +214,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
213214
tagalignCfg = &m.cfg.LintersSettings.TagAlign
214215
tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle
215216
tenvCfg = &m.cfg.LintersSettings.Tenv
217+
testifylintCfg = &m.cfg.LintersSettings.Testifylint
216218
testpackageCfg = &m.cfg.LintersSettings.Testpackage
217219
thelperCfg = &m.cfg.LintersSettings.Thelper
218220
unparamCfg = &m.cfg.LintersSettings.Unparam
@@ -788,6 +790,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
788790
WithPresets(linter.PresetTest).
789791
WithURL("https://github.com/maratori/testableexamples"),
790792

793+
linter.NewConfig(golinters.NewTestifylint(testifylintCfg)).
794+
WithSince("v1.55.0").
795+
WithPresets(linter.PresetTest, linter.PresetBugs).
796+
WithLoadForGoAnalysis().
797+
WithURL("https://github.com/Antonboom/testifylint"),
798+
791799
linter.NewConfig(golinters.NewTestpackage(testpackageCfg)).
792800
WithSince("v1.25.0").
793801
WithPresets(linter.PresetStyle, linter.PresetTest).

Diff for: test/testdata/testifylint.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//golangcitest:args -Etestifylint
2+
package testdata
3+
4+
import (
5+
"io"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
"github.com/stretchr/testify/suite"
11+
)
12+
13+
func TestTestifylint(t *testing.T) {
14+
var (
15+
predicate bool
16+
resultInt int
17+
resultFloat float64
18+
arr []string
19+
err error
20+
)
21+
22+
assert.Equal(t, predicate, true) // want "bool-compare: use assert\\.True"
23+
assert.True(t, resultInt == 1) // want "compares: use assert\\.Equal"
24+
assert.Equal(t, len(arr), 0) // want "empty: use assert\\.Empty"
25+
assert.Error(t, err, io.EOF) // want "error-is-as: invalid usage of assert\\.Error, use assert\\.ErrorIs instead"
26+
assert.Nil(t, err) // want "error-nil: use assert\\.NoError"
27+
assert.Equal(t, resultInt, 42) // want "expected-actual: need to reverse actual and expected values"
28+
assert.Equal(t, resultFloat, 42.42) // want "float-compare: use assert\\.InEpsilon \\(or InDelta\\)"
29+
assert.Equal(t, len(arr), 10) // want "len: use assert\\.Len"
30+
31+
assert.True(t, predicate)
32+
assert.Equal(t, resultInt, 1) // want "expected-actual: need to reverse actual and expected values"
33+
assert.Empty(t, arr)
34+
assert.ErrorIs(t, err, io.EOF) // want "require-error: for error assertions use require"
35+
assert.NoError(t, err) // want "require-error: for error assertions use require"
36+
assert.Equal(t, 42, resultInt)
37+
assert.InEpsilon(t, 42.42, resultFloat, 0.0001)
38+
assert.Len(t, arr, 10)
39+
40+
require.ErrorIs(t, err, io.EOF)
41+
require.NoError(t, err)
42+
43+
t.Run("formatted", func(t *testing.T) {
44+
assert.Equal(t, predicate, true, "message") // want "bool-compare: use assert\\.True"
45+
assert.Equal(t, predicate, true, "message %d", 42) // want "bool-compare: use assert\\.True"
46+
assert.Equalf(t, predicate, true, "message") // want "bool-compare: use assert\\.Truef"
47+
assert.Equalf(t, predicate, true, "message %d", 42) // want "bool-compare: use assert\\.Truef"
48+
})
49+
}
50+
51+
type SuiteExample struct {
52+
suite.Suite
53+
}
54+
55+
func TestSuiteExample(t *testing.T) {
56+
suite.Run(t, new(SuiteExample))
57+
}
58+
59+
func (s *SuiteExample) TestAll() {
60+
var b bool
61+
s.Assert().True(b) // want "suite-extra-assert-call: need to simplify the assertion to s\\.True"
62+
}

0 commit comments

Comments
 (0)