github.com/lonnblad/godog@v0.7.14-0.20200306004719-1b0cb3259847/fmt_events.go (about)

     1  package godog
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/cucumber/messages-go/v9"
     9  )
    10  
    11  const nanoSec = 1000000
    12  const spec = "0.1.0"
    13  
    14  func init() {
    15  	Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), eventsFunc)
    16  }
    17  
    18  func eventsFunc(suite string, out io.Writer) Formatter {
    19  	formatter := &events{basefmt: newBaseFmt(suite, out)}
    20  
    21  	formatter.event(&struct {
    22  		Event     string `json:"event"`
    23  		Version   string `json:"version"`
    24  		Timestamp int64  `json:"timestamp"`
    25  		Suite     string `json:"suite"`
    26  	}{
    27  		"TestRunStarted",
    28  		spec,
    29  		timeNowFunc().UnixNano() / nanoSec,
    30  		suite,
    31  	})
    32  
    33  	return formatter
    34  }
    35  
    36  type events struct {
    37  	*basefmt
    38  
    39  	// currently running feature path, to be part of id.
    40  	// this is sadly not passed by gherkin nodes.
    41  	// it restricts this formatter to run only in synchronous single
    42  	// threaded execution. Unless running a copy of formatter for each feature
    43  	path         string
    44  	status       stepResultStatus // last step status, before skipped
    45  	outlineSteps int              // number of current outline scenario steps
    46  }
    47  
    48  func (f *events) event(ev interface{}) {
    49  	data, err := json.Marshal(ev)
    50  	if err != nil {
    51  		panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
    52  	}
    53  	fmt.Fprintln(f.out, string(data))
    54  }
    55  
    56  func (f *events) Pickle(pickle *messages.Pickle) {
    57  	f.basefmt.Pickle(pickle)
    58  
    59  	f.event(&struct {
    60  		Event     string `json:"event"`
    61  		Location  string `json:"location"`
    62  		Timestamp int64  `json:"timestamp"`
    63  	}{
    64  		"TestCaseStarted",
    65  		f.scenarioLocation(pickle.AstNodeIds),
    66  		timeNowFunc().UnixNano() / nanoSec,
    67  	})
    68  
    69  	if len(pickle.Steps) == 0 {
    70  		// @TODO: is status undefined or passed? when there are no steps
    71  		// for this scenario
    72  		f.event(&struct {
    73  			Event     string `json:"event"`
    74  			Location  string `json:"location"`
    75  			Timestamp int64  `json:"timestamp"`
    76  			Status    string `json:"status"`
    77  		}{
    78  			"TestCaseFinished",
    79  			f.scenarioLocation(pickle.AstNodeIds),
    80  			timeNowFunc().UnixNano() / nanoSec,
    81  			"undefined",
    82  		})
    83  	}
    84  }
    85  
    86  func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
    87  	f.basefmt.Feature(ft, p, c)
    88  	f.path = p
    89  	f.event(&struct {
    90  		Event    string `json:"event"`
    91  		Location string `json:"location"`
    92  		Source   string `json:"source"`
    93  	}{
    94  		"TestSource",
    95  		fmt.Sprintf("%s:%d", p, ft.Feature.Location.Line),
    96  		string(c),
    97  	})
    98  }
    99  
   100  func (f *events) Summary() {
   101  	// @TODO: determine status
   102  	status := passed
   103  	if len(f.findStepResults(failed)) > 0 {
   104  		status = failed
   105  	} else if len(f.findStepResults(passed)) == 0 {
   106  		if len(f.findStepResults(undefined)) > len(f.findStepResults(pending)) {
   107  			status = undefined
   108  		} else {
   109  			status = pending
   110  		}
   111  	}
   112  
   113  	snips := f.snippets()
   114  	if len(snips) > 0 {
   115  		snips = "You can implement step definitions for undefined steps with these snippets:\n" + snips
   116  	}
   117  
   118  	f.event(&struct {
   119  		Event     string `json:"event"`
   120  		Status    string `json:"status"`
   121  		Timestamp int64  `json:"timestamp"`
   122  		Snippets  string `json:"snippets"`
   123  		Memory    string `json:"memory"`
   124  	}{
   125  		"TestRunFinished",
   126  		status.String(),
   127  		timeNowFunc().UnixNano() / nanoSec,
   128  		snips,
   129  		"", // @TODO not sure that could be correctly implemented
   130  	})
   131  }
   132  
   133  func (f *events) step(res *stepResult) {
   134  	step := f.findStep(res.step.AstNodeIds[0])
   135  
   136  	var errMsg string
   137  	if res.err != nil {
   138  		errMsg = res.err.Error()
   139  	}
   140  	f.event(&struct {
   141  		Event     string `json:"event"`
   142  		Location  string `json:"location"`
   143  		Timestamp int64  `json:"timestamp"`
   144  		Status    string `json:"status"`
   145  		Summary   string `json:"summary,omitempty"`
   146  	}{
   147  		"TestStepFinished",
   148  		fmt.Sprintf("%s:%d", f.path, step.Location.Line),
   149  		timeNowFunc().UnixNano() / nanoSec,
   150  		res.status.String(),
   151  		errMsg,
   152  	})
   153  
   154  	if isLastStep(res.owner, res.step) {
   155  		f.event(&struct {
   156  			Event     string `json:"event"`
   157  			Location  string `json:"location"`
   158  			Timestamp int64  `json:"timestamp"`
   159  			Status    string `json:"status"`
   160  		}{
   161  			"TestCaseFinished",
   162  			f.scenarioLocation(res.owner.AstNodeIds),
   163  			timeNowFunc().UnixNano() / nanoSec,
   164  			f.status.String(),
   165  		})
   166  	}
   167  }
   168  
   169  func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) {
   170  	step := f.findStep(pickleStep.AstNodeIds[0])
   171  
   172  	if def != nil {
   173  		m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:]
   174  		var args [][2]int
   175  		for i := 0; i < len(m)/2; i++ {
   176  			pair := m[i : i*2+2]
   177  			var idxs [2]int
   178  			idxs[0] = pair[0]
   179  			idxs[1] = pair[1]
   180  			args = append(args, idxs)
   181  		}
   182  
   183  		if len(args) == 0 {
   184  			args = make([][2]int, 0)
   185  		}
   186  
   187  		f.event(&struct {
   188  			Event    string   `json:"event"`
   189  			Location string   `json:"location"`
   190  			DefID    string   `json:"definition_id"`
   191  			Args     [][2]int `json:"arguments"`
   192  		}{
   193  			"StepDefinitionFound",
   194  			fmt.Sprintf("%s:%d", f.path, step.Location.Line),
   195  			def.definitionID(),
   196  			args,
   197  		})
   198  	}
   199  
   200  	f.event(&struct {
   201  		Event     string `json:"event"`
   202  		Location  string `json:"location"`
   203  		Timestamp int64  `json:"timestamp"`
   204  	}{
   205  		"TestStepStarted",
   206  		fmt.Sprintf("%s:%d", f.path, step.Location.Line),
   207  		timeNowFunc().UnixNano() / nanoSec,
   208  	})
   209  }
   210  
   211  func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
   212  	f.basefmt.Passed(pickle, step, match)
   213  
   214  	f.status = passed
   215  	f.step(f.lastStepResult())
   216  }
   217  
   218  func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
   219  	f.basefmt.Skipped(pickle, step, match)
   220  
   221  	f.step(f.lastStepResult())
   222  }
   223  
   224  func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
   225  	f.basefmt.Undefined(pickle, step, match)
   226  
   227  	f.status = undefined
   228  	f.step(f.lastStepResult())
   229  }
   230  
   231  func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
   232  	f.basefmt.Failed(pickle, step, match, err)
   233  
   234  	f.status = failed
   235  	f.step(f.lastStepResult())
   236  }
   237  
   238  func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
   239  	f.basefmt.Pending(pickle, step, match)
   240  
   241  	f.status = pending
   242  	f.step(f.lastStepResult())
   243  }
   244  
   245  func (f *events) scenarioLocation(astNodeIds []string) string {
   246  	scenario := f.findScenario(astNodeIds[0])
   247  	line := scenario.Location.Line
   248  	if len(astNodeIds) == 2 {
   249  		_, row := f.findExample(astNodeIds[1])
   250  		line = row.Location.Line
   251  	}
   252  
   253  	return fmt.Sprintf("%s:%d", f.path, line)
   254  }