From 3b2a0a5476fd7355cd2eceef25b55a3f7671d737 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Wed, 20 Nov 2019 16:32:12 -0500 Subject: [PATCH] tests/core/nogo: migrate to go_bazel_test Updates #2285 --- .bazelci/presubmit.yml | 15 - tests/core/nogo/config/BUILD.bazel | 26 +- tests/core/nogo/config/README.rst | 9 +- tests/core/nogo/config/config_test.go | 62 ++++ tests/core/nogo/config/pure_bin.go | 6 - tests/core/nogo/config/pure_lib.go | 1 - tests/core/nogo/custom/BUILD.bazel | 125 +------- tests/core/nogo/custom/README.rst | 19 +- tests/core/nogo/custom/config.json | 15 - tests/core/nogo/custom/custom_test.go | 395 ++++++++++++++++++++++++++ tests/core/nogo/custom/dep.go | 5 - tests/core/nogo/custom/foofuncname.go | 39 --- tests/core/nogo/custom/has_errors.go | 12 - tests/core/nogo/custom/importfmt.go | 37 --- tests/core/nogo/custom/no_errors.go | 9 - tests/core/nogo/custom/visibility.go | 99 ------- tests/core/nogo/deps/BUILD.bazel | 96 +------ tests/core/nogo/deps/README.rst | 4 +- tests/core/nogo/deps/a.go | 20 -- tests/core/nogo/deps/b.go | 20 -- tests/core/nogo/deps/c.go | 23 -- tests/core/nogo/deps/d.go | 20 -- tests/core/nogo/deps/deps_test.go | 211 ++++++++++++++ tests/core/nogo/deps/src.go | 5 - tests/core/nogo/vet/BUILD.bazel | 82 +----- tests/core/nogo/vet/README.rst | 17 +- tests/core/nogo/vet/fmtwrap.go | 7 - tests/core/nogo/vet/has_errors.go | 20 -- tests/core/nogo/vet/no_errors.go | 8 - tests/core/nogo/vet/vet_test.go | 179 ++++++++++++ 30 files changed, 879 insertions(+), 707 deletions(-) create mode 100644 tests/core/nogo/config/config_test.go delete mode 100644 tests/core/nogo/config/pure_bin.go delete mode 100644 tests/core/nogo/config/pure_lib.go delete mode 100644 tests/core/nogo/custom/config.json create mode 100644 tests/core/nogo/custom/custom_test.go delete mode 100644 tests/core/nogo/custom/dep.go delete mode 100644 tests/core/nogo/custom/foofuncname.go delete mode 100644 tests/core/nogo/custom/has_errors.go delete mode 100644 tests/core/nogo/custom/importfmt.go delete mode 100644 tests/core/nogo/custom/no_errors.go delete mode 100644 tests/core/nogo/custom/visibility.go delete mode 100644 tests/core/nogo/deps/a.go delete mode 100644 tests/core/nogo/deps/b.go delete mode 100644 tests/core/nogo/deps/c.go delete mode 100644 tests/core/nogo/deps/d.go create mode 100644 tests/core/nogo/deps/deps_test.go delete mode 100644 tests/core/nogo/deps/src.go delete mode 100644 tests/core/nogo/vet/fmtwrap.go delete mode 100644 tests/core/nogo/vet/has_errors.go delete mode 100644 tests/core/nogo/vet/no_errors.go create mode 100644 tests/core/nogo/vet/vet_test.go diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index d600dd19b5..f4835e13a9 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -172,13 +172,6 @@ platforms: - "-//tests/core/go_proto_library:wrap_lib" - "-//tests/core/go_test:data_test" - "-//tests/core/go_test:pwd_test" - - "-//tests/core/nogo/custom:custom_analyzers_custom_config" - - "-//tests/core/nogo/custom:custom_analyzers_default_config" - - "-//tests/core/nogo/custom:custom_analyzers_no_errors" - - "-//tests/core/nogo/deps:dep_graph" - - "-//tests/core/nogo/vet:vet_default" - - "-//tests/core/nogo/vet:vet_enabled_has_errors" - - "-//tests/core/nogo/vet:vet_enabled_no_errors" - "-//tests/core/output_groups:compilation_outputs_test" - "-//tests/core/race:race_bin" - "-//tests/core/race:race_feature_test" @@ -299,15 +292,7 @@ platforms: - "-//tests/core/go_proto_library:transitive_test" - "-//tests/core/go_test:data_test" - "-//tests/core/go_test:pwd_test" - - "-//tests/core/nogo/config:pure_aspect_test" - "-//tests/core/nogo/coverage:coverage_test" - - "-//tests/core/nogo/custom:custom_analyzers_custom_config" - - "-//tests/core/nogo/custom:custom_analyzers_default_config" - - "-//tests/core/nogo/custom:custom_analyzers_no_errors" - - "-//tests/core/nogo/deps:dep_graph" - - "-//tests/core/nogo/vet:vet_default" - - "-//tests/core/nogo/vet:vet_enabled_has_errors" - - "-//tests/core/nogo/vet:vet_enabled_no_errors" - "-//tests/core/output_groups:compilation_outputs_test" - "-//tests/core/race:race_auto_test" - "-//tests/core/race:race_bin" diff --git a/tests/core/nogo/config/BUILD.bazel b/tests/core/nogo/config/BUILD.bazel index 1f929b6941..ce5ad406f5 100644 --- a/tests/core/nogo/config/BUILD.bazel +++ b/tests/core/nogo/config/BUILD.bazel @@ -1,24 +1,6 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") -bazel_test( - name = "pure_aspect_test", - command = "build", - nogo = "@io_bazel_rules_go//:tools_nogo", - targets = [":pure_bin"], -) - -go_binary( - name = "pure_bin", - srcs = ["pure_bin.go"], - pure = "on", - tags = ["manual"], - deps = [":pure_lib"], -) - -go_library( - name = "pure_lib", - srcs = ["pure_lib.go"], - importpath = "github.com/bazelbuild/rules_go/tests/core/nogo/config/pure_lib", - tags = ["manual"], +go_bazel_test( + name = "config_test", + srcs = ["config_test.go"], ) diff --git a/tests/core/nogo/config/README.rst b/tests/core/nogo/config/README.rst index dae5d553c6..9f2938b2fa 100644 --- a/tests/core/nogo/config/README.rst +++ b/tests/core/nogo/config/README.rst @@ -8,7 +8,8 @@ Tests that verify nogo_ works on targets compiled in non-default configurations. .. contents:: -pure_aspect_test ----------------- -Verifies that a `go_binary`_ with ``pure = "on"`` (compiled with the aspect) -builds successfully with nogo. Verifies #1850. +config_test +----------- + +Verifies that a `go_binary`_ can be built in non-default configurations with +nogo. Verifies #1850. diff --git a/tests/core/nogo/config/config_test.go b/tests/core/nogo/config/config_test.go new file mode 100644 index 0000000000..489e67509f --- /dev/null +++ b/tests/core/nogo/config/config_test.go @@ -0,0 +1,62 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@io_bazel_rules_go//:tools_nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_binary( + name = "pure_bin", + srcs = ["pure_bin.go"], + pure = "on", + deps = [":pure_lib"], +) + +go_library( + name = "pure_lib", + srcs = ["pure_lib.go"], + importpath = "example.com/nogo/config/pure_lib", +) + +-- pure_bin.go -- +package main + +import _ "example.com/nogo/config/pure_lib" + +func main() { +} + +-- pure_lib.go -- +package pure_lib + +`, + }) +} + +func TestPureAspect(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//:pure_bin"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/nogo/config/pure_bin.go b/tests/core/nogo/config/pure_bin.go deleted file mode 100644 index 40b2e90636..0000000000 --- a/tests/core/nogo/config/pure_bin.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -import _ "github.com/bazelbuild/rules_go/tests/core/nogo/config/pure_lib" - -func main() { -} diff --git a/tests/core/nogo/config/pure_lib.go b/tests/core/nogo/config/pure_lib.go deleted file mode 100644 index 4125eadee4..0000000000 --- a/tests/core/nogo/config/pure_lib.go +++ /dev/null @@ -1 +0,0 @@ -package pure_lib diff --git a/tests/core/nogo/custom/BUILD.bazel b/tests/core/nogo/custom/BUILD.bazel index 2f5e1d38b1..06317a0beb 100644 --- a/tests/core/nogo/custom/BUILD.bazel +++ b/tests/core/nogo/custom/BUILD.bazel @@ -1,123 +1,6 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library") -load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") -load( - "@io_bazel_rules_go//tests/core/nogo:common.bzl", - "BUILD_FAILED_TMPL", - "BUILD_PASSED_TMPL", - "CONTAINS_ERR_TMPL", - "DOES_NOT_CONTAIN_ERR_TMPL", -) - -BUILD_TMPL = """ -load("@io_bazel_rules_go//go:def.bzl", "nogo", "go_tool_library") - -nogo( - name = "nogo", - deps = [ - ":foofuncname", - ":importfmt", - ":visibility", - ], - {config} - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "importfmt", - srcs = ["importfmt.go"], - importpath = "importfmtanalyzer", - deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "foofuncname", - srcs = ["foofuncname.go"], - importpath = "foofuncanalyzer", - deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "visibility", - srcs = ["visibility.go"], - importpath = "visibilityanalyzer", - deps = [ - "@org_golang_x_tools//go/analysis:go_tool_library", - "@org_golang_x_tools//go/ast/inspector:go_tool_library", - ], - visibility = ["//visibility:public"], -) -""" - -EXTRA_FILES = [ - ":foofuncname.go", - ":importfmt.go", - ":visibility.go", - ":config.json", -] - -NOGO = "@//:nogo" - -bazel_test( - name = "custom_analyzers_default_config", - build = BUILD_TMPL.format(config = ""), - check = BUILD_FAILED_TMPL.format( - check_err = - CONTAINS_ERR_TMPL.format(err = "custom/has_errors.go:.*package fmt must not be imported") + - CONTAINS_ERR_TMPL.format(err = "custom/has_errors.go:.*function must not be named Foo") + - CONTAINS_ERR_TMPL.format(err = "custom/has_errors.go:.*function D is not visible in this package"), - ), - command = "build", - extra_files = EXTRA_FILES, - nogo = NOGO, - targets = [":has_errors"], -) - -bazel_test( - name = "custom_analyzers_custom_config", - build = BUILD_TMPL.format(config = "config = \":config.json\","), - check = BUILD_FAILED_TMPL.format( - check_err = - CONTAINS_ERR_TMPL.format(err = "custom/has_errors.go:.*package fmt must not be imported") + - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "custom/has_errors.go:.*function D is not visible in this package") + - CONTAINS_ERR_TMPL.format(err = "custom/has_errors.go:.*function must not be named Foo"), - ), - command = "build", - extra_files = EXTRA_FILES, - nogo = NOGO, - targets = [":has_errors"], -) - -bazel_test( - name = "custom_analyzers_no_errors", - build = BUILD_TMPL.format(config = ""), - check = BUILD_PASSED_TMPL.format( - check_err = - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "no_errors.go:"), - ), - command = "build", - extra_files = EXTRA_FILES, - nogo = NOGO, - targets = [":no_errors"], -) - -go_library( - name = "has_errors", - srcs = ["has_errors.go"], - importpath = "haserrors", - deps = [":dep"], -) - -go_library( - name = "no_errors", - srcs = ["no_errors.go"], - importpath = "noerrors", - deps = [":dep"], -) +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") -go_library( - name = "dep", - srcs = ["dep.go"], - importpath = "dep", +go_bazel_test( + name = "custom_test", + srcs = ["custom_test.go"], ) diff --git a/tests/core/nogo/custom/README.rst b/tests/core/nogo/custom/README.rst index 1830fccd66..a3d0d8389d 100644 --- a/tests/core/nogo/custom/README.rst +++ b/tests/core/nogo/custom/README.rst @@ -8,20 +8,9 @@ Tests to ensure that custom `nogo`_ analyzers run and detect errors. .. contents:: -custom_analyzers_default_config -------------------------------- +custom_test +----------- Verifies that custom analyzers print errors and fail a `go_library`_ build when a configuration file is not provided, and that analyzers with the same package -name do not conflict. - -custom_analyzers_custom_config ------------------------------- -Verifies that custom analyzers can be configured to apply only to certain file -paths using a custom configuration file, and that analyzers with the same -package name do not conflict. - -custom_analyzers_no_errors --------------------------- -Verifies that a library build succeeds if custom analyzers do not find any -errors in the library's source code, and that analyzers with the same package -name do not conflict. +name do not conflict. Also checks that custom analyzers can be configured to +apply only to certain file paths using a custom configuration file. diff --git a/tests/core/nogo/custom/config.json b/tests/core/nogo/custom/config.json deleted file mode 100644 index b1ee752c2d..0000000000 --- a/tests/core/nogo/custom/config.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "importfmt": { - "only_files": { - "has_errors\\.go": "" - } - }, - "foofuncname": { - "description": "no exemptions since we know this check is 100% accurate" - }, - "visibility": { - "exclude_files": { - "has_.*\\.go": "special exception to visibility rules" - } - } -} diff --git a/tests/core/nogo/custom/custom_test.go b/tests/core/nogo/custom/custom_test.go new file mode 100644 index 0000000000..0f724fd917 --- /dev/null +++ b/tests/core/nogo/custom/custom_test.go @@ -0,0 +1,395 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package custom_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +const origConfig = `# config = "",` + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library", "nogo") + +nogo( + name = "nogo", + deps = [ + ":foofuncname", + ":importfmt", + ":visibility", + ], + # config = "", + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "importfmt", + srcs = ["importfmt.go"], + importpath = "importfmtanalyzer", + deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "foofuncname", + srcs = ["foofuncname.go"], + importpath = "foofuncanalyzer", + deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "visibility", + srcs = ["visibility.go"], + importpath = "visibilityanalyzer", + deps = [ + "@org_golang_x_tools//go/analysis:go_tool_library", + "@org_golang_x_tools//go/ast/inspector:go_tool_library", + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "has_errors", + srcs = ["has_errors.go"], + importpath = "haserrors", + deps = [":dep"], +) + +go_library( + name = "no_errors", + srcs = ["no_errors.go"], + importpath = "noerrors", + deps = [":dep"], +) + +go_library( + name = "dep", + srcs = ["dep.go"], + importpath = "dep", +) + +-- foofuncname.go -- +// importfmt checks for functions named "Foo". +// It has the same package name as another check to test the checks with +// the same package name do not conflict. +package importfmt + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +const doc = "report calls of functions named \"Foo\"\n\nThe foofuncname analyzer reports calls to functions that are\nnamed \"Foo\"." + +var Analyzer = &analysis.Analyzer{ + Name: "foofuncname", + Run: run, + Doc: doc, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + // TODO(samueltan): use package inspector once the latest golang.org/x/tools + // changes are pulled into this branch (see #1755). + ast.Inspect(f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.FuncDecl: + if n.Name.Name == "Foo" { + pass.Reportf(n.Pos(), "function must not be named Foo") + } + return true + } + return true + }) + } + return nil, nil +} + +-- importfmt.go -- +// importfmt checks for the import of package fmt. +package importfmt + +import ( + "go/ast" + "strconv" + + "golang.org/x/tools/go/analysis" +) + +const doc = "report imports of package fmt\n\nThe importfmt analyzer reports imports of package fmt." + +var Analyzer = &analysis.Analyzer{ + Name: "importfmt", + Run: run, + Doc: doc, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + // TODO(samueltan): use package inspector once the latest golang.org/x/tools + // changes are pulled into this branch (see #1755). + ast.Inspect(f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.ImportSpec: + if path, _ := strconv.Unquote(n.Path.Value); path == "fmt" { + pass.Reportf(n.Pos(), "package fmt must not be imported") + } + return true + } + return true + }) + } + return nil, nil +} + +-- visibility.go -- +// visibility looks for visibility annotations on functions and +// checks they are only called from packages allowed to call them. +package visibility + +import ( + "encoding/gob" + "go/ast" + "regexp" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = &analysis.Analyzer{ + Name: "visibility", + Run: run, + Doc: "enforce visibility requirements for functions\n\nThe visibility analyzer reads visibility annotations on functions and\nchecks that packages that call those functions are allowed to do so.", + FactTypes: []analysis.Fact{(*VisibilityFact)(nil)}, +} + +type VisibilityFact struct { + Paths []string +} + +func (_ *VisibilityFact) AFact() {} // dummy method to satisfy interface + +func init() { gob.Register((*VisibilityFact)(nil)) } + +var visibilityRegexp = regexp.MustCompile("visibility:([^\\s]+)") + +func run(pass *analysis.Pass) (interface{}, error) { + in := inspector.New(pass.Files) + + // Find visibility annotations on function declarations. + in.Nodes([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node, push bool) (prune bool) { + if !push { + return false + } + + fn := n.(*ast.FuncDecl) + + if fn.Doc == nil { + return true + } + obj := pass.TypesInfo.ObjectOf(fn.Name) + if obj == nil { + return true + } + doc := fn.Doc.Text() + + if matches := visibilityRegexp.FindAllStringSubmatch(doc, -1); matches != nil { + fact := &VisibilityFact{Paths: make([]string, len(matches))} + for i, m := range matches { + fact.Paths[i] = m[1] + } + pass.ExportObjectFact(obj, fact) + } + + return true + }) + + // Find calls that may be affected by visibility declarations. + in.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(n ast.Node, push bool) (prune bool) { + if !push { + return false + } + + callee, ok := n.(*ast.CallExpr).Fun.(*ast.SelectorExpr) + if !ok { + return false + } + obj := pass.TypesInfo.ObjectOf(callee.Sel) + if obj == nil { + return false + } + var fact VisibilityFact + if ok := pass.ImportObjectFact(obj, &fact); !ok { + return false + } + visible := false + for _, path := range fact.Paths { + if path == pass.Pkg.Path() { + visible = true + break + } + } + if !visible { + pass.Reportf(callee.Pos(), "function %s is not visible in this package", callee.Sel.Name) + } + + return false + }) + + return nil, nil +} + +-- config.json -- +{ + "importfmt": { + "only_files": { + "has_errors\\.go": "" + } + }, + "foofuncname": { + "description": "no exemptions since we know this check is 100% accurate" + }, + "visibility": { + "exclude_files": { + "has_.*\\.go": "special exception to visibility rules" + } + } +} + +-- has_errors.go -- +package haserrors + +import ( + _ "fmt" // This should fail importfmt + + "dep" +) + +func Foo() bool { // This should fail foofuncname + dep.D() // This should fail visibility + return true // This should fail boolreturn +} + +-- no_errors.go -- +// package noerrors contains no analyzer errors. +package noerrors + +import "dep" + +func Baz() int { + dep.D() + return 1 +} + +-- dep.go -- +package dep + +// visibility:noerrors +func D() { +} + +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, config, target string + wantSuccess bool + includes, excludes []string + }{ + { + desc: "default_config", + target: "//:has_errors", + wantSuccess: false, + includes: []string{ + "has_errors.go:.*package fmt must not be imported", + "has_errors.go:.*function must not be named Foo", + "has_errors.go:.*function D is not visible in this package", + }, + }, { + desc: "custom_config", + target: "//:has_errors", + wantSuccess: false, + includes: []string{ + "has_errors.go:.*package fmt must not be imported", + "has_errors.go:.*function must not be named Foo", + }, + excludes: []string{ + "custom/has_errors.go:.*function D is not visible in this package", + }, + }, { + desc: "no_errors", + target: "//:no_errors", + wantSuccess: true, + excludes: []string{"no_errors.go"}, + }, + } { + t.Run(test.desc, func(t *testing.T) { + if test.config != "" { + customConfig := fmt.Sprintf("config = %q,", test.config) + if err := replaceInFile("BUILD.bazel", origConfig, customConfig); err != nil { + t.Fatal(err) + } + defer replaceInFile("BUILD.bazel", customConfig, origConfig) + } + + cmd := bazel_testing.BazelCmd("build", test.target) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil && !test.wantSuccess { + t.Fatal("unexpected success") + } else if err != nil && test.wantSuccess { + t.Fatalf("unexpected error: %v", err) + } + + for _, pattern := range test.includes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if !matched { + t.Errorf("output did not contain pattern: %s", pattern) + } + } + for _, pattern := range test.excludes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if matched { + t.Errorf("output contained pattern: %s", pattern) + } + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +} diff --git a/tests/core/nogo/custom/dep.go b/tests/core/nogo/custom/dep.go deleted file mode 100644 index 634a802666..0000000000 --- a/tests/core/nogo/custom/dep.go +++ /dev/null @@ -1,5 +0,0 @@ -package dep - -// visibility:noerrors -func D() { -} diff --git a/tests/core/nogo/custom/foofuncname.go b/tests/core/nogo/custom/foofuncname.go deleted file mode 100644 index 2e10b00f1c..0000000000 --- a/tests/core/nogo/custom/foofuncname.go +++ /dev/null @@ -1,39 +0,0 @@ -// importfmt checks for functions named "Foo". -// It has the same package name as another check to test the checks with -// the same package name do not conflict. -package importfmt - -import ( - "go/ast" - - "golang.org/x/tools/go/analysis" -) - -const doc = `report calls of functions named "Foo" - -The foofuncname analyzer reports calls to functions that are -named "Foo".` - -var Analyzer = &analysis.Analyzer{ - Name: "foofuncname", - Run: run, - Doc: doc, -} - -func run(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - // TODO(samueltan): use package inspector once the latest golang.org/x/tools - // changes are pulled into this branch (see #1755). - ast.Inspect(f, func(n ast.Node) bool { - switch n := n.(type) { - case *ast.FuncDecl: - if n.Name.Name == "Foo" { - pass.Reportf(n.Pos(), "function must not be named Foo") - } - return true - } - return true - }) - } - return nil, nil -} diff --git a/tests/core/nogo/custom/has_errors.go b/tests/core/nogo/custom/has_errors.go deleted file mode 100644 index d892e4a4ad..0000000000 --- a/tests/core/nogo/custom/has_errors.go +++ /dev/null @@ -1,12 +0,0 @@ -package haserrors - -import ( - _ "fmt" // This should fail importfmt - - "dep" -) - -func Foo() bool { // This should fail foofuncname - dep.D() // This should fail visibility - return true // This should fail boolreturn -} diff --git a/tests/core/nogo/custom/importfmt.go b/tests/core/nogo/custom/importfmt.go deleted file mode 100644 index 495ec78aa5..0000000000 --- a/tests/core/nogo/custom/importfmt.go +++ /dev/null @@ -1,37 +0,0 @@ -// importfmt checks for the import of package fmt. -package importfmt - -import ( - "go/ast" - "strconv" - - "golang.org/x/tools/go/analysis" -) - -const doc = `report imports of package fmt - -The importfmt analyzer reports imports of package fmt.` - -var Analyzer = &analysis.Analyzer{ - Name: "importfmt", - Run: run, - Doc: doc, -} - -func run(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - // TODO(samueltan): use package inspector once the latest golang.org/x/tools - // changes are pulled into this branch (see #1755). - ast.Inspect(f, func(n ast.Node) bool { - switch n := n.(type) { - case *ast.ImportSpec: - if path, _ := strconv.Unquote(n.Path.Value); path == "fmt" { - pass.Reportf(n.Pos(), "package fmt must not be imported") - } - return true - } - return true - }) - } - return nil, nil -} diff --git a/tests/core/nogo/custom/no_errors.go b/tests/core/nogo/custom/no_errors.go deleted file mode 100644 index defc1221fe..0000000000 --- a/tests/core/nogo/custom/no_errors.go +++ /dev/null @@ -1,9 +0,0 @@ -// package noerrors contains no analyzer errors. -package noerrors - -import "dep" - -func Baz() int { - dep.D() - return 1 -} diff --git a/tests/core/nogo/custom/visibility.go b/tests/core/nogo/custom/visibility.go deleted file mode 100644 index f5088782ff..0000000000 --- a/tests/core/nogo/custom/visibility.go +++ /dev/null @@ -1,99 +0,0 @@ -// visibility looks for visibility annotations on functions and -// checks they are only called from packages allowed to call them. -package visibility - -import ( - "encoding/gob" - "go/ast" - "regexp" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/ast/inspector" -) - -var Analyzer = &analysis.Analyzer{ - Name: "visibility", - Run: run, - Doc: `enforce visibility requirements for functions - -The visibility analyzer reads visibility annotations on functions and -checks that packages that call those functions are allowed to do so. -`, - FactTypes: []analysis.Fact{(*VisibilityFact)(nil)}, -} - -type VisibilityFact struct { - Paths []string -} - -func (_ *VisibilityFact) AFact() {} // dummy method to satisfy interface - -func init() { gob.Register((*VisibilityFact)(nil)) } - -var visibilityRegexp = regexp.MustCompile(`visibility:([^\s]+)`) - -func run(pass *analysis.Pass) (interface{}, error) { - in := inspector.New(pass.Files) - - // Find visibility annotations on function declarations. - in.Nodes([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node, push bool) (prune bool) { - if !push { - return false - } - - fn := n.(*ast.FuncDecl) - - if fn.Doc == nil { - return true - } - obj := pass.TypesInfo.ObjectOf(fn.Name) - if obj == nil { - return true - } - doc := fn.Doc.Text() - - if matches := visibilityRegexp.FindAllStringSubmatch(doc, -1); matches != nil { - fact := &VisibilityFact{Paths: make([]string, len(matches))} - for i, m := range matches { - fact.Paths[i] = m[1] - } - pass.ExportObjectFact(obj, fact) - } - - return true - }) - - // Find calls that may be affected by visibility declarations. - in.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(n ast.Node, push bool) (prune bool) { - if !push { - return false - } - - callee, ok := n.(*ast.CallExpr).Fun.(*ast.SelectorExpr) - if !ok { - return false - } - obj := pass.TypesInfo.ObjectOf(callee.Sel) - if obj == nil { - return false - } - var fact VisibilityFact - if ok := pass.ImportObjectFact(obj, &fact); !ok { - return false - } - visible := false - for _, path := range fact.Paths { - if path == pass.Pkg.Path() { - visible = true - break - } - } - if !visible { - pass.Reportf(callee.Pos(), "function %s is not visible in this package", callee.Sel.Name) - } - - return false - }) - - return nil, nil -} diff --git a/tests/core/nogo/deps/BUILD.bazel b/tests/core/nogo/deps/BUILD.bazel index 577c18de20..a1d998fd25 100644 --- a/tests/core/nogo/deps/BUILD.bazel +++ b/tests/core/nogo/deps/BUILD.bazel @@ -1,94 +1,6 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") -load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") -load( - "@io_bazel_rules_go//tests/core/nogo:common.bzl", - "BUILD_FAILED_TMPL", - "CONTAINS_ERR_TMPL", - "DOES_NOT_CONTAIN_ERR_TMPL", -) - -BUILD_TMPL = """ -load("@io_bazel_rules_go//go:def.bzl", "nogo", "go_tool_library") - -nogo( - name = "nogo", - deps = [ - ":a", - ":b", - ":c", - ], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "a", - srcs = ["a.go"], - importpath = "a", - deps = [ - ":c", - "@org_golang_x_tools//go/analysis:go_tool_library" - ], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "b", - srcs = ["b.go"], - importpath = "b", - deps = [ - ":c", - "@org_golang_x_tools//go/analysis:go_tool_library" - ], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "c", - srcs = ["c.go"], - importpath = "c", - deps = [ - ":d", - "@org_golang_x_tools//go/analysis:go_tool_library" - ], - visibility = ["//visibility:public"], -) - -go_tool_library( - name = "d", - srcs = ["d.go"], - importpath = "d", - deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], - visibility = ["//visibility:public"], -) -""" - -EXTRA_FILES = [ - ":a.go", - ":b.go", - ":c.go", - ":d.go", -] - -NOGO = "@//:nogo" - -bazel_test( - name = "dep_graph", - build = BUILD_TMPL.format(config = ""), - check = BUILD_FAILED_TMPL.format( - check_err = - CONTAINS_ERR_TMPL.format(err = "a c d") + - CONTAINS_ERR_TMPL.format(err = "b c d") + - CONTAINS_ERR_TMPL.format(err = "only printed once") + - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "this should not be printed"), - ), - command = "build", - extra_files = EXTRA_FILES, - nogo = NOGO, - targets = [":src"], -) +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") -go_library( - name = "src", - srcs = ["src.go"], - importpath = "src", +go_bazel_test( + name = "deps_test", + srcs = ["deps_test.go"], ) diff --git a/tests/core/nogo/deps/README.rst b/tests/core/nogo/deps/README.rst index 51902f77dd..682fe87867 100644 --- a/tests/core/nogo/deps/README.rst +++ b/tests/core/nogo/deps/README.rst @@ -9,8 +9,8 @@ run in the correct order. .. contents:: -dep_graph ----------------------------- +deps_test +--------- Given the following dependency graph of analyzers: a ----+ diff --git a/tests/core/nogo/deps/a.go b/tests/core/nogo/deps/a.go deleted file mode 100644 index 65559d63ce..0000000000 --- a/tests/core/nogo/deps/a.go +++ /dev/null @@ -1,20 +0,0 @@ -package a - -import ( - "c" - "go/token" - - "golang.org/x/tools/go/analysis" -) - -var Analyzer = &analysis.Analyzer{ - Name: "a", - Doc: "an analyzer that depends on c.Analyzer", - Run: run, - Requires: []*analysis.Analyzer{c.Analyzer}, -} - -func run(pass *analysis.Pass) (interface{}, error) { - pass.Reportf(token.NoPos, "a %s", pass.ResultOf[c.Analyzer]) - return nil, nil -} diff --git a/tests/core/nogo/deps/b.go b/tests/core/nogo/deps/b.go deleted file mode 100644 index 3de01a329f..0000000000 --- a/tests/core/nogo/deps/b.go +++ /dev/null @@ -1,20 +0,0 @@ -package b - -import ( - "c" - "go/token" - - "golang.org/x/tools/go/analysis" -) - -var Analyzer = &analysis.Analyzer{ - Name: "b", - Doc: "an analyzer that depends on c.Analyzer", - Run: run, - Requires: []*analysis.Analyzer{c.Analyzer}, -} - -func run(pass *analysis.Pass) (interface{}, error) { - pass.Reportf(token.NoPos, "b %s", pass.ResultOf[c.Analyzer]) - return nil, nil -} diff --git a/tests/core/nogo/deps/c.go b/tests/core/nogo/deps/c.go deleted file mode 100644 index 34e81f70a8..0000000000 --- a/tests/core/nogo/deps/c.go +++ /dev/null @@ -1,23 +0,0 @@ -package c - -import ( - "d" - "fmt" - "go/token" - "reflect" - - "golang.org/x/tools/go/analysis" -) - -var Analyzer = &analysis.Analyzer{ - Name: "c", - Doc: "an analyzer that depends on d.Analyzer", - Run: run, - Requires: []*analysis.Analyzer{d.Analyzer}, - ResultType: reflect.TypeOf(""), -} - -func run(pass *analysis.Pass) (interface{}, error) { - pass.Reportf(token.NoPos, "only printed once") - return fmt.Sprintf("c %s", pass.ResultOf[d.Analyzer]), nil -} diff --git a/tests/core/nogo/deps/d.go b/tests/core/nogo/deps/d.go deleted file mode 100644 index 1cdd7e2701..0000000000 --- a/tests/core/nogo/deps/d.go +++ /dev/null @@ -1,20 +0,0 @@ -package d - -import ( - "go/token" - "reflect" - - "golang.org/x/tools/go/analysis" -) - -var Analyzer = &analysis.Analyzer{ - Name: "d", - Doc: "an analyzer that does not depend on other analyzers", - Run: run, - ResultType: reflect.TypeOf(""), -} - -func run(pass *analysis.Pass) (interface{}, error) { - pass.Reportf(token.NoPos, "this should not be printed") - return "d", nil -} diff --git a/tests/core/nogo/deps/deps_test.go b/tests/core/nogo/deps/deps_test.go new file mode 100644 index 0000000000..91993fc251 --- /dev/null +++ b/tests/core/nogo/deps/deps_test.go @@ -0,0 +1,211 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deps_test + +import ( + "bytes" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library", "nogo") + +nogo( + name = "nogo", + deps = [ + ":a", + ":b", + ":c", + ], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "a", + srcs = ["a.go"], + importpath = "a", + deps = [ + ":c", + "@org_golang_x_tools//go/analysis:go_tool_library" + ], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "b", + srcs = ["b.go"], + importpath = "b", + deps = [ + ":c", + "@org_golang_x_tools//go/analysis:go_tool_library" + ], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "c", + srcs = ["c.go"], + importpath = "c", + deps = [ + ":d", + "@org_golang_x_tools//go/analysis:go_tool_library" + ], + visibility = ["//visibility:public"], +) + +go_tool_library( + name = "d", + srcs = ["d.go"], + importpath = "d", + deps = ["@org_golang_x_tools//go/analysis:go_tool_library"], + visibility = ["//visibility:public"], +) + +go_library( + name = "src", + srcs = ["src.go"], + importpath = "src", +) + +-- a.go -- +package a + +import ( + "c" + "go/token" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "a", + Doc: "an analyzer that depends on c.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{c.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "a %s", pass.ResultOf[c.Analyzer]) + return nil, nil +} + +-- b.go -- +package b + +import ( + "c" + "go/token" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "b", + Doc: "an analyzer that depends on c.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{c.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "b %s", pass.ResultOf[c.Analyzer]) + return nil, nil +} + +-- c.go -- +package c + +import ( + "d" + "fmt" + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "c", + Doc: "an analyzer that depends on d.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{d.Analyzer}, + ResultType: reflect.TypeOf(""), +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "only printed once") + return fmt.Sprintf("c %s", pass.ResultOf[d.Analyzer]), nil +} + +-- d.go -- +package d + +import ( + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "d", + Doc: "an analyzer that does not depend on other analyzers", + Run: run, + ResultType: reflect.TypeOf(""), +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "this should not be printed") + return "d", nil +} + +-- src.go -- +package src + +func Foo() int { + return 1 +} + +`, + }) +} + +func Test(t *testing.T) { + cmd := bazel_testing.BazelCmd("build", "//:src") + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil { + t.Fatal("unexpected success") + } + + for _, pattern := range []string{ + "a c d", + "b c d", + "only printed once", + } { + if matched, _ := regexp.Match(pattern, stderr.Bytes()); !matched { + t.Errorf("output does not contain pattern: %s", pattern) + } + } + if bytes.Contains(stderr.Bytes(), []byte("this should not be printed")) { + t.Errorf("%q was printed", "this should not be printed") + } +} diff --git a/tests/core/nogo/deps/src.go b/tests/core/nogo/deps/src.go deleted file mode 100644 index 733267d8c1..0000000000 --- a/tests/core/nogo/deps/src.go +++ /dev/null @@ -1,5 +0,0 @@ -package src - -func Foo() int { - return 1 -} diff --git a/tests/core/nogo/vet/BUILD.bazel b/tests/core/nogo/vet/BUILD.bazel index 1ac4f3b15f..b01e048217 100644 --- a/tests/core/nogo/vet/BUILD.bazel +++ b/tests/core/nogo/vet/BUILD.bazel @@ -1,80 +1,6 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") -load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") -load( - "@io_bazel_rules_go//tests/core/nogo:common.bzl", - "BUILD_FAILED_TMPL", - "BUILD_PASSED_TMPL", - "CONTAINS_ERR_TMPL", - "DOES_NOT_CONTAIN_ERR_TMPL", -) - -BUILD_ENABLE_VET = """ -load("@io_bazel_rules_go//go:def.bzl", "nogo", "go_tool_library") - -nogo( - name = "nogo", - vet = True, - visibility = ["//visibility:public"], -) -""" - -NOGO = "@//:nogo" - -bazel_test( - name = "vet_enabled_no_errors", - build = BUILD_ENABLE_VET, - check = BUILD_PASSED_TMPL.format( - check_err = ":", # no-op - ), - command = "build", - nogo = NOGO, - targets = [":no_errors"], -) - -bazel_test( - name = "vet_enabled_has_errors", - build = BUILD_ENABLE_VET, - check = BUILD_FAILED_TMPL.format( - check_err = - CONTAINS_ERR_TMPL.format(err = "has_errors.go:3:1: +build comment must appear before package clause and be followed by a blank line") + - CONTAINS_ERR_TMPL.format(err = "has_errors.go:15:5: comparison of function F == nil is always false") + - CONTAINS_ERR_TMPL.format(err = 'has_errors.go:18:2: Printf format %b has arg "hi" of wrong type strin') + - CONTAINS_ERR_TMPL.format(err = "has_errors.go:19:9: redundant or: true || true"), - ), - command = "build", - nogo = NOGO, - targets = [":has_errors"], -) - -bazel_test( - name = "vet_default", - check = BUILD_PASSED_TMPL.format( - check_err = - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "+build comment must appear before package clause and be followed by a blank line") + - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "comparison of function F == nil is always false") + - DOES_NOT_CONTAIN_ERR_TMPL.format(err = 'Printf format %b has arg "hi" of wrong type strin') + - DOES_NOT_CONTAIN_ERR_TMPL.format(err = "redundant or: true || true"), - ), - command = "build", - targets = [":has_errors"], -) - -go_library( - name = "has_errors", - srcs = ["has_errors.go"], - importpath = "haserrors", - deps = [":fmtwrap"], -) - -go_library( - name = "no_errors", - srcs = ["no_errors.go"], - cgo = True, - importpath = "noerrors", -) +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") -go_library( - name = "fmtwrap", - srcs = ["fmtwrap.go"], - importpath = "fmtwrap", +go_bazel_test( + name = "vet_test", + srcs = ["vet_test.go"], ) diff --git a/tests/core/nogo/vet/README.rst b/tests/core/nogo/vet/README.rst index 5a3b8f2557..1822830a90 100644 --- a/tests/core/nogo/vet/README.rst +++ b/tests/core/nogo/vet/README.rst @@ -7,15 +7,8 @@ Tests to ensure that vet runs and detects errors. .. contents:: -vet_enabled_no_errors ---------------------- -Verifies that vet does not fail the build when analyzing error-free source code. - -vet_enabled_has_errors ----------------------- -Verifies that vet emits findings and fails a `go_library`_ build when analyzing -erroneous source code. - -vet_default ------------ -Verifies that vet is disabled by default. +vet_test +-------- +Verifies that vet errors are emitted on a `go_library`_ with problems when built +with a ``nogo`` binary with ``vet = True``. No errors should be emitted when +analyzing error-free source code. Vet should not be enabled by default. diff --git a/tests/core/nogo/vet/fmtwrap.go b/tests/core/nogo/vet/fmtwrap.go deleted file mode 100644 index 23e5450cb2..0000000000 --- a/tests/core/nogo/vet/fmtwrap.go +++ /dev/null @@ -1,7 +0,0 @@ -package fmtwrap - -import "fmt" - -func Printf(format string, args ...interface{}) { - fmt.Printf(format, args...) -} diff --git a/tests/core/nogo/vet/has_errors.go b/tests/core/nogo/vet/has_errors.go deleted file mode 100644 index 5c438512e1..0000000000 --- a/tests/core/nogo/vet/has_errors.go +++ /dev/null @@ -1,20 +0,0 @@ -package haserrors - -// +build build_tags_error - -import ( - "fmtwrap" - "sync/atomic" -) - -func F() {} - -func Foo() bool { - x := uint64(1) - _ = atomic.AddUint64(&x, 1) - if F == nil { // nilfunc error. - return false - } - fmtwrap.Printf("%b", "hi") // printf error. - return true || true // redundant boolean error. -} diff --git a/tests/core/nogo/vet/no_errors.go b/tests/core/nogo/vet/no_errors.go deleted file mode 100644 index 2dfbaab9f0..0000000000 --- a/tests/core/nogo/vet/no_errors.go +++ /dev/null @@ -1,8 +0,0 @@ -package noerrors - -// const int x = 1; -import "C" - -func Foo() bool { - return bool(C.x == 1) -} diff --git a/tests/core/nogo/vet/vet_test.go b/tests/core/nogo/vet/vet_test.go new file mode 100644 index 0000000000..37be7c69ed --- /dev/null +++ b/tests/core/nogo/vet/vet_test.go @@ -0,0 +1,179 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vet_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library", "nogo") + +nogo( + name = "nogo", + vet = True, + visibility = ["//visibility:public"], +) + +go_library( + name = "has_errors", + srcs = ["has_errors.go"], + importpath = "haserrors", + deps = [":fmtwrap"], +) + +go_library( + name = "no_errors", + srcs = ["no_errors.go"], + cgo = True, + importpath = "noerrors", +) + +go_library( + name = "fmtwrap", + srcs = ["fmtwrap.go"], + importpath = "fmtwrap", +) + +-- has_errors.go -- +package haserrors + +// +build build_tags_error + +import ( + "fmtwrap" + "sync/atomic" +) + +func F() {} + +func Foo() bool { + x := uint64(1) + _ = atomic.AddUint64(&x, 1) + if F == nil { // nilfunc error. + return false + } + fmtwrap.Printf("%b", "hi") // printf error. + return true || true // redundant boolean error. +} + +-- no_errors.go -- +package noerrors + +// const int x = 1; +import "C" + +func Foo() bool { + return bool(C.x == 1) +} + +-- fmtwrap.go -- +package fmtwrap + +import "fmt" + +func Printf(format string, args ...interface{}) { + fmt.Printf(format, args...) +} +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, nogo, target string + wantSuccess bool + includes, excludes []string + }{ + { + desc: "default", + target: "//:has_errors", + wantSuccess: true, + excludes: []string{ + "\\+build comment must appear before package clause and be followed by a blank line", + "comparison of function F == nil is always false", + "Printf format %b has arg \"hi\" of wrong type string", + "redundant or: true \\|\\| true", + }, + }, { + desc: "enabled_no_errors", + target: "//:no_errors", + wantSuccess: true, + }, { + desc: "enabled_has_errors", + nogo: "@//:nogo", + target: "//:has_errors", + includes: []string{ + "\\+build comment must appear before package clause and be followed by a blank line", + "comparison of function F == nil is always false", + "Printf format %b has arg \"hi\" of wrong type string", + "redundant or: true \\|\\| true", + }, + }, + } { + t.Run(test.desc, func(t *testing.T) { + if test.nogo != "" { + origRegister := "go_register_toolchains()" + customRegister := fmt.Sprintf("go_register_toolchains(nogo = %q)", test.nogo) + if err := replaceInFile("WORKSPACE", origRegister, customRegister); err != nil { + t.Fatal(err) + } + defer replaceInFile("WORKSPACE", customRegister, origRegister) + } + + cmd := bazel_testing.BazelCmd("build", test.target) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil && !test.wantSuccess { + t.Fatal("unexpected success") + } else if err != nil && test.wantSuccess { + t.Fatalf("unexpected error: %v", err) + } + + for _, pattern := range test.includes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if !matched { + t.Errorf("output did not contain pattern: %s", pattern) + } + } + for _, pattern := range test.excludes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if matched { + t.Errorf("output contained pattern: %s", pattern) + } + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +}