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  }