blob: 03969da426b4b4c86d1e70515efadf2b947d1b2f [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"flag"
"fmt"
"internal/coverage/slicewriter"
"io"
"log"
"os"
"path/filepath"
"runtime/coverage"
"strings"
)
var verbflag = flag.Int("v", 0, "Verbose trace output level")
var testpointflag = flag.String("tp", "", "Testpoint to run")
var outdirflag = flag.String("o", "", "Output dir into which to emit")
func emitToWriter() {
log.SetPrefix("emitToWriter: ")
var slwm slicewriter.WriteSeeker
if err := coverage.WriteMeta(&slwm); err != nil {
log.Fatalf("error: WriteMeta returns %v", err)
}
mf := filepath.Join(*outdirflag, "covmeta.0abcdef")
if err := os.WriteFile(mf, slwm.BytesWritten(), 0666); err != nil {
log.Fatalf("error: writing %s: %v", mf, err)
}
var slwc slicewriter.WriteSeeker
if err := coverage.WriteCounters(&slwc); err != nil {
log.Fatalf("error: WriteCounters returns %v", err)
}
cf := filepath.Join(*outdirflag, "covcounters.0abcdef.99.77")
if err := os.WriteFile(cf, slwc.BytesWritten(), 0666); err != nil {
log.Fatalf("error: writing %s: %v", cf, err)
}
}
func emitToDir() {
log.SetPrefix("emitToDir: ")
if err := coverage.WriteMetaDir(*outdirflag); err != nil {
log.Fatalf("error: WriteMetaDir returns %v", err)
}
if err := coverage.WriteCountersDir(*outdirflag); err != nil {
log.Fatalf("error: WriteCountersDir returns %v", err)
}
}
func emitToNonexistentDir() {
log.SetPrefix("emitToNonexistentDir: ")
want := []string{
"no such file or directory", // linux-ish
"system cannot find the file specified", // windows
"does not exist", // plan9
}
checkWant := func(which string, got string) {
found := false
for _, w := range want {
if strings.Contains(got, w) {
found = true
break
}
}
if !found {
log.Fatalf("%s emit to bad dir: got error:\n %v\nwanted error with one of:\n %+v", which, got, want)
}
}
// Mangle the output directory to produce something nonexistent.
mangled := *outdirflag + "_MANGLED"
if err := coverage.WriteMetaDir(mangled); err == nil {
log.Fatal("expected error from WriteMetaDir to nonexistent dir")
} else {
got := fmt.Sprintf("%v", err)
checkWant("meta data", got)
}
// Now try to emit counter data file to a bad dir.
if err := coverage.WriteCountersDir(mangled); err == nil {
log.Fatal("expected error emitting counter data to bad dir")
} else {
got := fmt.Sprintf("%v", err)
checkWant("counter data", got)
}
}
func emitToUnwritableDir() {
log.SetPrefix("emitToUnwritableDir: ")
want := "permission denied"
if err := coverage.WriteMetaDir(*outdirflag); err == nil {
log.Fatal("expected error from WriteMetaDir to unwritable dir")
} else {
got := fmt.Sprintf("%v", err)
if !strings.Contains(got, want) {
log.Fatalf("meta-data emit to unwritable dir: wanted error containing %q got %q", want, got)
}
}
// Similarly with writing counter data.
if err := coverage.WriteCountersDir(*outdirflag); err == nil {
log.Fatal("expected error emitting counter data to unwritable dir")
} else {
got := fmt.Sprintf("%v", err)
if !strings.Contains(got, want) {
log.Fatalf("emitting counter data to unwritable dir: wanted error containing %q got %q", want, got)
}
}
}
func emitToNilWriter() {
log.SetPrefix("emitToWriter: ")
want := "nil writer"
var bad io.WriteSeeker
if err := coverage.WriteMeta(bad); err == nil {
log.Fatal("expected error passing nil writer for meta emit")
} else {
got := fmt.Sprintf("%v", err)
if !strings.Contains(got, want) {
log.Fatalf("emitting meta-data passing nil writer: wanted error containing %q got %q", want, got)
}
}
if err := coverage.WriteCounters(bad); err == nil {
log.Fatal("expected error passing nil writer for counter emit")
} else {
got := fmt.Sprintf("%v", err)
if !strings.Contains(got, want) {
log.Fatalf("emitting counter data passing nil writer: wanted error containing %q got %q", want, got)
}
}
}
type failingWriter struct {
writeCount int
writeLimit int
slws slicewriter.WriteSeeker
}
func (f *failingWriter) Write(p []byte) (n int, err error) {
c := f.writeCount
f.writeCount++
if f.writeLimit < 0 || c < f.writeLimit {
return f.slws.Write(p)
}
return 0, fmt.Errorf("manufactured write error")
}
func (f *failingWriter) Seek(offset int64, whence int) (int64, error) {
return f.slws.Seek(offset, whence)
}
func (f *failingWriter) reset(lim int) {
f.writeCount = 0
f.writeLimit = lim
f.slws = slicewriter.WriteSeeker{}
}
func writeStressTest(tag string, testf func(testf *failingWriter) error) {
// Invoke the function initially without the write limit
// set, to capture the number of writes performed.
fw := &failingWriter{writeLimit: -1}
testf(fw)
// Now that we know how many writes are going to happen, run the
// function repeatedly, each time with a Write operation set to
// fail at a new spot. The goal here is to make sure that:
// A) an error is reported, and B) nothing crashes.
tot := fw.writeCount
for i := 0; i < tot; i++ {
fw.reset(i)
err := testf(fw)
if err == nil {
log.Fatalf("no error from write %d tag %s", i, tag)
}
}
}
func postClear() int {
return 42
}
func preClear() int {
return 42
}
// This test is designed to ensure that write errors are properly
// handled by the code that writes out coverage data. It repeatedly
// invokes the 'emit to writer' apis using a specially crafted writer
// that captures the total number of expected writes, then replays the
// execution N times with a manufactured write error at the
// appropriate spot.
func emitToFailingWriter() {
log.SetPrefix("emitToFailingWriter: ")
writeStressTest("emit-meta", func(f *failingWriter) error {
return coverage.WriteMeta(f)
})
writeStressTest("emit-counter", func(f *failingWriter) error {
return coverage.WriteCounters(f)
})
}
func emitWithCounterClear() {
log.SetPrefix("emitWitCounterClear: ")
preClear()
if err := coverage.ClearCounters(); err != nil {
log.Fatalf("clear failed: %v", err)
}
postClear()
if err := coverage.WriteMetaDir(*outdirflag); err != nil {
log.Fatalf("error: WriteMetaDir returns %v", err)
}
if err := coverage.WriteCountersDir(*outdirflag); err != nil {
log.Fatalf("error: WriteCountersDir returns %v", err)
}
}
func final() int {
println("I run last.")
return 43
}
func main() {
log.SetFlags(0)
flag.Parse()
if *testpointflag == "" {
log.Fatalf("error: no testpoint (use -tp flag)")
}
if *outdirflag == "" {
log.Fatalf("error: no output dir specified (use -o flag)")
}
switch *testpointflag {
case "emitToDir":
emitToDir()
case "emitToWriter":
emitToWriter()
case "emitToNonexistentDir":
emitToNonexistentDir()
case "emitToUnwritableDir":
emitToUnwritableDir()
case "emitToNilWriter":
emitToNilWriter()
case "emitToFailingWriter":
emitToFailingWriter()
case "emitWithCounterClear":
emitWithCounterClear()
default:
log.Fatalf("error: unknown testpoint %q", *testpointflag)
}
final()
}