• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2022 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 main
6
7import (
8	"flag"
9	"fmt"
10	"internal/coverage/slicewriter"
11	"io"
12	"log"
13	"os"
14	"path/filepath"
15	"runtime/coverage"
16	"strings"
17)
18
19var verbflag = flag.Int("v", 0, "Verbose trace output level")
20var testpointflag = flag.String("tp", "", "Testpoint to run")
21var outdirflag = flag.String("o", "", "Output dir into which to emit")
22
23func emitToWriter() {
24	log.SetPrefix("emitToWriter: ")
25	var slwm slicewriter.WriteSeeker
26	if err := coverage.WriteMeta(&slwm); err != nil {
27		log.Fatalf("error: WriteMeta returns %v", err)
28	}
29	mf := filepath.Join(*outdirflag, "covmeta.0abcdef")
30	if err := os.WriteFile(mf, slwm.BytesWritten(), 0666); err != nil {
31		log.Fatalf("error: writing %s: %v", mf, err)
32	}
33	var slwc slicewriter.WriteSeeker
34	if err := coverage.WriteCounters(&slwc); err != nil {
35		log.Fatalf("error: WriteCounters returns %v", err)
36	}
37	cf := filepath.Join(*outdirflag, "covcounters.0abcdef.99.77")
38	if err := os.WriteFile(cf, slwc.BytesWritten(), 0666); err != nil {
39		log.Fatalf("error: writing %s: %v", cf, err)
40	}
41}
42
43func emitToDir() {
44	log.SetPrefix("emitToDir: ")
45	if err := coverage.WriteMetaDir(*outdirflag); err != nil {
46		log.Fatalf("error: WriteMetaDir returns %v", err)
47	}
48	if err := coverage.WriteCountersDir(*outdirflag); err != nil {
49		log.Fatalf("error: WriteCountersDir returns %v", err)
50	}
51}
52
53func emitToNonexistentDir() {
54	log.SetPrefix("emitToNonexistentDir: ")
55
56	want := []string{
57		"no such file or directory",             // linux-ish
58		"system cannot find the file specified", // windows
59		"does not exist",                        // plan9
60	}
61
62	checkWant := func(which string, got string) {
63		found := false
64		for _, w := range want {
65			if strings.Contains(got, w) {
66				found = true
67				break
68			}
69		}
70		if !found {
71			log.Fatalf("%s emit to bad dir: got error:\n  %v\nwanted error with one of:\n  %+v", which, got, want)
72		}
73	}
74
75	// Mangle the output directory to produce something nonexistent.
76	mangled := *outdirflag + "_MANGLED"
77	if err := coverage.WriteMetaDir(mangled); err == nil {
78		log.Fatal("expected error from WriteMetaDir to nonexistent dir")
79	} else {
80		got := fmt.Sprintf("%v", err)
81		checkWant("meta data", got)
82	}
83
84	// Now try to emit counter data file to a bad dir.
85	if err := coverage.WriteCountersDir(mangled); err == nil {
86		log.Fatal("expected error emitting counter data to bad dir")
87	} else {
88		got := fmt.Sprintf("%v", err)
89		checkWant("counter data", got)
90	}
91}
92
93func emitToUnwritableDir() {
94	log.SetPrefix("emitToUnwritableDir: ")
95
96	want := "permission denied"
97
98	if err := coverage.WriteMetaDir(*outdirflag); err == nil {
99		log.Fatal("expected error from WriteMetaDir to unwritable dir")
100	} else {
101		got := fmt.Sprintf("%v", err)
102		if !strings.Contains(got, want) {
103			log.Fatalf("meta-data emit to unwritable dir: wanted error containing %q got %q", want, got)
104		}
105	}
106
107	// Similarly with writing counter data.
108	if err := coverage.WriteCountersDir(*outdirflag); err == nil {
109		log.Fatal("expected error emitting counter data to unwritable dir")
110	} else {
111		got := fmt.Sprintf("%v", err)
112		if !strings.Contains(got, want) {
113			log.Fatalf("emitting counter data to unwritable dir: wanted error containing %q got %q", want, got)
114		}
115	}
116}
117
118func emitToNilWriter() {
119	log.SetPrefix("emitToWriter: ")
120	want := "nil writer"
121	var bad io.WriteSeeker
122	if err := coverage.WriteMeta(bad); err == nil {
123		log.Fatal("expected error passing nil writer for meta emit")
124	} else {
125		got := fmt.Sprintf("%v", err)
126		if !strings.Contains(got, want) {
127			log.Fatalf("emitting meta-data passing nil writer: wanted error containing %q got %q", want, got)
128		}
129	}
130
131	if err := coverage.WriteCounters(bad); err == nil {
132		log.Fatal("expected error passing nil writer for counter emit")
133	} else {
134		got := fmt.Sprintf("%v", err)
135		if !strings.Contains(got, want) {
136			log.Fatalf("emitting counter data passing nil writer: wanted error containing %q got %q", want, got)
137		}
138	}
139}
140
141type failingWriter struct {
142	writeCount int
143	writeLimit int
144	slws       slicewriter.WriteSeeker
145}
146
147func (f *failingWriter) Write(p []byte) (n int, err error) {
148	c := f.writeCount
149	f.writeCount++
150	if f.writeLimit < 0 || c < f.writeLimit {
151		return f.slws.Write(p)
152	}
153	return 0, fmt.Errorf("manufactured write error")
154}
155
156func (f *failingWriter) Seek(offset int64, whence int) (int64, error) {
157	return f.slws.Seek(offset, whence)
158}
159
160func (f *failingWriter) reset(lim int) {
161	f.writeCount = 0
162	f.writeLimit = lim
163	f.slws = slicewriter.WriteSeeker{}
164}
165
166func writeStressTest(tag string, testf func(testf *failingWriter) error) {
167	// Invoke the function initially without the write limit
168	// set, to capture the number of writes performed.
169	fw := &failingWriter{writeLimit: -1}
170	testf(fw)
171
172	// Now that we know how many writes are going to happen, run the
173	// function repeatedly, each time with a Write operation set to
174	// fail at a new spot. The goal here is to make sure that:
175	// A) an error is reported, and B) nothing crashes.
176	tot := fw.writeCount
177	for i := 0; i < tot; i++ {
178		fw.reset(i)
179		err := testf(fw)
180		if err == nil {
181			log.Fatalf("no error from write %d tag %s", i, tag)
182		}
183	}
184}
185
186func postClear() int {
187	return 42
188}
189
190func preClear() int {
191	return 42
192}
193
194// This test is designed to ensure that write errors are properly
195// handled by the code that writes out coverage data. It repeatedly
196// invokes the 'emit to writer' apis using a specially crafted writer
197// that captures the total number of expected writes, then replays the
198// execution N times with a manufactured write error at the
199// appropriate spot.
200func emitToFailingWriter() {
201	log.SetPrefix("emitToFailingWriter: ")
202
203	writeStressTest("emit-meta", func(f *failingWriter) error {
204		return coverage.WriteMeta(f)
205	})
206	writeStressTest("emit-counter", func(f *failingWriter) error {
207		return coverage.WriteCounters(f)
208	})
209}
210
211func emitWithCounterClear() {
212	log.SetPrefix("emitWitCounterClear: ")
213	preClear()
214	if err := coverage.ClearCounters(); err != nil {
215		log.Fatalf("clear failed: %v", err)
216	}
217	postClear()
218	if err := coverage.WriteMetaDir(*outdirflag); err != nil {
219		log.Fatalf("error: WriteMetaDir returns %v", err)
220	}
221	if err := coverage.WriteCountersDir(*outdirflag); err != nil {
222		log.Fatalf("error: WriteCountersDir returns %v", err)
223	}
224}
225
226func final() int {
227	println("I run last.")
228	return 43
229}
230
231func main() {
232	log.SetFlags(0)
233	flag.Parse()
234	if *testpointflag == "" {
235		log.Fatalf("error: no testpoint (use -tp flag)")
236	}
237	if *outdirflag == "" {
238		log.Fatalf("error: no output dir specified (use -o flag)")
239	}
240	switch *testpointflag {
241	case "emitToDir":
242		emitToDir()
243	case "emitToWriter":
244		emitToWriter()
245	case "emitToNonexistentDir":
246		emitToNonexistentDir()
247	case "emitToUnwritableDir":
248		emitToUnwritableDir()
249	case "emitToNilWriter":
250		emitToNilWriter()
251	case "emitToFailingWriter":
252		emitToFailingWriter()
253	case "emitWithCounterClear":
254		emitWithCounterClear()
255	default:
256		log.Fatalf("error: unknown testpoint %q", *testpointflag)
257	}
258	final()
259}
260