diff options
Diffstat (limited to 'src/internal/coverage/test')
-rw-r--r-- | src/internal/coverage/test/counter_test.go | 237 | ||||
-rw-r--r-- | src/internal/coverage/test/roundtrip_test.go | 331 |
2 files changed, 568 insertions, 0 deletions
diff --git a/src/internal/coverage/test/counter_test.go b/src/internal/coverage/test/counter_test.go new file mode 100644 index 0000000..e29baed --- /dev/null +++ b/src/internal/coverage/test/counter_test.go @@ -0,0 +1,237 @@ +// 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) 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) + } + } + } +} diff --git a/src/internal/coverage/test/roundtrip_test.go b/src/internal/coverage/test/roundtrip_test.go new file mode 100644 index 0000000..614f56e --- /dev/null +++ b/src/internal/coverage/test/roundtrip_test.go @@ -0,0 +1,331 @@ +// 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) + } + } +} |