mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Add utilities for reading and writing of counter data files as part of the new code coverage implementation. Trivia note: currently the contents of counter data files are emitted in little-endian form, which for the counters is somewhat painful in that we need to visit each counter value and properly encode it. It might be better to instead emit counters in native endianity and then teach the tools to decode properly in the case of an endianity mismatch. Updates #51430. Change-Id: I124fdcb40fc339a48b64b35264bf24c3be50ddd4 Reviewed-on: https://go-review.googlesource.com/c/go/+/359403 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Than McIntosh <thanm@google.com>
275 lines
7 KiB
Go
275 lines
7 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"
|
|
"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 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), 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()
|
|
}
|
|
}
|