github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/coverage/test/counter_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/decodecounter"
    16  	"github.com/go-asm/go/coverage/encodecounter"
    17  )
    18  
    19  type ctrVis struct {
    20  	funcs []decodecounter.FuncPayload
    21  }
    22  
    23  func (v *ctrVis) VisitFuncs(f encodecounter.CounterVisitorFn) error {
    24  	for _, fn := range v.funcs {
    25  		if err := f(fn.PkgIdx, fn.FuncIdx, fn.Counters); err != nil {
    26  			return err
    27  		}
    28  	}
    29  	return nil
    30  }
    31  
    32  func mkfunc(p uint32, f uint32, c []uint32) decodecounter.FuncPayload {
    33  	return decodecounter.FuncPayload{
    34  		PkgIdx:   p,
    35  		FuncIdx:  f,
    36  		Counters: c,
    37  	}
    38  }
    39  
    40  func TestCounterDataWriterReader(t *testing.T) {
    41  	flavors := []coverage.CounterFlavor{
    42  		coverage.CtrRaw,
    43  		coverage.CtrULeb128,
    44  	}
    45  
    46  	isDead := func(fp decodecounter.FuncPayload) bool {
    47  		for _, v := range fp.Counters {
    48  			if v != 0 {
    49  				return false
    50  			}
    51  		}
    52  		return true
    53  	}
    54  
    55  	funcs := []decodecounter.FuncPayload{
    56  		mkfunc(0, 0, []uint32{13, 14, 15}),
    57  		mkfunc(0, 1, []uint32{16, 17}),
    58  		mkfunc(1, 0, []uint32{18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 976543, 7}),
    59  	}
    60  	writeVisitor := &ctrVis{funcs: funcs}
    61  
    62  	for kf, flav := range flavors {
    63  
    64  		t.Logf("testing flavor %d\n", flav)
    65  
    66  		// Open a counter data file in preparation for emitting data.
    67  		d := t.TempDir()
    68  		cfpath := filepath.Join(d, fmt.Sprintf("covcounters.hash.0.%d", kf))
    69  		of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    70  		if err != nil {
    71  			t.Fatalf("opening covcounters: %v", err)
    72  		}
    73  
    74  		// Perform the encode and write.
    75  		cdfw := encodecounter.NewCoverageDataWriter(of, flav)
    76  		if cdfw == nil {
    77  			t.Fatalf("NewCoverageDataWriter failed")
    78  		}
    79  		finalHash := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}
    80  		args := map[string]string{"argc": "3", "argv0": "arg0", "argv1": "arg1", "argv2": "arg_________2"}
    81  		if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
    82  			t.Fatalf("counter file Write failed: %v", err)
    83  		}
    84  		if err := of.Close(); err != nil {
    85  			t.Fatalf("closing covcounters: %v", err)
    86  		}
    87  		cdfw = nil
    88  
    89  		// Decode the same file.
    90  		var cdr *decodecounter.CounterDataReader
    91  		inf, err := os.Open(cfpath)
    92  		defer func() {
    93  			if err := inf.Close(); err != nil {
    94  				t.Fatalf("close failed with: %v", err)
    95  			}
    96  		}()
    97  
    98  		if err != nil {
    99  			t.Fatalf("reopening covcounters file: %v", err)
   100  		}
   101  		if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
   102  			t.Fatalf("opening covcounters for read: %v", err)
   103  		}
   104  		decodedArgs := cdr.OsArgs()
   105  		aWant := "[arg0 arg1 arg_________2]"
   106  		aGot := fmt.Sprintf("%+v", decodedArgs)
   107  		if aWant != aGot {
   108  			t.Errorf("reading decoded args, got %s want %s", aGot, aWant)
   109  		}
   110  		for i := range funcs {
   111  			if isDead(funcs[i]) {
   112  				continue
   113  			}
   114  			var fp decodecounter.FuncPayload
   115  			if ok, err := cdr.NextFunc(&fp); err != nil {
   116  				t.Fatalf("reading func %d: %v", i, err)
   117  			} else if !ok {
   118  				t.Fatalf("reading func %d: bad return", i)
   119  			}
   120  			got := fmt.Sprintf("%+v", fp)
   121  			want := fmt.Sprintf("%+v", funcs[i])
   122  			if got != want {
   123  				t.Errorf("cdr.NextFunc iter %d\ngot  %+v\nwant %+v", i, got, want)
   124  			}
   125  		}
   126  		var dummy decodecounter.FuncPayload
   127  		if ok, err := cdr.NextFunc(&dummy); err != nil {
   128  			t.Fatalf("reading func after loop: %v", err)
   129  		} else if ok {
   130  			t.Fatalf("reading func after loop: expected EOF")
   131  		}
   132  	}
   133  }
   134  
   135  func TestCounterDataAppendSegment(t *testing.T) {
   136  	d := t.TempDir()
   137  	cfpath := filepath.Join(d, "covcounters.hash2.0")
   138  	of, err := os.OpenFile(cfpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
   139  	if err != nil {
   140  		t.Fatalf("opening covcounters: %v", err)
   141  	}
   142  
   143  	const numSegments = 2
   144  
   145  	// Write a counter with with multiple segments.
   146  	args := map[string]string{"argc": "1", "argv0": "prog.exe"}
   147  	allfuncs := [][]decodecounter.FuncPayload{}
   148  	ctrs := []uint32{}
   149  	q := uint32(0)
   150  	var cdfw *encodecounter.CoverageDataWriter
   151  	for idx := 0; idx < numSegments; idx++ {
   152  		args[fmt.Sprintf("seg%d", idx)] = "x"
   153  		q += 7
   154  		ctrs = append(ctrs, q)
   155  		funcs := []decodecounter.FuncPayload{}
   156  		for k := 0; k < idx+1; k++ {
   157  			c := make([]uint32, len(ctrs))
   158  			copy(c, ctrs)
   159  			funcs = append(funcs, mkfunc(uint32(idx), uint32(k), c))
   160  		}
   161  		allfuncs = append(allfuncs, funcs)
   162  
   163  		writeVisitor := &ctrVis{funcs: funcs}
   164  
   165  		if idx == 0 {
   166  			// Perform the encode and write.
   167  			cdfw = encodecounter.NewCoverageDataWriter(of, coverage.CtrRaw)
   168  			if cdfw == nil {
   169  				t.Fatalf("NewCoverageDataWriter failed")
   170  			}
   171  			finalHash := [16]byte{1, 2}
   172  			if err := cdfw.Write(finalHash, args, writeVisitor); err != nil {
   173  				t.Fatalf("counter file Write failed: %v", err)
   174  			}
   175  		} else {
   176  			if err := cdfw.AppendSegment(args, writeVisitor); err != nil {
   177  				t.Fatalf("counter file AppendSegment failed: %v", err)
   178  			}
   179  		}
   180  	}
   181  	if err := of.Close(); err != nil {
   182  		t.Fatalf("closing covcounters: %v", err)
   183  	}
   184  
   185  	// Read the result file.
   186  	var cdr *decodecounter.CounterDataReader
   187  	inf, err := os.Open(cfpath)
   188  	defer func() {
   189  		if err := inf.Close(); err != nil {
   190  			t.Fatalf("close failed with: %v", err)
   191  		}
   192  	}()
   193  
   194  	if err != nil {
   195  		t.Fatalf("reopening covcounters file: %v", err)
   196  	}
   197  	if cdr, err = decodecounter.NewCounterDataReader(cfpath, inf); err != nil {
   198  		t.Fatalf("opening covcounters for read: %v", err)
   199  	}
   200  	ns := cdr.NumSegments()
   201  	if ns != numSegments {
   202  		t.Fatalf("got %d segments want %d", ns, numSegments)
   203  	}
   204  	if len(allfuncs) != numSegments {
   205  		t.Fatalf("expected %d got %d", numSegments, len(allfuncs))
   206  	}
   207  
   208  	for sidx := 0; sidx < int(ns); sidx++ {
   209  		if off, err := inf.Seek(0, io.SeekCurrent); err != nil {
   210  			t.Fatalf("Seek failed: %v", err)
   211  		} else {
   212  			t.Logf("sidx=%d off=%d\n", sidx, off)
   213  		}
   214  
   215  		if sidx != 0 {
   216  			if ok, err := cdr.BeginNextSegment(); err != nil {
   217  				t.Fatalf("BeginNextSegment failed: %v", err)
   218  			} else if !ok {
   219  				t.Fatalf("BeginNextSegment return %v on iter %d",
   220  					ok, sidx)
   221  			}
   222  		}
   223  		funcs := allfuncs[sidx]
   224  		for i := range funcs {
   225  			var fp decodecounter.FuncPayload
   226  			if ok, err := cdr.NextFunc(&fp); err != nil {
   227  				t.Fatalf("reading func %d: %v", i, err)
   228  			} else if !ok {
   229  				t.Fatalf("reading func %d: bad return", i)
   230  			}
   231  			got := fmt.Sprintf("%+v", fp)
   232  			want := fmt.Sprintf("%+v", funcs[i])
   233  			if got != want {
   234  				t.Errorf("cdr.NextFunc iter %d\ngot  %+v\nwant %+v", i, got, want)
   235  			}
   236  		}
   237  	}
   238  }