// 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) } } }