1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cov_test
6
7import (
8	"cmd/internal/cov"
9	"fmt"
10	"internal/coverage"
11	"internal/coverage/decodecounter"
12	"internal/coverage/decodemeta"
13	"internal/coverage/pods"
14	"internal/goexperiment"
15	"internal/testenv"
16	"os"
17	"path/filepath"
18	"testing"
19)
20
21// visitor implements the CovDataVisitor interface in a very stripped
22// down way, just keeps track of interesting events.
23type visitor struct {
24	metaFileCount    int
25	counterFileCount int
26	funcCounterData  int
27	metaFuncCount    int
28}
29
30func (v *visitor) BeginPod(p pods.Pod) {}
31func (v *visitor) EndPod(p pods.Pod)   {}
32func (v *visitor) VisitMetaDataFile(mdf string, mfr *decodemeta.CoverageMetaFileReader) {
33	v.metaFileCount++
34}
35func (v *visitor) BeginCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {
36	v.counterFileCount++
37}
38func (v *visitor) EndCounterDataFile(cdf string, cdr *decodecounter.CounterDataReader, dirIdx int) {}
39func (v *visitor) VisitFuncCounterData(payload decodecounter.FuncPayload)                          { v.funcCounterData++ }
40func (v *visitor) EndCounters()                                                                    {}
41func (v *visitor) BeginPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32)              {}
42func (v *visitor) EndPackage(pd *decodemeta.CoverageMetaDataDecoder, pkgIdx uint32)                {}
43func (v *visitor) VisitFunc(pkgIdx uint32, fnIdx uint32, fd *coverage.FuncDesc)                    { v.metaFuncCount++ }
44func (v *visitor) Finish()                                                                         {}
45
46func TestIssue58411(t *testing.T) {
47	testenv.MustHaveGoBuild(t)
48	if !goexperiment.CoverageRedesign {
49		t.Skipf("skipping since this test requires 'go build -cover'")
50	}
51
52	// Build a tiny test program with -cover. Smallness is important;
53	// it is one of the factors that triggers issue 58411.
54	d := t.TempDir()
55	exepath := filepath.Join(d, "small.exe")
56	path := filepath.Join("testdata", "small.go")
57	cmd := testenv.Command(t, testenv.GoToolPath(t), "build",
58		"-o", exepath, "-cover", path)
59	b, err := cmd.CombinedOutput()
60	if len(b) != 0 {
61		t.Logf("## build output:\n%s", b)
62	}
63	if err != nil {
64		t.Fatalf("build error: %v", err)
65	}
66
67	// Run to produce coverage data. Note the large argument; we need a large
68	// argument (more than 4k) to trigger the bug, but the overall file
69	// has to remain small (since large files will be read with mmap).
70	covdir := filepath.Join(d, "covdata")
71	if err = os.Mkdir(covdir, 0777); err != nil {
72		t.Fatalf("creating covdir: %v", err)
73	}
74	large := fmt.Sprintf("%07999d", 0)
75	cmd = testenv.Command(t, exepath, "1", "2", "3", large)
76	cmd.Dir = covdir
77	cmd.Env = append(os.Environ(), "GOCOVERDIR="+covdir)
78	b, err = cmd.CombinedOutput()
79	if err != nil {
80		t.Logf("## run output:\n%s", b)
81		t.Fatalf("build error: %v", err)
82	}
83
84	vis := &visitor{}
85
86	// Read resulting coverage data. Without the fix, this would
87	// yield a "short read" error.
88	const verbosityLevel = 0
89	const flags = 0
90	cdr := cov.MakeCovDataReader(vis, []string{covdir}, verbosityLevel, flags, nil)
91	err = cdr.Visit()
92	if err != nil {
93		t.Fatalf("visit failed: %v", err)
94	}
95
96	// make sure we saw a few things just for grins
97	const want = "{metaFileCount:1 counterFileCount:1 funcCounterData:1 metaFuncCount:1}"
98	got := fmt.Sprintf("%+v", *vis)
99	if want != got {
100		t.Errorf("visitor contents: want %v got %v\n", want, got)
101	}
102}
103