mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Fix a coding error in coverage meta-data decoding in the method decodemeta.CoverageMetaDataDecoder.ReadFunc. The code was not unconditionally assigning the "function literal" field of the coverage.FuncDesc object passed in, resulting in bad values depending on what the state of the field happened to be in the object. Fixes #57942. Change-Id: I6dfd7d7f7af6004f05c622f9a7116e9f6018cf4f Reviewed-on: https://go-review.googlesource.com/c/go/+/462955 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com>
331 lines
8.5 KiB
Go
331 lines
8.5 KiB
Go
// Copyright 2021 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package test
|
|
|
|
import (
|
|
"fmt"
|
|
"internal/coverage"
|
|
"internal/coverage/decodemeta"
|
|
"internal/coverage/encodemeta"
|
|
"internal/coverage/slicewriter"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func cmpFuncDesc(want, got coverage.FuncDesc) string {
|
|
swant := fmt.Sprintf("%+v", want)
|
|
sgot := fmt.Sprintf("%+v", got)
|
|
if swant == sgot {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("wanted %q got %q", swant, sgot)
|
|
}
|
|
|
|
func TestMetaDataEmptyPackage(t *testing.T) {
|
|
// Make sure that encoding/decoding works properly with packages
|
|
// that don't actually have any functions.
|
|
p := "empty/package"
|
|
pn := "package"
|
|
mp := "m"
|
|
b, err := encodemeta.NewCoverageMetaDataBuilder(p, pn, mp)
|
|
if err != nil {
|
|
t.Fatalf("making builder: %v", err)
|
|
}
|
|
drws := &slicewriter.WriteSeeker{}
|
|
b.Emit(drws)
|
|
drws.Seek(0, io.SeekStart)
|
|
dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false)
|
|
if err != nil {
|
|
t.Fatalf("making decoder: %v", err)
|
|
}
|
|
nf := dec.NumFuncs()
|
|
if nf != 0 {
|
|
t.Errorf("dec.NumFuncs(): got %d want %d", nf, 0)
|
|
}
|
|
pp := dec.PackagePath()
|
|
if pp != p {
|
|
t.Errorf("dec.PackagePath(): got %s want %s", pp, p)
|
|
}
|
|
ppn := dec.PackageName()
|
|
if ppn != pn {
|
|
t.Errorf("dec.PackageName(): got %s want %s", ppn, pn)
|
|
}
|
|
pmp := dec.ModulePath()
|
|
if pmp != mp {
|
|
t.Errorf("dec.ModulePath(): got %s want %s", pmp, mp)
|
|
}
|
|
}
|
|
|
|
func TestMetaDataEncoderDecoder(t *testing.T) {
|
|
// Test encode path.
|
|
pp := "foo/bar/pkg"
|
|
pn := "pkg"
|
|
mp := "barmod"
|
|
b, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp)
|
|
if err != nil {
|
|
t.Fatalf("making builder: %v", err)
|
|
}
|
|
f1 := coverage.FuncDesc{
|
|
Funcname: "func",
|
|
Srcfile: "foo.go",
|
|
Units: []coverage.CoverableUnit{
|
|
coverage.CoverableUnit{StLine: 1, StCol: 2, EnLine: 3, EnCol: 4, NxStmts: 5},
|
|
coverage.CoverableUnit{StLine: 6, StCol: 7, EnLine: 8, EnCol: 9, NxStmts: 10},
|
|
},
|
|
}
|
|
idx := b.AddFunc(f1)
|
|
if idx != 0 {
|
|
t.Errorf("b.AddFunc(f1) got %d want %d", idx, 0)
|
|
}
|
|
|
|
f2 := coverage.FuncDesc{
|
|
Funcname: "xfunc",
|
|
Srcfile: "bar.go",
|
|
Units: []coverage.CoverableUnit{
|
|
coverage.CoverableUnit{StLine: 1, StCol: 2, EnLine: 3, EnCol: 4, NxStmts: 5},
|
|
coverage.CoverableUnit{StLine: 6, StCol: 7, EnLine: 8, EnCol: 9, NxStmts: 10},
|
|
coverage.CoverableUnit{StLine: 11, StCol: 12, EnLine: 13, EnCol: 14, NxStmts: 15},
|
|
},
|
|
}
|
|
idx = b.AddFunc(f2)
|
|
if idx != 1 {
|
|
t.Errorf("b.AddFunc(f2) got %d want %d", idx, 0)
|
|
}
|
|
|
|
// Emit into a writer.
|
|
drws := &slicewriter.WriteSeeker{}
|
|
b.Emit(drws)
|
|
|
|
// Test decode path.
|
|
drws.Seek(0, io.SeekStart)
|
|
dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false)
|
|
if err != nil {
|
|
t.Fatalf("NewCoverageMetaDataDecoder error: %v", err)
|
|
}
|
|
nf := dec.NumFuncs()
|
|
if nf != 2 {
|
|
t.Errorf("dec.NumFuncs(): got %d want %d", nf, 2)
|
|
}
|
|
|
|
gotpp := dec.PackagePath()
|
|
if gotpp != pp {
|
|
t.Errorf("packagepath: got %s want %s", gotpp, pp)
|
|
}
|
|
gotpn := dec.PackageName()
|
|
if gotpn != pn {
|
|
t.Errorf("packagename: got %s want %s", gotpn, pn)
|
|
}
|
|
|
|
cases := []coverage.FuncDesc{f1, f2}
|
|
for i := uint32(0); i < uint32(len(cases)); i++ {
|
|
var fn coverage.FuncDesc
|
|
if err := dec.ReadFunc(i, &fn); err != nil {
|
|
t.Fatalf("err reading function %d: %v", i, err)
|
|
}
|
|
res := cmpFuncDesc(cases[i], fn)
|
|
if res != "" {
|
|
t.Errorf("ReadFunc(%d): %s", i, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createFuncs(i int) []coverage.FuncDesc {
|
|
res := []coverage.FuncDesc{}
|
|
lc := uint32(1)
|
|
for fi := 0; fi < i+1; fi++ {
|
|
units := []coverage.CoverableUnit{}
|
|
for ui := 0; ui < (fi+1)*(i+1); ui++ {
|
|
units = append(units,
|
|
coverage.CoverableUnit{StLine: lc, StCol: lc + 1,
|
|
EnLine: lc + 2, EnCol: lc + 3, NxStmts: lc + 4,
|
|
})
|
|
lc += 5
|
|
}
|
|
f := coverage.FuncDesc{
|
|
Funcname: fmt.Sprintf("func_%d_%d", i, fi),
|
|
Srcfile: fmt.Sprintf("foo_%d.go", i),
|
|
Units: units,
|
|
}
|
|
res = append(res, f)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func createBlob(t *testing.T, i int) []byte {
|
|
nomodule := ""
|
|
b, err := encodemeta.NewCoverageMetaDataBuilder("foo/pkg", "pkg", nomodule)
|
|
if err != nil {
|
|
t.Fatalf("making builder: %v", err)
|
|
}
|
|
|
|
funcs := createFuncs(i)
|
|
for _, f := range funcs {
|
|
b.AddFunc(f)
|
|
}
|
|
drws := &slicewriter.WriteSeeker{}
|
|
b.Emit(drws)
|
|
return drws.BytesWritten()
|
|
}
|
|
|
|
func createMetaDataBlobs(t *testing.T, nb int) [][]byte {
|
|
res := [][]byte{}
|
|
for i := 0; i < nb; i++ {
|
|
res = append(res, createBlob(t, i))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func TestMetaDataWriterReader(t *testing.T) {
|
|
d := t.TempDir()
|
|
|
|
// Emit a meta-file...
|
|
mfpath := filepath.Join(d, "covmeta.hash.0")
|
|
of, err := os.OpenFile(mfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
t.Fatalf("opening covmeta: %v", err)
|
|
}
|
|
//t.Logf("meta-file path is %s", mfpath)
|
|
blobs := createMetaDataBlobs(t, 7)
|
|
gran := coverage.CtrGranularityPerBlock
|
|
mfw := encodemeta.NewCoverageMetaFileWriter(mfpath, of)
|
|
finalHash := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
|
err = mfw.Write(finalHash, blobs, coverage.CtrModeAtomic, gran)
|
|
if err != nil {
|
|
t.Fatalf("writing meta-file: %v", err)
|
|
}
|
|
if err = of.Close(); err != nil {
|
|
t.Fatalf("closing meta-file: %v", err)
|
|
}
|
|
|
|
// ... then read it back in, first time without setting fileView,
|
|
// second time setting it.
|
|
for k := 0; k < 2; k++ {
|
|
var fileView []byte
|
|
|
|
inf, err := os.Open(mfpath)
|
|
if err != nil {
|
|
t.Fatalf("open() on meta-file: %v", err)
|
|
}
|
|
|
|
if k != 0 {
|
|
// Use fileview to exercise different paths in reader.
|
|
fi, err := os.Stat(mfpath)
|
|
if err != nil {
|
|
t.Fatalf("stat() on meta-file: %v", err)
|
|
}
|
|
fileView = make([]byte, fi.Size())
|
|
if _, err := inf.Read(fileView); err != nil {
|
|
t.Fatalf("read() on meta-file: %v", err)
|
|
}
|
|
if _, err := inf.Seek(int64(0), io.SeekStart); err != nil {
|
|
t.Fatalf("seek() on meta-file: %v", err)
|
|
}
|
|
}
|
|
|
|
mfr, err := decodemeta.NewCoverageMetaFileReader(inf, fileView)
|
|
if err != nil {
|
|
t.Fatalf("k=%d NewCoverageMetaFileReader failed with: %v", k, err)
|
|
}
|
|
np := mfr.NumPackages()
|
|
if np != 7 {
|
|
t.Fatalf("k=%d wanted 7 packages got %d", k, np)
|
|
}
|
|
md := mfr.CounterMode()
|
|
wmd := coverage.CtrModeAtomic
|
|
if md != wmd {
|
|
t.Fatalf("k=%d wanted mode %d got %d", k, wmd, md)
|
|
}
|
|
gran := mfr.CounterGranularity()
|
|
wgran := coverage.CtrGranularityPerBlock
|
|
if gran != wgran {
|
|
t.Fatalf("k=%d wanted gran %d got %d", k, wgran, gran)
|
|
}
|
|
|
|
payload := []byte{}
|
|
for pi := 0; pi < int(np); pi++ {
|
|
var pd *decodemeta.CoverageMetaDataDecoder
|
|
var err error
|
|
pd, payload, err = mfr.GetPackageDecoder(uint32(pi), payload)
|
|
if err != nil {
|
|
t.Fatalf("GetPackageDecoder(%d) failed with: %v", pi, err)
|
|
}
|
|
efuncs := createFuncs(pi)
|
|
nf := pd.NumFuncs()
|
|
if len(efuncs) != int(nf) {
|
|
t.Fatalf("decoding pk %d wanted %d funcs got %d",
|
|
pi, len(efuncs), nf)
|
|
}
|
|
var f coverage.FuncDesc
|
|
for fi := 0; fi < int(nf); fi++ {
|
|
if err := pd.ReadFunc(uint32(fi), &f); err != nil {
|
|
t.Fatalf("ReadFunc(%d) pk %d got error %v",
|
|
fi, pi, err)
|
|
}
|
|
res := cmpFuncDesc(efuncs[fi], f)
|
|
if res != "" {
|
|
t.Errorf("ReadFunc(%d) pk %d: %s", fi, pi, res)
|
|
}
|
|
}
|
|
}
|
|
inf.Close()
|
|
}
|
|
}
|
|
|
|
func TestMetaDataDecodeLitFlagIssue57942(t *testing.T) {
|
|
|
|
// Encode a package with a few functions. The funcs alternate
|
|
// between regular functions and function literals.
|
|
pp := "foo/bar/pkg"
|
|
pn := "pkg"
|
|
mp := "barmod"
|
|
b, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp)
|
|
if err != nil {
|
|
t.Fatalf("making builder: %v", err)
|
|
}
|
|
const NF = 6
|
|
const NCU = 1
|
|
ln := uint32(10)
|
|
wantfds := []coverage.FuncDesc{}
|
|
for fi := uint32(0); fi < NF; fi++ {
|
|
fis := fmt.Sprintf("%d", fi)
|
|
fd := coverage.FuncDesc{
|
|
Funcname: "func" + fis,
|
|
Srcfile: "foo" + fis + ".go",
|
|
Units: []coverage.CoverableUnit{
|
|
coverage.CoverableUnit{StLine: ln + 1, StCol: 2, EnLine: ln + 3, EnCol: 4, NxStmts: fi + 2},
|
|
},
|
|
Lit: (fi % 2) == 0,
|
|
}
|
|
wantfds = append(wantfds, fd)
|
|
b.AddFunc(fd)
|
|
}
|
|
|
|
// Emit into a writer.
|
|
drws := &slicewriter.WriteSeeker{}
|
|
b.Emit(drws)
|
|
|
|
// Decode the result.
|
|
drws.Seek(0, io.SeekStart)
|
|
dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false)
|
|
if err != nil {
|
|
t.Fatalf("making decoder: %v", err)
|
|
}
|
|
nf := dec.NumFuncs()
|
|
if nf != NF {
|
|
t.Fatalf("decoder number of functions: got %d want %d", nf, NF)
|
|
}
|
|
var fn coverage.FuncDesc
|
|
for i := uint32(0); i < uint32(NF); i++ {
|
|
if err := dec.ReadFunc(i, &fn); err != nil {
|
|
t.Fatalf("err reading function %d: %v", i, err)
|
|
}
|
|
res := cmpFuncDesc(wantfds[i], fn)
|
|
if res != "" {
|
|
t.Errorf("ReadFunc(%d): %s", i, res)
|
|
}
|
|
}
|
|
}
|