gitee.com/mirrors/gauge@v1.0.6/reporter/reporter.go (about)

     1  // Copyright 2015 ThoughtWorks, Inc.
     2  
     3  // This file is part of Gauge.
     4  
     5  // Gauge is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  
    10  // Gauge is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  
    15  // You should have received a copy of the GNU General Public License
    16  // along with Gauge.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package reporter
    19  
    20  import (
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"runtime/debug"
    25  
    26  	"sync"
    27  
    28  	"github.com/getgauge/gauge/execution/event"
    29  	"github.com/getgauge/gauge/execution/result"
    30  	"github.com/getgauge/gauge/formatter"
    31  	"github.com/getgauge/gauge/gauge"
    32  	"github.com/getgauge/gauge/gauge_messages"
    33  	"github.com/getgauge/gauge/logger"
    34  )
    35  
    36  // IsParallel represents console reporting format based on simple/parallel execution
    37  var IsParallel bool
    38  
    39  // NumberOfExecutionStreams indicates the total number of parallel execution streams
    40  var NumberOfExecutionStreams int
    41  
    42  // SimpleConsoleOutput represents if coloring should be removed from the Console output
    43  var SimpleConsoleOutput bool
    44  
    45  // Verbose represents level of console Reporting. If true its at step level, else at scenario level.
    46  var Verbose bool
    47  
    48  // MachineReadable represents if output should be in JSON format.
    49  var MachineReadable bool
    50  
    51  const newline = "\n"
    52  
    53  // Reporter reports the progress of spec execution. It reports
    54  // 1. Which spec / scenarion / step (if verbose) is currently executing.
    55  // 2. Status (pass/fail) of the spec / scenario / step (if verbose) once its executed.
    56  type Reporter interface {
    57  	SuiteStart()
    58  	SpecStart(*gauge.Specification, result.Result)
    59  	SpecEnd(*gauge.Specification, result.Result)
    60  	ScenarioStart(*gauge.Scenario, gauge_messages.ExecutionInfo, result.Result)
    61  	ScenarioEnd(*gauge.Scenario, result.Result, gauge_messages.ExecutionInfo)
    62  	StepStart(string)
    63  	StepEnd(gauge.Step, result.Result, gauge_messages.ExecutionInfo)
    64  	ConceptStart(string)
    65  	ConceptEnd(result.Result)
    66  	DataTable(string)
    67  	SuiteEnd(result.Result)
    68  
    69  	Errorf(string, ...interface{})
    70  
    71  	io.Writer
    72  }
    73  
    74  var currentReporter Reporter
    75  
    76  func reporter(e event.ExecutionEvent) Reporter {
    77  	if IsParallel {
    78  		return ParallelReporter(e.Stream)
    79  	}
    80  	return Current()
    81  }
    82  
    83  // Current returns the current instance of Reporter, if present. Else, it returns a new Reporter.
    84  func Current() Reporter {
    85  	if currentReporter == nil {
    86  		if MachineReadable {
    87  			currentReporter = newJSONConsole(os.Stdout, IsParallel, 0)
    88  		} else if SimpleConsoleOutput {
    89  			currentReporter = newSimpleConsole(os.Stdout)
    90  		} else if Verbose {
    91  			currentReporter = newVerboseColoredConsole(os.Stdout)
    92  		} else {
    93  			currentReporter = newColoredConsole(os.Stdout)
    94  		}
    95  	}
    96  	return currentReporter
    97  }
    98  
    99  type parallelReportWriter struct {
   100  	nRunner int
   101  }
   102  
   103  func (p *parallelReportWriter) Write(b []byte) (int, error) {
   104  	return fmt.Printf("[runner: %d] %s", p.nRunner, string(b))
   105  }
   106  
   107  // ParallelReporter returns the instance of parallel console reporter
   108  func ParallelReporter(n int) Reporter {
   109  	if r, ok := parallelReporters[n]; ok {
   110  		return r
   111  	}
   112  	return Current()
   113  }
   114  
   115  var parallelReporters map[int]Reporter
   116  
   117  func initParallelReporters() {
   118  	parallelReporters = make(map[int]Reporter, NumberOfExecutionStreams)
   119  	for i := 1; i <= NumberOfExecutionStreams; i++ {
   120  		if MachineReadable {
   121  			parallelReporters[i] = newJSONConsole(os.Stdout, true, i)
   122  		} else {
   123  			writer := &parallelReportWriter{nRunner: i}
   124  			parallelReporters[i] = newSimpleConsole(writer)
   125  		}
   126  	}
   127  }
   128  
   129  // ListenExecutionEvents listens to all execution events for reporting on console
   130  func ListenExecutionEvents(wg *sync.WaitGroup) {
   131  	ch := make(chan event.ExecutionEvent, 0)
   132  	initParallelReporters()
   133  	event.Register(ch, event.SuiteStart, event.SpecStart, event.SpecEnd, event.ScenarioStart, event.ScenarioEnd, event.StepStart, event.StepEnd, event.ConceptStart, event.ConceptEnd, event.SuiteEnd)
   134  	var r Reporter
   135  	wg.Add(1)
   136  
   137  	go func() {
   138  		defer recoverPanic()
   139  		for {
   140  			e := <-ch
   141  			r = reporter(e)
   142  			switch e.Topic {
   143  			case event.SuiteStart:
   144  				r.SuiteStart()
   145  			case event.SpecStart:
   146  				r.SpecStart(e.Item.(*gauge.Specification), e.Result)
   147  			case event.ScenarioStart:
   148  				skipped := e.Result.(*result.ScenarioResult).ProtoScenario.GetExecutionStatus() == gauge_messages.ExecutionStatus_SKIPPED
   149  				sce := e.Item.(*gauge.Scenario)
   150  				// if it is datatable driven execution
   151  				if !skipped {
   152  					if sce.SpecDataTableRow.GetRowCount() != 0 {
   153  						r.DataTable(formatter.FormatTable(&sce.SpecDataTableRow))
   154  					}
   155  					if sce.ScenarioDataTableRow.GetRowCount() != 0 {
   156  						r.DataTable(formatter.FormatTable(&sce.ScenarioDataTableRow))
   157  					}
   158  				}
   159  				r.ScenarioStart(sce, e.ExecutionInfo, e.Result)
   160  			case event.ConceptStart:
   161  				r.ConceptStart(formatter.FormatStep(e.Item.(*gauge.Step)))
   162  			case event.StepStart:
   163  				r.StepStart(formatter.FormatStepWithResolvedArgs(e.Item.(*gauge.Step)))
   164  			case event.StepEnd:
   165  				r.StepEnd(e.Item.(gauge.Step), e.Result, e.ExecutionInfo)
   166  			case event.ConceptEnd:
   167  				r.ConceptEnd(e.Result)
   168  			case event.ScenarioEnd:
   169  				r.ScenarioEnd(e.Item.(*gauge.Scenario), e.Result, e.ExecutionInfo)
   170  			case event.SpecEnd:
   171  				r.SpecEnd(e.Item.(*gauge.Specification), e.Result)
   172  			case event.SuiteEnd:
   173  				r.SuiteEnd(e.Result)
   174  				wg.Done()
   175  			}
   176  		}
   177  	}()
   178  }
   179  
   180  func recoverPanic() {
   181  	if r := recover(); r != nil {
   182  		logger.Fatalf(true, "%v\n%s", r, string(debug.Stack()))
   183  	}
   184  }