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