Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate coverage reports in LCOV format #3117

Merged
merged 8 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ cgo_context_data_proxy(
# to depend on all build settings directly.
go_config(
name = "go_config",
cover_format = "//go/config:cover_format",
# Always include debug symbols with -c dbg.
debug = select({
"//go/private:is_compilation_mode_dbg": "//go/private:always_true",
Expand Down
10 changes: 10 additions & 0 deletions go/config/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ string_list_flag(
visibility = ["//visibility:public"],
)

string_flag(
name = "cover_format",
build_setting_default = "lcov",
fmeum marked this conversation as resolved.
Show resolved Hide resolved
values = [
"go_cover",
"lcov",
],
visibility = ["//visibility:public"],
)

filegroup(
name = "all_files",
testonly = True,
Expand Down
1 change: 1 addition & 0 deletions go/private/actions/compilepkg.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def emit_compilepkg(
args.add("-cover_mode", "atomic")
else:
args.add("-cover_mode", "set")
args.add("-cover_format", go.cover_format)
args.add_all(cover, before_each = "-cover")
args.add_all(archives, before_each = "-arc", map_each = _archive)
if importpath:
Expand Down
6 changes: 6 additions & 0 deletions go/private/context.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ def go_context(ctx, attr = None):
tags = tags,
stamp = mode.stamp,
label = ctx.label,
cover_format = mode.cover_format,

# Action generators
archive = toolchain.actions.archive,
Expand Down Expand Up @@ -781,6 +782,7 @@ def _go_config_impl(ctx):
linkmode = ctx.attr.linkmode[BuildSettingInfo].value,
tags = ctx.attr.gotags[BuildSettingInfo].value,
stamp = ctx.attr.stamp,
cover_format = ctx.attr.cover_format[BuildSettingInfo].value,
)]

go_config = rule(
Expand Down Expand Up @@ -819,6 +821,10 @@ go_config = rule(
providers = [BuildSettingInfo],
),
"stamp": attr.bool(mandatory = True),
"cover_format": attr.label(
mandatory = True,
providers = [BuildSettingInfo],
),
},
provides = [GoConfigInfo],
doc = """Collects information about build settings in the current
Expand Down
2 changes: 2 additions & 0 deletions go/private/mode.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info):
stamp = go_config_info.stamp if go_config_info else False
debug = go_config_info.debug if go_config_info else False
linkmode = go_config_info.linkmode if go_config_info else LINKMODE_NORMAL
cover_format = go_config_info and go_config_info.cover_format
goos = go_toolchain.default_goos
goarch = go_toolchain.default_goarch

Expand Down Expand Up @@ -107,6 +108,7 @@ def get_mode(ctx, go_toolchain, cgo_context_info, go_config_info):
goos = goos,
goarch = goarch,
tags = tags,
cover_format = cover_format,
)

def installsuffix(mode):
Expand Down
1 change: 1 addition & 0 deletions go/private/rules/test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def _go_test_impl(ctx):
arguments.add("-cover_mode", "atomic")
else:
arguments.add("-cover_mode", "set")
arguments.add("-cover_format", go.cover_format)
arguments.add(
# the l is the alias for the package under test, the l_test must be the
# same with the test suffix
Expand Down
33 changes: 22 additions & 11 deletions go/tools/builders/compilepkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func compilePkg(args []string) error {
var outPath, outFactsPath, cgoExportHPath string
var testFilter string
var gcFlags, asmFlags, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags quoteMultiFlag
var coverFormat string
fs.Var(&unfilteredSrcs, "src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and compiled")
fs.Var(&coverSrcs, "cover", ".go file that should be instrumented for coverage (must also be a -src)")
fs.Var(&embedSrcs, "embedsrc", "file that may be compiled into the package with a //go:embed directive")
Expand All @@ -67,6 +68,7 @@ func compilePkg(args []string) error {
fs.StringVar(&outFactsPath, "x", "", "The output archive file to write export data and nogo facts")
fs.StringVar(&cgoExportHPath, "cgoexport", "", "The _cgo_exports.h file to write")
fs.StringVar(&testFilter, "testfilter", "off", "Controls test package filtering")
fs.StringVar(&coverFormat, "cover_format", "", "Emit source file paths in coverage instrumentation suitable for the specified coverage format")
if err := fs.Parse(args); err != nil {
return err
}
Expand All @@ -85,9 +87,6 @@ func compilePkg(args []string) error {
for i := range embedSrcs {
embedSrcs[i] = abs(embedSrcs[i])
}
for i := range coverSrcs {
coverSrcs[i] = abs(coverSrcs[i])
}

// Filter sources.
srcs, err := filterAndSplitFiles(unfilteredSrcs)
Expand Down Expand Up @@ -143,7 +142,8 @@ func compilePkg(args []string) error {
packageListPath,
outPath,
outFactsPath,
cgoExportHPath)
cgoExportHPath,
coverFormat)
}

func compileArchive(
Expand All @@ -169,7 +169,8 @@ func compileArchive(
packageListPath string,
outPath string,
outXPath string,
cgoExportHPath string) error {
cgoExportHPath string,
coverFormat string) error {

workDir, cleanup, err := goenv.workDir()
if err != nil {
Expand Down Expand Up @@ -232,23 +233,33 @@ func compileArchive(

// Instrument source files for coverage.
if coverMode != "" {
shouldCover := make(map[string]bool)
relCoverPath := make(map[string]string)
for _, s := range coverSrcs {
shouldCover[s] = true
relCoverPath[abs(s)] = s
}

combined := append([]string{}, goSrcs...)
if cgoEnabled {
combined = append(combined, cgoSrcs...)
}
for i, origSrc := range combined {
if !shouldCover[origSrc] {
if _, ok := relCoverPath[origSrc]; !ok {
continue
}

srcName := origSrc
if importPath != "" {
srcName = path.Join(importPath, filepath.Base(origSrc))
var srcName string
switch coverFormat {
case "go_cover":
srcName = origSrc
if importPath != "" {
srcName = path.Join(importPath, filepath.Base(origSrc))
}
case "lcov":
// Bazel merges lcov reports across languages and thus assumes
// that the source file paths are relative to the exec root.
srcName = relCoverPath[origSrc]
default:
return fmt.Errorf("invalid value for -cover_format: %q", coverFormat)
}

stem := filepath.Base(origSrc)
Expand Down
32 changes: 26 additions & 6 deletions go/tools/builders/generate_test_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Cases struct {
Examples []Example
TestMain string
CoverMode string
CoverFormat string
Pkgname string
}

Expand Down Expand Up @@ -171,10 +172,16 @@ func main() {
}
}

testDeps :=
{{if eq .CoverFormat "lcov"}}
bzltestutil.LcovTestDeps{TestDeps: testdeps.TestDeps{}}
{{else}}
testdeps.TestDeps{}
{{end}}
{{if .Version "go1.18"}}
m := testing.MainStart(testdeps.TestDeps{}, testsInShard(), benchmarks, fuzzTargets, examples)
m := testing.MainStart(testDeps, testsInShard(), benchmarks, fuzzTargets, examples)
{{else}}
m := testing.MainStart(testdeps.TestDeps{}, testsInShard(), benchmarks, examples)
m := testing.MainStart(testDeps, testsInShard(), benchmarks, examples)
{{end}}

if filter := os.Getenv("TESTBRIDGE_TEST_ONLY"); filter != "" {
Expand All @@ -185,6 +192,12 @@ func main() {
flag.Lookup("test.failfast").Value.Set("true")
}

// Setting this flag serves two purposes:
// 1. It attains parity with "go test", which enables this feature by default.
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.1:src/cmd/go/internal/test/test.go;l=1331-1337
// 2. It provides a way to run hooks right before testing.M.Run() returns.
flag.Lookup("test.paniconexit0").Value.Set("true")

{{if ne .CoverMode ""}}
if len(coverdata.Counters) > 0 {
testing.RegisterCover(testing.Cover{
Expand All @@ -194,18 +207,23 @@ func main() {
})

if coverageDat, ok := os.LookupEnv("COVERAGE_OUTPUT_FILE"); ok {
{{if eq .CoverFormat "lcov"}}
flag.Lookup("test.coverprofile").Value.Set(coverageDat+".cover")
{{else}}
flag.Lookup("test.coverprofile").Value.Set(coverageDat)
{{end}}
}
}
{{end}}

{{if not .TestMain}}
os.Exit(m.Run())
res := m.Run()
{{else}}
{{.TestMain}}(m)
{{/* See golang.org/issue/34129 and golang.org/cl/219639 */}}
os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
res := int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())
{{end}}
os.Exit(res)
}
`

Expand All @@ -221,6 +239,7 @@ func genTestMain(args []string) error {
goenv := envFlags(flags)
out := flags.String("output", "", "output file to write. Defaults to stdout.")
coverMode := flags.String("cover_mode", "", "the coverage mode to use")
coverFormat := flags.String("cover_format", "", "the coverage report type to generate (go_cover or lcov)")
pkgname := flags.String("pkgname", "", "package name of test")
flags.Var(&imports, "import", "Packages to import")
flags.Var(&sources, "src", "Sources to process for tests")
Expand Down Expand Up @@ -270,8 +289,9 @@ func genTestMain(args []string) error {
}

cases := Cases{
CoverMode: *coverMode,
Pkgname: *pkgname,
CoverFormat: *coverFormat,
CoverMode: *coverMode,
Pkgname: *pkgname,
}

testFileSet := token.NewFileSet()
Expand Down
2 changes: 2 additions & 0 deletions go/tools/bzltestutil/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_tool_library(
name = "bzltestutil",
srcs = [
"init.go",
"lcov.go",
"test2json.go",
"wrap.go",
"xml.go",
Expand All @@ -15,6 +16,7 @@ go_tool_library(
go_test(
name = "bzltestutil_test",
srcs = [
"lcov_test.go",
"wrap_test.go",
"xml_test.go",
],
Expand Down
Loading