github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/coverage/test/roundtrip_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package test 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "path/filepath" 12 "testing" 13 14 "github.com/go-asm/go/coverage" 15 "github.com/go-asm/go/coverage/decodemeta" 16 "github.com/go-asm/go/coverage/encodemeta" 17 "github.com/go-asm/go/coverage/slicewriter" 18 ) 19 20 func cmpFuncDesc(want, got coverage.FuncDesc) string { 21 swant := fmt.Sprintf("%+v", want) 22 sgot := fmt.Sprintf("%+v", got) 23 if swant == sgot { 24 return "" 25 } 26 return fmt.Sprintf("wanted %q got %q", swant, sgot) 27 } 28 29 func TestMetaDataEmptyPackage(t *testing.T) { 30 // Make sure that encoding/decoding works properly with packages 31 // that don't actually have any functions. 32 p := "empty/package" 33 pn := "package" 34 mp := "m" 35 b, err := encodemeta.NewCoverageMetaDataBuilder(p, pn, mp) 36 if err != nil { 37 t.Fatalf("making builder: %v", err) 38 } 39 drws := &slicewriter.WriteSeeker{} 40 b.Emit(drws) 41 drws.Seek(0, io.SeekStart) 42 dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false) 43 if err != nil { 44 t.Fatalf("making decoder: %v", err) 45 } 46 nf := dec.NumFuncs() 47 if nf != 0 { 48 t.Errorf("dec.NumFuncs(): got %d want %d", nf, 0) 49 } 50 pp := dec.PackagePath() 51 if pp != p { 52 t.Errorf("dec.PackagePath(): got %s want %s", pp, p) 53 } 54 ppn := dec.PackageName() 55 if ppn != pn { 56 t.Errorf("dec.PackageName(): got %s want %s", ppn, pn) 57 } 58 pmp := dec.ModulePath() 59 if pmp != mp { 60 t.Errorf("dec.ModulePath(): got %s want %s", pmp, mp) 61 } 62 } 63 64 func TestMetaDataEncoderDecoder(t *testing.T) { 65 // Test encode path. 66 pp := "foo/bar/pkg" 67 pn := "pkg" 68 mp := "barmod" 69 b, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp) 70 if err != nil { 71 t.Fatalf("making builder: %v", err) 72 } 73 f1 := coverage.FuncDesc{ 74 Funcname: "func", 75 Srcfile: "foo.go", 76 Units: []coverage.CoverableUnit{ 77 {StLine: 1, StCol: 2, EnLine: 3, EnCol: 4, NxStmts: 5}, 78 {StLine: 6, StCol: 7, EnLine: 8, EnCol: 9, NxStmts: 10}, 79 }, 80 } 81 idx := b.AddFunc(f1) 82 if idx != 0 { 83 t.Errorf("b.AddFunc(f1) got %d want %d", idx, 0) 84 } 85 86 f2 := coverage.FuncDesc{ 87 Funcname: "xfunc", 88 Srcfile: "bar.go", 89 Units: []coverage.CoverableUnit{ 90 {StLine: 1, StCol: 2, EnLine: 3, EnCol: 4, NxStmts: 5}, 91 {StLine: 6, StCol: 7, EnLine: 8, EnCol: 9, NxStmts: 10}, 92 {StLine: 11, StCol: 12, EnLine: 13, EnCol: 14, NxStmts: 15}, 93 }, 94 } 95 idx = b.AddFunc(f2) 96 if idx != 1 { 97 t.Errorf("b.AddFunc(f2) got %d want %d", idx, 0) 98 } 99 100 // Emit into a writer. 101 drws := &slicewriter.WriteSeeker{} 102 b.Emit(drws) 103 104 // Test decode path. 105 drws.Seek(0, io.SeekStart) 106 dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false) 107 if err != nil { 108 t.Fatalf("NewCoverageMetaDataDecoder error: %v", err) 109 } 110 nf := dec.NumFuncs() 111 if nf != 2 { 112 t.Errorf("dec.NumFuncs(): got %d want %d", nf, 2) 113 } 114 115 gotpp := dec.PackagePath() 116 if gotpp != pp { 117 t.Errorf("packagepath: got %s want %s", gotpp, pp) 118 } 119 gotpn := dec.PackageName() 120 if gotpn != pn { 121 t.Errorf("packagename: got %s want %s", gotpn, pn) 122 } 123 124 cases := []coverage.FuncDesc{f1, f2} 125 for i := uint32(0); i < uint32(len(cases)); i++ { 126 var fn coverage.FuncDesc 127 if err := dec.ReadFunc(i, &fn); err != nil { 128 t.Fatalf("err reading function %d: %v", i, err) 129 } 130 res := cmpFuncDesc(cases[i], fn) 131 if res != "" { 132 t.Errorf("ReadFunc(%d): %s", i, res) 133 } 134 } 135 } 136 137 func createFuncs(i int) []coverage.FuncDesc { 138 res := []coverage.FuncDesc{} 139 lc := uint32(1) 140 for fi := 0; fi < i+1; fi++ { 141 units := []coverage.CoverableUnit{} 142 for ui := 0; ui < (fi+1)*(i+1); ui++ { 143 units = append(units, 144 coverage.CoverableUnit{StLine: lc, StCol: lc + 1, 145 EnLine: lc + 2, EnCol: lc + 3, NxStmts: lc + 4, 146 }) 147 lc += 5 148 } 149 f := coverage.FuncDesc{ 150 Funcname: fmt.Sprintf("func_%d_%d", i, fi), 151 Srcfile: fmt.Sprintf("foo_%d.go", i), 152 Units: units, 153 } 154 res = append(res, f) 155 } 156 return res 157 } 158 159 func createBlob(t *testing.T, i int) []byte { 160 nomodule := "" 161 b, err := encodemeta.NewCoverageMetaDataBuilder("foo/pkg", "pkg", nomodule) 162 if err != nil { 163 t.Fatalf("making builder: %v", err) 164 } 165 166 funcs := createFuncs(i) 167 for _, f := range funcs { 168 b.AddFunc(f) 169 } 170 drws := &slicewriter.WriteSeeker{} 171 b.Emit(drws) 172 return drws.BytesWritten() 173 } 174 175 func createMetaDataBlobs(t *testing.T, nb int) [][]byte { 176 res := [][]byte{} 177 for i := 0; i < nb; i++ { 178 res = append(res, createBlob(t, i)) 179 } 180 return res 181 } 182 183 func TestMetaDataWriterReader(t *testing.T) { 184 d := t.TempDir() 185 186 // Emit a meta-file... 187 mfpath := filepath.Join(d, "covmeta.hash.0") 188 of, err := os.OpenFile(mfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) 189 if err != nil { 190 t.Fatalf("opening covmeta: %v", err) 191 } 192 //t.Logf("meta-file path is %s", mfpath) 193 blobs := createMetaDataBlobs(t, 7) 194 gran := coverage.CtrGranularityPerBlock 195 mfw := encodemeta.NewCoverageMetaFileWriter(mfpath, of) 196 finalHash := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 197 err = mfw.Write(finalHash, blobs, coverage.CtrModeAtomic, gran) 198 if err != nil { 199 t.Fatalf("writing meta-file: %v", err) 200 } 201 if err = of.Close(); err != nil { 202 t.Fatalf("closing meta-file: %v", err) 203 } 204 205 // ... then read it back in, first time without setting fileView, 206 // second time setting it. 207 for k := 0; k < 2; k++ { 208 var fileView []byte 209 210 inf, err := os.Open(mfpath) 211 if err != nil { 212 t.Fatalf("open() on meta-file: %v", err) 213 } 214 215 if k != 0 { 216 // Use fileview to exercise different paths in reader. 217 fi, err := os.Stat(mfpath) 218 if err != nil { 219 t.Fatalf("stat() on meta-file: %v", err) 220 } 221 fileView = make([]byte, fi.Size()) 222 if _, err := inf.Read(fileView); err != nil { 223 t.Fatalf("read() on meta-file: %v", err) 224 } 225 if _, err := inf.Seek(int64(0), io.SeekStart); err != nil { 226 t.Fatalf("seek() on meta-file: %v", err) 227 } 228 } 229 230 mfr, err := decodemeta.NewCoverageMetaFileReader(inf, fileView) 231 if err != nil { 232 t.Fatalf("k=%d NewCoverageMetaFileReader failed with: %v", k, err) 233 } 234 np := mfr.NumPackages() 235 if np != 7 { 236 t.Fatalf("k=%d wanted 7 packages got %d", k, np) 237 } 238 md := mfr.CounterMode() 239 wmd := coverage.CtrModeAtomic 240 if md != wmd { 241 t.Fatalf("k=%d wanted mode %d got %d", k, wmd, md) 242 } 243 gran := mfr.CounterGranularity() 244 wgran := coverage.CtrGranularityPerBlock 245 if gran != wgran { 246 t.Fatalf("k=%d wanted gran %d got %d", k, wgran, gran) 247 } 248 249 payload := []byte{} 250 for pi := 0; pi < int(np); pi++ { 251 var pd *decodemeta.CoverageMetaDataDecoder 252 var err error 253 pd, payload, err = mfr.GetPackageDecoder(uint32(pi), payload) 254 if err != nil { 255 t.Fatalf("GetPackageDecoder(%d) failed with: %v", pi, err) 256 } 257 efuncs := createFuncs(pi) 258 nf := pd.NumFuncs() 259 if len(efuncs) != int(nf) { 260 t.Fatalf("decoding pk %d wanted %d funcs got %d", 261 pi, len(efuncs), nf) 262 } 263 var f coverage.FuncDesc 264 for fi := 0; fi < int(nf); fi++ { 265 if err := pd.ReadFunc(uint32(fi), &f); err != nil { 266 t.Fatalf("ReadFunc(%d) pk %d got error %v", 267 fi, pi, err) 268 } 269 res := cmpFuncDesc(efuncs[fi], f) 270 if res != "" { 271 t.Errorf("ReadFunc(%d) pk %d: %s", fi, pi, res) 272 } 273 } 274 } 275 inf.Close() 276 } 277 } 278 279 func TestMetaDataDecodeLitFlagIssue57942(t *testing.T) { 280 281 // Encode a package with a few functions. The funcs alternate 282 // between regular functions and function literals. 283 pp := "foo/bar/pkg" 284 pn := "pkg" 285 mp := "barmod" 286 b, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp) 287 if err != nil { 288 t.Fatalf("making builder: %v", err) 289 } 290 const NF = 6 291 const NCU = 1 292 ln := uint32(10) 293 wantfds := []coverage.FuncDesc{} 294 for fi := uint32(0); fi < NF; fi++ { 295 fis := fmt.Sprintf("%d", fi) 296 fd := coverage.FuncDesc{ 297 Funcname: "func" + fis, 298 Srcfile: "foo" + fis + ".go", 299 Units: []coverage.CoverableUnit{ 300 {StLine: ln + 1, StCol: 2, EnLine: ln + 3, EnCol: 4, NxStmts: fi + 2}, 301 }, 302 Lit: (fi % 2) == 0, 303 } 304 wantfds = append(wantfds, fd) 305 b.AddFunc(fd) 306 } 307 308 // Emit into a writer. 309 drws := &slicewriter.WriteSeeker{} 310 b.Emit(drws) 311 312 // Decode the result. 313 drws.Seek(0, io.SeekStart) 314 dec, err := decodemeta.NewCoverageMetaDataDecoder(drws.BytesWritten(), false) 315 if err != nil { 316 t.Fatalf("making decoder: %v", err) 317 } 318 nf := dec.NumFuncs() 319 if nf != NF { 320 t.Fatalf("decoder number of functions: got %d want %d", nf, NF) 321 } 322 var fn coverage.FuncDesc 323 for i := uint32(0); i < uint32(NF); i++ { 324 if err := dec.ReadFunc(i, &fn); err != nil { 325 t.Fatalf("err reading function %d: %v", i, err) 326 } 327 res := cmpFuncDesc(wantfds[i], fn) 328 if res != "" { 329 t.Errorf("ReadFunc(%d): %s", i, res) 330 } 331 } 332 }