Skip to content

Commit

Permalink
test nogo/coverage: test generated code
Browse files Browse the repository at this point in the history
This add a test that demonstrate the current behavior of nogo today:
code which was generated under "bazel coverage" to capture coverage
information is going through static analysis.

This behavior is not ideal as end users have very little control over
the way rules_go generated these code and would not be able to fix them
to please the analyzer.

In a future patch, we will introduce a mechanism to alleviate this pain
and flip the expectation in this test.
  • Loading branch information
sluongng committed Jun 26, 2022
1 parent bebe25e commit 8d6b7f4
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 0 deletions.
5 changes: 5 additions & 0 deletions tests/core/nogo/coverage/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ go_bazel_test(
name = "coverage_test",
srcs = ["coverage_test.go"],
)

go_bazel_test(
name = "gen_code_test",
srcs = ["gen_code_test.go"],
)
10 changes: 10 additions & 0 deletions tests/core/nogo/coverage/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ handle libraries like this that do not have serialized facts. Verifies `#1940`_.

Also checks that `nogo`_ itself can be built with coverage enabled.
Verifies `#2146`_.

gen_code_test
-------------
Checks how `nogo`_ would interact with source code that was generated as part of
rules_go's coverage implementation. Currently `nogo`_ would be run over these
generated source code, which is not desirable as end-user have very little control
over how these code were generated.

In a future version, we shall flip this behavior so that `nogo`_ would not run
over source files that users have no control over.
142 changes: 142 additions & 0 deletions tests/core/nogo/coverage/gen_code_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// 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 gen_code_test

import (
"errors"
"os/exec"
"strings"
"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_test", "nogo")
go_library(
name = "simple",
srcs = ["simple.go"],
importpath = "simple"
)
go_test(
name = "simple_test",
srcs = ["simple_test.go"],
embed = [":simple"]
)
nogo(
name = "nogo",
deps = ["//nocover"],
visibility = ["//visibility:public"],
)
-- simple.go --
package simple
func Foo() string {
return "foo"
}
-- simple_test.go --
package simple
import "testing"
func TestFoo(t *testing.T) {
if actual, expected := Foo(), "foo"; actual != expected {
t.Errorf("Foo() should return foo")
}
}
-- nocover/BUILD.bazel --
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "nocover",
srcs = ["analyzer.go"],
importpath = "nocover",
visibility = ["//visibility:public"],
deps = [
"@org_golang_x_tools//go/analysis",
"@org_golang_x_tools//go/analysis/passes/inspect",
"@org_golang_x_tools//go/ast/inspector",
],
)
-- nocover/analyzer.go --
package nocover
import (
"fmt"
"go/ast"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
var Analyzer = &analysis.Analyzer{
Name: "nocover",
Doc: "nocover ensure that source code was not a generated file created by rules_go's coverage implementation",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
}
func run(pass *analysis.Pass) (interface{}, error) {
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
inspector.Preorder([]ast.Node{(*ast.ValueSpec)(nil)}, func(node ast.Node) {
valueSpec := node.(*ast.ValueSpec)
if len(valueSpec.Names) != 1 {
return
}
varName := valueSpec.Names[0].Name
// check for coverage variable name that matches this pattern: CoverZ%sZ%dZ%s
// see go/tools/builders/compilepkg.go -> coverVar for more information
if strings.HasPrefix(varName, "CoverZ") && strings.Count(varName, "Z") >= 3 {
pass.Report(analysis.Diagnostic{
Pos: valueSpec.Pos(),
End: valueSpec.End(),
Message: fmt.Sprintf("variable %s was generated by rules_go", varName),
})
}
})
return nil, nil
}
`,
Nogo: `@//:nogo`,
})
}

func TestNogoCoverGenCode(t *testing.T) {
out, err := bazel_testing.BazelOutput("coverage", "//:simple_test")
if err == nil {
t.Fatal("test should fail")
}

var eErr *exec.ExitError
if errors.As(err, &eErr) && strings.Contains(string(eErr.Stderr), "was generated by rules_go (nocover)") {
// Expected failure
return
}

println(string(out))
t.Fatal(err)
}

0 comments on commit 8d6b7f4

Please sign in to comment.