Source file src/runtime/coverage/testdata/harness.go

     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  
     5  package main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"internal/coverage/slicewriter"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime/coverage"
    16  	"strings"
    17  )
    18  
    19  var verbflag = flag.Int("v", 0, "Verbose trace output level")
    20  var testpointflag = flag.String("tp", "", "Testpoint to run")
    21  var outdirflag = flag.String("o", "", "Output dir into which to emit")
    22  
    23  func 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  
    43  func 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  
    53  func 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  
    93  func 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  
   118  func 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  
   141  type failingWriter struct {
   142  	writeCount int
   143  	writeLimit int
   144  	slws       slicewriter.WriteSeeker
   145  }
   146  
   147  func (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  
   156  func (f *failingWriter) Seek(offset int64, whence int) (int64, error) {
   157  	return f.slws.Seek(offset, whence)
   158  }
   159  
   160  func (f *failingWriter) reset(lim int) {
   161  	f.writeCount = 0
   162  	f.writeLimit = lim
   163  	f.slws = slicewriter.WriteSeeker{}
   164  }
   165  
   166  func 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  
   186  func postClear() int {
   187  	return 42
   188  }
   189  
   190  func 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.
   200  func 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  
   211  func 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  
   226  func final() int {
   227  	println("I run last.")
   228  	return 43
   229  }
   230  
   231  func 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  

View as plain text