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