From 992f53281f7fb1a199e63c2290d63f268200c275 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Wed, 3 May 2023 18:45:00 +0300 Subject: [PATCH 1/3] feat: allow to specify Go version --- README.md | 4 ++- tenv.go | 43 +++++++++++++++++++++++++++++++- tenv_test.go | 7 ++++++ testdata/src/go116/go.mod | 3 +++ testdata/src/go116/go116_test.go | 12 +++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 testdata/src/go116/go.mod create mode 100644 testdata/src/go116/go116_test.go diff --git a/README.md b/README.md index c5d0047..30921d5 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ go vet -vettool=(which tenv) ./... ### option -The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. +The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + +The option `go` allows to specify Go version. If the version is not empty and lower than Go 1.17 the analysis will be skipped. By default, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. diff --git a/tenv.go b/tenv.go index fcff98d..000a7e9 100644 --- a/tenv.go +++ b/tenv.go @@ -2,6 +2,7 @@ package tenv import ( "go/ast" + "strconv" "strings" "golang.org/x/tools/go/analysis" @@ -24,13 +25,22 @@ var Analyzer = &analysis.Analyzer{ var ( A = "all" aflag bool + + Go = "go" + goflag string ) func init() { Analyzer.Flags.BoolVar(&aflag, A, false, "the all option will run against all method in test file") + Analyzer.Flags.StringVar(&goflag, Go, "1.17", "Go version") } func run(pass *analysis.Pass) (interface{}, error) { + if goVersionLower117(goflag) { + // Do nothing because T.Setenv added in go1.17 + return nil, nil + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ @@ -44,7 +54,6 @@ func run(pass *analysis.Pass) (interface{}, error) { checkFuncDecl(pass, n, pass.Fset.File(n.Pos()).Name()) case *ast.FuncLit: checkFuncLit(pass, n, pass.Fset.File(n.Pos()).Name()) - } }) return nil, nil @@ -211,3 +220,35 @@ func checkSelectorExprTarget(typ *ast.SelectorExpr) bool { targetName := x.Name + "." + typ.Sel.Name return targetName == "testing.TB" } + +// goVersionLower117 returns true if version is lower than go1.17. +// In case of any parse errors it returns false. +func goVersionLower117(version string) bool { + version = strings.TrimPrefix(version, "go") + if version == "" { + return false + } + + parts := strings.Split(version, ".") + if len(parts) != 2 { + return false + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return false + } + if major < 1 { + return true + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return false + } + if minor < 17 { + return true + } + + return false +} diff --git a/tenv_test.go b/tenv_test.go index c4789c0..cd958e4 100644 --- a/tenv_test.go +++ b/tenv_test.go @@ -14,3 +14,10 @@ func TestAnalyzer(t *testing.T) { testdata := testutil.WithModules(t, analysistest.TestData(), nil) analysistest.Run(t, testdata, tenv.Analyzer, "a") } + +func TestAnalyzerGo116(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", "1.16"}) + analysistest.Run(t, testdata, a, "go116") +} diff --git a/testdata/src/go116/go.mod b/testdata/src/go116/go.mod new file mode 100644 index 0000000..f78d17a --- /dev/null +++ b/testdata/src/go116/go.mod @@ -0,0 +1,3 @@ +module go116 + +go 1.16 diff --git a/testdata/src/go116/go116_test.go b/testdata/src/go116/go116_test.go new file mode 100644 index 0000000..dc6554c --- /dev/null +++ b/testdata/src/go116/go116_test.go @@ -0,0 +1,12 @@ +package go116 + +import ( + "os" + "testing" +) + +func TestF(t *testing.T) { + os.Setenv("a", "b") // if -go = 1.16, "" + err := os.Setenv("a", "b") // if -go = 1.16, "" + _ = err +} From e180cebeddfc7f8f542de1960a66143437b86e75 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Mon, 15 May 2023 11:40:00 +0300 Subject: [PATCH 2/3] Add tests, extend README --- README.md | 13 +++++++++---- tenv.go | 32 +++++++++++++++++++------------- tenv_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 30921d5..5dd85e8 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,10 @@ go vet -vettool=(which tenv) ./... ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain ``` -### option +### options The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. -The option `go` allows to specify Go version. If the version is not empty and lower than Go 1.17 the analysis will be skipped. - By default, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. ```go @@ -77,13 +75,20 @@ func helper() { ``` ```console -go vet -vettool=(which tenv) -tenv.all ./... +go vet -vettool=$(which tenv) -tenv.all ./... # a ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain ./main_test.go:19:2: os.Setenv() can be replaced by `testing.Setenv()` in helper ``` +The option `go` allows to specify Go version. If the version is not empty and lower than Go 1.17 the analysis will be skipped. + +```console +go vet -vettool=$(which tenv) -tenv.go 1.16 ./... + +Outputs nothing because specified Go 1.16 is lower than 1.17. + ## CI ### CircleCI diff --git a/tenv.go b/tenv.go index 000a7e9..e677f17 100644 --- a/tenv.go +++ b/tenv.go @@ -1,6 +1,8 @@ package tenv import ( + "errors" + "fmt" "go/ast" "strconv" "strings" @@ -36,7 +38,12 @@ func init() { } func run(pass *analysis.Pass) (interface{}, error) { - if goVersionLower117(goflag) { + lower, err := goVersionLower117(goflag) + if err != nil { + return nil, err + } + + if lower { // Do nothing because T.Setenv added in go1.17 return nil, nil } @@ -54,6 +61,7 @@ func run(pass *analysis.Pass) (interface{}, error) { checkFuncDecl(pass, n, pass.Fset.File(n.Pos()).Name()) case *ast.FuncLit: checkFuncLit(pass, n, pass.Fset.File(n.Pos()).Name()) + } }) return nil, nil @@ -221,34 +229,32 @@ func checkSelectorExprTarget(typ *ast.SelectorExpr) bool { return targetName == "testing.TB" } -// goVersionLower117 returns true if version is lower than go1.17. -// In case of any parse errors it returns false. -func goVersionLower117(version string) bool { +// goVersionLower117 returns true if version is lower than Go 1.17 or empty. +// version must be in the format 'go1.17', '1.17'. +// In case of an invalid input returns not-nil error. +func goVersionLower117(version string) (bool, error) { version = strings.TrimPrefix(version, "go") if version == "" { - return false + return false, nil } parts := strings.Split(version, ".") if len(parts) != 2 { - return false + return false, errors.New(`go version must has format "go." or "."`) } major, err := strconv.Atoi(parts[0]) if err != nil { - return false + return false, fmt.Errorf("go version major part must be a number: %w", err) } if major < 1 { - return true + return true, nil } minor, err := strconv.Atoi(parts[1]) if err != nil { - return false - } - if minor < 17 { - return true + return false, fmt.Errorf("go version minor part must be a number: %w", err) } - return false + return minor < 17, nil } diff --git a/tenv_test.go b/tenv_test.go index cd958e4..52fec82 100644 --- a/tenv_test.go +++ b/tenv_test.go @@ -21,3 +21,30 @@ func TestAnalyzerGo116(t *testing.T) { a.Flags.Parse([]string{"-go", "1.16"}) analysistest.Run(t, testdata, a, "go116") } + +func TestRun(t *testing.T) { + t.Run("empty Go version", func(t *testing.T) { + for _, goVersion := range []string{ + "", "go", + } { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", goVersion}) + analysistest.Run(t, testdata, a, "a") + } + }) + + t.Run("invalid Go version", func(t *testing.T) { + for _, goVersion := range []string{ + "go1", "goa.2", "go1.a", + } { + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", goVersion}) + _, err := a.Run(nil) + + if err == nil { + t.Error("expected error, but got ") + } + } + }) +} From 18b08b1ca6e170777c819d96a3f60e6eccea6f07 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 30 May 2024 16:59:18 +0300 Subject: [PATCH 3/3] Update README.md Co-authored-by: sivchari --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dd85e8..dad939b 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ go vet -vettool=$(which tenv) -tenv.all ./... ./main_test.go:19:2: os.Setenv() can be replaced by `testing.Setenv()` in helper ``` -The option `go` allows to specify Go version. If the version is not empty and lower than Go 1.17 the analysis will be skipped. +The option `go` allows to specify Go version. If the version is not empty or lower than Go 1.17, the analysis will be skipped. ```console go vet -vettool=$(which tenv) -tenv.go 1.16 ./...