1
0
mirror of https://github.com/golang/go synced 2024-11-17 23:24:50 -07:00

cmd/{go,cover,covdata}: fix 'package main' inconsistent handling

Fix a buglet in cmd/cover in how we handle package name/path for the
"go build -o foo.exe *.go" and "go run *.go" cases.

The go command assigns a dummy import path of "command-line-arguments"
to the main package built in these cases; rather than expose this
dummy to the user in coverage reports, the cover tool had a special
case hack intended to rewrite such package paths to "main". The hack
was too general, however, and was rewriting the import path of all
packages with (p.name == "main") to an import path of "main". The hack
also produced unexpected results for cases such as

  go test -cover foo.go foo_test.go

This patch removes the hack entirely, leaving the package path for
such cases as "command-line-arguments".

Fixes #57169.

Change-Id: Ib6071db5e3485da3b8c26e16ef57f6fa1712402c
Reviewed-on: https://go-review.googlesource.com/c/go/+/456237
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
This commit is contained in:
Than McIntosh 2022-12-08 10:58:49 -05:00
parent 0aad4d3257
commit 7973b0e508
5 changed files with 81 additions and 24 deletions

View File

@ -111,6 +111,8 @@ func emitFile(t *testing.T, dst, src string) {
}
}
const mainPkgPath = "prog"
func buildProg(t *testing.T, prog string, dir string, tag string, flags []string) (string, string) {
// Create subdirs.
subdir := filepath.Join(dir, prog+"dir"+tag)
@ -132,7 +134,7 @@ func buildProg(t *testing.T, prog string, dir string, tag string, flags []string
// Emit go.mod.
mod := filepath.Join(subdir, "go.mod")
modsrc := "\nmodule prog\n\ngo 1.19\n"
modsrc := "\nmodule " + mainPkgPath + "\n\ngo 1.19\n"
if err := os.WriteFile(mod, []byte(modsrc), 0666); err != nil {
t.Fatal(err)
}
@ -305,7 +307,7 @@ func runToolOp(t *testing.T, s state, op string, args []string) []string {
func testDump(t *testing.T, s state) {
// Run the dumper on the two dirs we generated.
dargs := []string{"-pkg=main", "-live", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "debugdump", dargs)
// Sift through the output to make sure it has some key elements.
@ -319,7 +321,7 @@ func testDump(t *testing.T, s state) {
},
{
"main package",
regexp.MustCompile(`^Package path: main\s*$`),
regexp.MustCompile(`^Package path: ` + mainPkgPath + `\s*$`),
},
{
"main function",
@ -337,7 +339,7 @@ func testDump(t *testing.T, s state) {
}
}
if !found {
t.Errorf("dump output regexp match failed for %s", testpoint.tag)
t.Errorf("dump output regexp match failed for %q", testpoint.tag)
bad = true
}
}
@ -348,7 +350,7 @@ func testDump(t *testing.T, s state) {
func testPercent(t *testing.T, s state) {
// Run the dumper on the two dirs we generated.
dargs := []string{"-pkg=main", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "percent", dargs)
// Sift through the output to make sure it has the needful.
@ -380,11 +382,12 @@ func testPercent(t *testing.T, s state) {
dumplines(lines)
}
}
func testPkgList(t *testing.T, s state) {
dargs := []string{"-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "pkglist", dargs)
want := []string{"main", "prog/dep"}
want := []string{mainPkgPath, mainPkgPath + "/dep"}
bad := false
if len(lines) != 2 {
t.Errorf("expect pkglist to return two lines")
@ -405,7 +408,7 @@ func testPkgList(t *testing.T, s state) {
func testTextfmt(t *testing.T, s state) {
outf := s.dir + "/" + "t.txt"
dargs := []string{"-pkg=main", "-i=" + s.outdirs[0] + "," + s.outdirs[1],
dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1],
"-o", outf}
lines := runToolOp(t, s, "textfmt", dargs)
@ -426,7 +429,7 @@ func testTextfmt(t *testing.T, s state) {
dumplines(lines[0:10])
t.Errorf("textfmt: want %s got %s", want0, lines[0])
}
want1 := "prog/prog1.go:13.14,15.2 1 1"
want1 := mainPkgPath + "/prog1.go:13.14,15.2 1 1"
if lines[1] != want1 {
dumplines(lines[0:10])
t.Errorf("textfmt: want %s got %s", want1, lines[1])
@ -571,7 +574,7 @@ func testMergeSimple(t *testing.T, s state, indir1, indir2, tag string) {
nonzero: true,
},
}
flags := []string{"-live", "-pkg=main"}
flags := []string{"-live", "-pkg=" + mainPkgPath}
runDumpChecks(t, s, outdir, flags, testpoints)
}
@ -585,7 +588,7 @@ func testMergeSelect(t *testing.T, s state, indir1, indir2 string, tag string) {
// based on package.
ins := fmt.Sprintf("-i=%s,%s", indir1, indir2)
out := fmt.Sprintf("-o=%s", outdir)
margs := []string{"-pkg=prog/dep", ins, out}
margs := []string{"-pkg=" + mainPkgPath + "/dep", ins, out}
lines := runToolOp(t, s, "merge", margs)
if len(lines) != 0 {
t.Errorf("merge run produced %d lines of unexpected output", len(lines))
@ -600,7 +603,7 @@ func testMergeSelect(t *testing.T, s state, indir1, indir2 string, tag string) {
t.Fatalf("dump run produced no output")
}
want := map[string]int{
"Package path: prog/dep": 0,
"Package path: " + mainPkgPath + "/dep": 0,
"Func: Dep1": 0,
"Func: PDep": 0,
}
@ -696,7 +699,7 @@ func testMergeCombinePrograms(t *testing.T, s state) {
},
}
flags := []string{"-live", "-pkg=main"}
flags := []string{"-live", "-pkg=" + mainPkgPath}
runDumpChecks(t, s, moutdir, flags, testpoints)
}
@ -717,7 +720,7 @@ func testSubtract(t *testing.T, s state) {
}
// Dump the files in the subtract output dir and examine the result.
dargs := []string{"-pkg=main", "-live", "-i=" + soutdir}
dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + soutdir}
lines = runToolOp(t, s, "debugdump", dargs)
if len(lines) == 0 {
t.Errorf("dump run produced no output")
@ -774,7 +777,7 @@ func testIntersect(t *testing.T, s state, indir1, indir2, tag string) {
}
// Dump the files in the subtract output dir and examine the result.
dargs := []string{"-pkg=main", "-live", "-i=" + ioutdir}
dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + ioutdir}
lines = runToolOp(t, s, "debugdump", dargs)
if len(lines) == 0 {
t.Errorf("dump run produced no output")

View File

@ -542,9 +542,6 @@ func annotate(names []string) {
if *pkgcfg != "" {
pp := pkgconfig.PkgPath
pn := pkgconfig.PkgName
if pn == "main" {
pp = "main"
}
mp := pkgconfig.ModulePath
mdb, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp)
if err != nil {

View File

@ -26,7 +26,7 @@ env GOCOVERDIR=$SAVEGOCOVERDIR
# Check to make sure we instrumented just the main package, not
# any dependencies.
go tool covdata pkglist -i=$WORK/covdata
stdout main
stdout cmd/nm
! stdout cmd/internal/goobj pkglist.txt
# ... now collect a coverage profile from a Go file
@ -41,7 +41,7 @@ env GOCOVERDIR=$SAVEGOCOVERDIR
# Check to make sure we instrumented just the main package.
go tool covdata pkglist -i=$WORK/covdata2
stdout main
stdout command-line-arguments
! stdout fmt
-- go.mod --

View File

@ -0,0 +1,54 @@
# This test is intended to verify that coverage reporting is consistent
# between "go test -cover" and "go build -cover" with respect to how
# the "main" package is handled. See issue 57169 for details.
[short] skip
# Build this program with -cover and run to collect a profile.
go build -cover -o $WORK/prog.exe .
# Save off old GOCOVERDIR setting
env SAVEGOCOVERDIR=$GOCOVERDIR
mkdir $WORK/covdata
env GOCOVERDIR=$WORK/covdata
exec $WORK/prog.exe
# Restore previous GOCOVERDIR setting
env GOCOVERDIR=$SAVEGOCOVERDIR
# Report percent lines covered.
go tool covdata percent -i=$WORK/covdata
stdout '\s*mainwithtest\s+coverage:'
! stdout 'main\s+coverage:'
# Go test -cover should behave the same way.
go test -cover .
stdout 'ok\s+mainwithtest\s+\S+\s+coverage:'
! stdout 'ok\s+main\s+.*'
-- go.mod --
module mainwithtest
go 1.20
-- mymain.go --
package main
func main() {
println("hi mom")
}
func Mainer() int {
return 42
}
-- main_test.go --
package main
import "testing"
func TestCoverage(t *testing.T) {
println(Mainer())
}

View File

@ -157,7 +157,7 @@ func runHarness(t *testing.T, harnessPath string, tp string, setGoCoverDir bool,
func testForSpecificFunctions(t *testing.T, dir string, want []string, avoid []string) string {
args := []string{"tool", "covdata", "debugdump",
"-live", "-pkg=main", "-i=" + dir}
"-live", "-pkg=command-line-arguments", "-i=" + dir}
t.Logf("running: go %v\n", args)
cmd := exec.Command(testenv.GoToolPath(t), args...)
b, err := cmd.CombinedOutput()
@ -167,18 +167,21 @@ func testForSpecificFunctions(t *testing.T, dir string, want []string, avoid []s
output := string(b)
rval := ""
for _, f := range want {
wf := "Func: " + f
wf := "Func: " + f + "\n"
if strings.Contains(output, wf) {
continue
}
rval += fmt.Sprintf("error: output should contain %q but does not\n", wf)
}
for _, f := range avoid {
wf := "Func: " + f
wf := "Func: " + f + "\n"
if strings.Contains(output, wf) {
rval += fmt.Sprintf("error: output should not contain %q but does\n", wf)
}
}
if rval != "" {
t.Logf("=-= begin output:\n" + output + "\n=-= end output\n")
}
return rval
}