blob: 3fc111ea123cdc329bb9cb9ef90b58fbd4bf1192 [file] [log] [blame]
// 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/decodecounter"
"internal/coverage/encodecounter"
"io"
"os"
"path/filepath"
"testing"
)
type ctrVis struct {
funcs []decodecounter.FuncPayload
}
func (v *ctrVis) NumFuncs() (int, error) {
return len(v.funcs), nil
}
func (v *ctrVis) VisitFuncs(f encodecounter.CounterVisitorFn) error {
for _, fn := range v.funcs {
if err := f(fn.PkgIdx, fn.FuncIdx, fn.Counters); err != nil {
return err
}
}
return nil
}
func mkfunc(p uint32, f uint32, c []uint32) decodecounter.FuncPayload {
return decodecounter.FuncPayload{
PkgIdx: p,
FuncIdx: f,
Counters: c,
}
}
func TestCounterDataWriterReader(t *testing.T) {
flavors := []coverage.CounterFlavor{
coverage.CtrRaw,
coverage.CtrULeb128,
}
isDead := func(fp decodecounter.FuncPayload) bool {
for _, v := range fp.Counters {
if v != 0 {
return false
}
}
return true
}
funcs := []decodecounter.FuncPayload{
mkfunc(0, 0, []uint32{13, 14, 15}),
mkfunc(0, 1, []uint32{16, 17}),
mkfunc(1, 0, []uint32{18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 976543, 7}),
}
writeVisitor := &ctrVis{funcs: funcs}
for kf, flav := range flavors {
t.Logf("testing flavor %d\n", flav)
// Open a counter data file in preparation for emitting data.
d := t.TempDir()
cfpath := filepath.Join(d, fmt.Sprintf("covcounters.hash.0.%d", kf))
of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
t.Fatalf("opening covcounters: %v", err)
}
// Perform the encode and write.
cdfw := encodecounter.NewCoverageDataWriter(of, flav)
if cdfw == nil {
t.Fatalf("NewCoverageDataWriter failed")
}
finalHash := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}
args := map[string]string{"argc": "3", "argv0": "arg0", "argv1": "arg1", "argv2": "arg_________2"}
if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
t.Fatalf("counter file Write failed: %v", err)
}
if err := of.Close(); err != nil {
t.Fatalf("closing covcounters: %v", err)
}
cdfw = nil
// Decode the same file.
var cdr *decodecounter.CounterDataReader
inf, err := os.Open(cfpath)
defer func() {
if err := inf.Close(); err != nil {
t.Fatalf("close failed with: %v", err)
}
}()
if err != nil {
t.Fatalf("reopening covcounters file: %v", err)
}
if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
t.Fatalf("opening covcounters for read: %v", err)
}
decodedArgs := cdr.OsArgs()
aWant := "[arg0 arg1 arg_________2]"
aGot := fmt.Sprintf("%+v", decodedArgs)
if aWant != aGot {
t.Errorf("reading decoded args, got %s want %s", aGot, aWant)
}
for i := range funcs {
if isDead(funcs[i]) {
continue
}
var fp decodecounter.FuncPayload
if ok, err := cdr.NextFunc(&fp); err != nil {
t.Fatalf("reading func %d: %v", i, err)
} else if !ok {
t.Fatalf("reading func %d: bad return", i)
}
got := fmt.Sprintf("%+v", fp)
want := fmt.Sprintf("%+v", funcs[i])
if got != want {
t.Errorf("cdr.NextFunc iter %d\ngot %+v\nwant %+v", i, got, want)
}
}
var dummy decodecounter.FuncPayload
if ok, err := cdr.NextFunc(&dummy); err != nil {
t.Fatalf("reading func after loop: %v", err)
} else if ok {
t.Fatalf("reading func after loop: expected EOF")
}
}
}
func TestCounterDataAppendSegment(t *testing.T) {
d := t.TempDir()
cfpath := filepath.Join(d, "covcounters.hash2.0")
of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
t.Fatalf("opening covcounters: %v", err)
}
const numSegments = 2
// Write a counter with with multiple segments.
args := map[string]string{"argc": "1", "argv0": "prog.exe"}
allfuncs := [][]decodecounter.FuncPayload{}
ctrs := []uint32{}
q := uint32(0)
var cdfw *encodecounter.CoverageDataWriter
for idx := 0; idx < numSegments; idx++ {
args[fmt.Sprintf("seg%d", idx)] = "x"
q += 7
ctrs = append(ctrs, q)
funcs := []decodecounter.FuncPayload{}
for k := 0; k < idx+1; k++ {
c := make([]uint32, len(ctrs))
copy(c, ctrs)
funcs = append(funcs, mkfunc(uint32(idx), uint32(k), c))
}
allfuncs = append(allfuncs, funcs)
writeVisitor := &ctrVis{funcs: funcs}
if idx == 0 {
// Perform the encode and write.
cdfw = encodecounter.NewCoverageDataWriter(of, coverage.CtrRaw)
if cdfw == nil {
t.Fatalf("NewCoverageDataWriter failed")
}
finalHash := [16]byte{1, 2}
if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
t.Fatalf("counter file Write failed: %v", err)
}
} else {
if err := cdfw.AppendSegment(args, writeVisitor); err != nil {
t.Fatalf("counter file AppendSegment failed: %v", err)
}
}
}
if err := of.Close(); err != nil {
t.Fatalf("closing covcounters: %v", err)
}
// Read the result file.
var cdr *decodecounter.CounterDataReader
inf, err := os.Open(cfpath)
defer func() {
if err := inf.Close(); err != nil {
t.Fatalf("close failed with: %v", err)
}
}()
if err != nil {
t.Fatalf("reopening covcounters file: %v", err)
}
if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
t.Fatalf("opening covcounters for read: %v", err)
}
ns := cdr.NumSegments()
if ns != numSegments {
t.Fatalf("got %d segments want %d", ns, numSegments)
}
if len(allfuncs) != numSegments {
t.Fatalf("expected %d got %d", numSegments, len(allfuncs))
}
for sidx := 0; sidx < int(ns); sidx++ {
if off, err := inf.Seek(0, io.SeekCurrent); err != nil {
t.Fatalf("Seek failed: %v", err)
} else {
t.Logf("sidx=%d off=%d\n", sidx, off)
}
if sidx != 0 {
if ok, err := cdr.BeginNextSegment(); err != nil {
t.Fatalf("BeginNextSegment failed: %v", err)
} else if !ok {
t.Fatalf("BeginNextSegment return %v on iter %d",
ok, sidx)
}
}
funcs := allfuncs[sidx]
for i := range funcs {
var fp decodecounter.FuncPayload
if ok, err := cdr.NextFunc(&fp); err != nil {
t.Fatalf("reading func %d: %v", i, err)
} else if !ok {
t.Fatalf("reading func %d: bad return", i)
}
got := fmt.Sprintf("%+v", fp)
want := fmt.Sprintf("%+v", funcs[i])
if got != want {
t.Errorf("cdr.NextFunc iter %d\ngot %+v\nwant %+v", i, got, want)
}
}
}
}