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 }