github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/coverage/testsupport.go (about)

     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 coverage
     6  
     7  import (
     8  	"fmt"
     9  	"internal/coverage"
    10  	"internal/coverage/calloc"
    11  	"internal/coverage/cformat"
    12  	"internal/coverage/cmerge"
    13  	"internal/coverage/decodecounter"
    14  	"internal/coverage/decodemeta"
    15  	"internal/coverage/pods"
    16  	"io"
    17  	"os"
    18  )
    19  
    20  // processCoverTestDir is called (via a linknamed reference) from
    21  // testmain code when "go test -cover" is in effect. It is not
    22  // intended to be used other than internally by the Go command's
    23  // generated code.
    24  func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error {
    25  	return processCoverTestDirInternal(dir, cfile, cm, cpkg, os.Stdout)
    26  }
    27  
    28  // processCoverTestDirInternal is an io.Writer version of processCoverTestDir,
    29  // exposed for unit testing.
    30  func processCoverTestDirInternal(dir string, cfile string, cm string, cpkg string, w io.Writer) error {
    31  	cmode := coverage.ParseCounterMode(cm)
    32  	if cmode == coverage.CtrModeInvalid {
    33  		return fmt.Errorf("invalid counter mode %q", cm)
    34  	}
    35  
    36  	// Emit meta-data and counter data.
    37  	ml := getCovMetaList()
    38  	if len(ml) == 0 {
    39  		// This corresponds to the case where we have a package that
    40  		// contains test code but no functions (which is fine). In this
    41  		// case there is no need to emit anything.
    42  	} else {
    43  		if err := emitMetaDataToDirectory(dir, ml); err != nil {
    44  			return err
    45  		}
    46  		if err := emitCounterDataToDirectory(dir); err != nil {
    47  			return err
    48  		}
    49  	}
    50  
    51  	// Collect pods from test run. For the majority of cases we would
    52  	// expect to see a single pod here, but allow for multiple pods in
    53  	// case the test harness is doing extra work to collect data files
    54  	// from builds that it kicks off as part of the testing.
    55  	podlist, err := pods.CollectPods([]string{dir}, false)
    56  	if err != nil {
    57  		return fmt.Errorf("reading from %s: %v", dir, err)
    58  	}
    59  
    60  	// Open text output file if appropriate.
    61  	var tf *os.File
    62  	var tfClosed bool
    63  	if cfile != "" {
    64  		var err error
    65  		tf, err = os.Create(cfile)
    66  		if err != nil {
    67  			return fmt.Errorf("internal error: opening coverage data output file %q: %v", cfile, err)
    68  		}
    69  		defer func() {
    70  			if !tfClosed {
    71  				tfClosed = true
    72  				tf.Close()
    73  			}
    74  		}()
    75  	}
    76  
    77  	// Read/process the pods.
    78  	ts := &tstate{
    79  		cm:    &cmerge.Merger{},
    80  		cf:    cformat.NewFormatter(cmode),
    81  		cmode: cmode,
    82  	}
    83  	for _, p := range podlist {
    84  		if err := ts.processPod(p); err != nil {
    85  			return err
    86  		}
    87  	}
    88  
    89  	// Emit percent.
    90  	if err := ts.cf.EmitPercent(w, cpkg, true); err != nil {
    91  		return err
    92  	}
    93  
    94  	// Emit text output.
    95  	if tf != nil {
    96  		if err := ts.cf.EmitTextual(tf); err != nil {
    97  			return err
    98  		}
    99  		tfClosed = true
   100  		if err := tf.Close(); err != nil {
   101  			return fmt.Errorf("closing %s: %v", cfile, err)
   102  		}
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  type tstate struct {
   109  	calloc.BatchCounterAlloc
   110  	cm    *cmerge.Merger
   111  	cf    *cformat.Formatter
   112  	cmode coverage.CounterMode
   113  }
   114  
   115  // processPod reads coverage counter data for a specific pod.
   116  func (ts *tstate) processPod(p pods.Pod) error {
   117  	// Open meta-data file
   118  	f, err := os.Open(p.MetaFile)
   119  	if err != nil {
   120  		return fmt.Errorf("unable to open meta-data file %s: %v", p.MetaFile, err)
   121  	}
   122  	defer func() {
   123  		f.Close()
   124  	}()
   125  	var mfr *decodemeta.CoverageMetaFileReader
   126  	mfr, err = decodemeta.NewCoverageMetaFileReader(f, nil)
   127  	if err != nil {
   128  		return fmt.Errorf("error reading meta-data file %s: %v", p.MetaFile, err)
   129  	}
   130  	newmode := mfr.CounterMode()
   131  	if newmode != ts.cmode {
   132  		return fmt.Errorf("internal error: counter mode clash: %q from test harness, %q from data file %s", ts.cmode.String(), newmode.String(), p.MetaFile)
   133  	}
   134  	newgran := mfr.CounterGranularity()
   135  	if err := ts.cm.SetModeAndGranularity(p.MetaFile, cmode, newgran); err != nil {
   136  		return err
   137  	}
   138  
   139  	// Read counter data files.
   140  	pmm := make(map[pkfunc][]uint32)
   141  	for _, cdf := range p.CounterDataFiles {
   142  		cf, err := os.Open(cdf)
   143  		if err != nil {
   144  			return fmt.Errorf("opening counter data file %s: %s", cdf, err)
   145  		}
   146  		var cdr *decodecounter.CounterDataReader
   147  		cdr, err = decodecounter.NewCounterDataReader(cdf, cf)
   148  		if err != nil {
   149  			return fmt.Errorf("reading counter data file %s: %s", cdf, err)
   150  		}
   151  		var data decodecounter.FuncPayload
   152  		for {
   153  			ok, err := cdr.NextFunc(&data)
   154  			if err != nil {
   155  				return fmt.Errorf("reading counter data file %s: %v", cdf, err)
   156  			}
   157  			if !ok {
   158  				break
   159  			}
   160  
   161  			// NB: sanity check on pkg and func IDs?
   162  			key := pkfunc{pk: data.PkgIdx, fcn: data.FuncIdx}
   163  			if prev, found := pmm[key]; found {
   164  				// Note: no overflow reporting here.
   165  				if err, _ := ts.cm.MergeCounters(data.Counters, prev); err != nil {
   166  					return fmt.Errorf("processing counter data file %s: %v", cdf, err)
   167  				}
   168  			}
   169  			c := ts.AllocateCounters(len(data.Counters))
   170  			copy(c, data.Counters)
   171  			pmm[key] = c
   172  		}
   173  	}
   174  
   175  	// Visit meta-data file.
   176  	np := uint32(mfr.NumPackages())
   177  	payload := []byte{}
   178  	for pkIdx := uint32(0); pkIdx < np; pkIdx++ {
   179  		var pd *decodemeta.CoverageMetaDataDecoder
   180  		pd, payload, err = mfr.GetPackageDecoder(pkIdx, payload)
   181  		if err != nil {
   182  			return fmt.Errorf("reading pkg %d from meta-file %s: %s", pkIdx, p.MetaFile, err)
   183  		}
   184  		ts.cf.SetPackage(pd.PackagePath())
   185  		var fd coverage.FuncDesc
   186  		nf := pd.NumFuncs()
   187  		for fnIdx := uint32(0); fnIdx < nf; fnIdx++ {
   188  			if err := pd.ReadFunc(fnIdx, &fd); err != nil {
   189  				return fmt.Errorf("reading meta-data file %s: %v",
   190  					p.MetaFile, err)
   191  			}
   192  			key := pkfunc{pk: pkIdx, fcn: fnIdx}
   193  			counters, haveCounters := pmm[key]
   194  			for i := 0; i < len(fd.Units); i++ {
   195  				u := fd.Units[i]
   196  				// Skip units with non-zero parent (no way to represent
   197  				// these in the existing format).
   198  				if u.Parent != 0 {
   199  					continue
   200  				}
   201  				count := uint32(0)
   202  				if haveCounters {
   203  					count = counters[i]
   204  				}
   205  				ts.cf.AddUnit(fd.Srcfile, fd.Funcname, fd.Lit, u, count)
   206  			}
   207  		}
   208  	}
   209  	return nil
   210  }
   211  
   212  type pkfunc struct {
   213  	pk, fcn uint32
   214  }