oss.indeed.com/go/go-opine@v1.3.0/internal/gotest/event.go (about)

     1  package gotest
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"io"
     7  	"regexp"
     8  	"time"
     9  )
    10  
    11  var workaroundGoIssue35169Regexp = regexp.MustCompile(`^FAIL\s+(\S+)\s+\[build failed\]$`)
    12  
    13  // event is a test event printed by "go test -json". See
    14  // "go doc test2json" for more details. This struct was
    15  // copied directly from those docs.
    16  type event struct {
    17  	Time    time.Time // encodes as an RFC3339-format string
    18  	Action  string
    19  	Package string
    20  	Test    string
    21  	Elapsed float64 // seconds
    22  	Output  string
    23  }
    24  
    25  // eventAccepter accepts events created by an eventStreamParser.
    26  type eventAccepter interface {
    27  	Accept(e event) error
    28  }
    29  
    30  // eventConverter converts a single line (without the newline)
    31  // to events.
    32  type eventConverter interface {
    33  	Convert(line []byte) ([]event, error)
    34  }
    35  
    36  // jsonEventConverter converts JSON event lines (as printed by
    37  // "go test -json") into singleton lists with the corresponding
    38  // event.
    39  type jsonEventConverter struct {
    40  }
    41  
    42  var _ eventConverter = jsonEventConverter{}
    43  
    44  func (jsonEventConverter) Convert(line []byte) ([]event, error) {
    45  	var e event
    46  	if err := json.Unmarshal(line, &e); err != nil {
    47  		return nil, err
    48  	}
    49  	return []event{e}, nil
    50  }
    51  
    52  // workaroundGoIssue35169EventConverter first calls the primary
    53  // eventConverter and, if that fails, falls back to converting
    54  // lines like "FAIL	example.com [build failed]" into events.
    55  //
    56  // This is to work around https://github.com/golang/go/issues/35169.
    57  type workaroundGoIssue35169EventConverter struct {
    58  	primary eventConverter
    59  }
    60  
    61  var _ eventConverter = (*workaroundGoIssue35169EventConverter)(nil)
    62  
    63  func (w *workaroundGoIssue35169EventConverter) Convert(line []byte) ([]event, error) {
    64  	events, err := w.primary.Convert(line)
    65  	if err == nil {
    66  		return events, nil
    67  	}
    68  
    69  	match := workaroundGoIssue35169Regexp.FindSubmatch(line)
    70  	if match != nil {
    71  		ts := time.Now()
    72  		pkg := string(match[1])
    73  		events := []event{
    74  			{
    75  				Time:    ts,
    76  				Action:  "output",
    77  				Package: pkg,
    78  				Output:  string(line) + "\n", // bufio.Sanner removes the newline
    79  			},
    80  			{
    81  				Time:    ts,
    82  				Action:  "fail",
    83  				Package: pkg,
    84  				Elapsed: 0,
    85  			},
    86  		}
    87  		return events, nil
    88  	}
    89  
    90  	return nil, err
    91  }
    92  
    93  // eventStreamParser reads "go test -json" output, converts
    94  // each line to an event, and passes each event to the eventAccepter.
    95  type eventStreamParser struct {
    96  	to        eventAccepter
    97  	converter eventConverter
    98  }
    99  
   100  func newEventStreamParser(to eventAccepter) *eventStreamParser {
   101  	return &eventStreamParser{
   102  		to:        to,
   103  		converter: &workaroundGoIssue35169EventConverter{jsonEventConverter{}},
   104  	}
   105  }
   106  
   107  // Parse "go test -json" output into events and pass them to the
   108  // eventAccepter.
   109  //
   110  // If any line is not JSON, or if the eventAccepter returns an
   111  // error then Parse will stop immediately and return the error.
   112  func (esp *eventStreamParser) Parse(r io.Reader) error {
   113  	scanner := bufio.NewScanner(r)
   114  	for scanner.Scan() {
   115  		events, err := esp.converter.Convert(scanner.Bytes())
   116  		if err != nil {
   117  			return err
   118  		}
   119  		for _, e := range events {
   120  			if err := esp.to.Accept(e); err != nil {
   121  				return err
   122  			}
   123  		}
   124  	}
   125  	return scanner.Err()
   126  }