mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
internal/coverage: add coverage meta-data decoder
Add a coverage meta-data decoder, which provides APIs for reading encoded coverage meta-data and expanding it usable form. This package is intended to be used in the coverage tooling that reads data files emitted from coverage runs. Along with the new decoding package is a unit test that runs the encode/decode paths together to check to make sure that "decode(encode(X)) == X". Updates #51430. Change-Id: I81d27d8da0b2fcfa5039114a6e35a4b463d19b3c Reviewed-on: https://go-review.googlesource.com/c/go/+/353454 Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
f951f697c4
commit
84f95aa811
9 changed files with 1082 additions and 3 deletions
275
src/internal/coverage/test/roundtrip_test.go
Normal file
275
src/internal/coverage/test/roundtrip_test.go
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// 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"
|
||||
"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, os.SEEK_SET)
|
||||
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, os.SEEK_SET)
|
||||
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 TestMetaDataFileWriterReader(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), os.SEEK_SET); 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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue