// 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" "io/ioutil" "log" "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 := ioutil.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 := ioutil.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() }