github.com/getgauge/gauge@v1.6.9/execution/simpleExecution.go (about)

     1  /*----------------------------------------------------------------
     2   *  Copyright (c) ThoughtWorks, Inc.
     3   *  Licensed under the Apache License, Version 2.0
     4   *  See LICENSE in the project root for license information.
     5   *----------------------------------------------------------------*/
     6  
     7  package execution
     8  
     9  import (
    10  	"fmt"
    11  	"path/filepath"
    12  	"time"
    13  
    14  	"github.com/getgauge/gauge-proto/go/gauge_messages"
    15  	"github.com/getgauge/gauge/config"
    16  	"github.com/getgauge/gauge/execution/event"
    17  	"github.com/getgauge/gauge/execution/result"
    18  	"github.com/getgauge/gauge/gauge"
    19  	"github.com/getgauge/gauge/logger"
    20  	"github.com/getgauge/gauge/manifest"
    21  	"github.com/getgauge/gauge/plugin"
    22  	"github.com/getgauge/gauge/runner"
    23  )
    24  
    25  // ExecuteTags holds the tags to filter the execution by
    26  var ExecuteTags = ""
    27  var tableRowsIndexes []int
    28  
    29  // SetTableRows is used to limit data driven execution to specific rows
    30  func SetTableRows(tableRows string) {
    31  	tableRowsIndexes = getDataTableRows(tableRows)
    32  }
    33  
    34  type simpleExecution struct {
    35  	manifest             *manifest.Manifest
    36  	runner               runner.Runner
    37  	specCollection       *gauge.SpecCollection
    38  	pluginHandler        plugin.Handler
    39  	currentExecutionInfo *gauge_messages.ExecutionInfo
    40  	suiteResult          *result.SuiteResult
    41  	errMaps              *gauge.BuildErrors
    42  	startTime            time.Time
    43  	stream               int
    44  	skipSuiteEvents      bool
    45  }
    46  
    47  func newSimpleExecution(executionInfo *executionInfo, combineDataTableSpecs, skipSuiteEvents bool) *simpleExecution {
    48  	if combineDataTableSpecs {
    49  		executionInfo.specs = gauge.NewSpecCollection(executionInfo.specs.Specs(), true)
    50  	}
    51  	ei := &gauge_messages.ExecutionInfo{
    52  		ProjectName:              filepath.Base(config.ProjectRoot),
    53  		NumberOfExecutionStreams: int32(NumberOfExecutionStreams),
    54  		RunnerId:                 int32(executionInfo.stream),
    55  		ExecutionArgs:            gauge.ConvertToProtoExecutionArg(ExecutionArgs),
    56  	}
    57  
    58  	return &simpleExecution{
    59  		manifest:             executionInfo.manifest,
    60  		specCollection:       executionInfo.specs,
    61  		runner:               executionInfo.runner,
    62  		pluginHandler:        executionInfo.pluginHandler,
    63  		errMaps:              executionInfo.errMaps,
    64  		stream:               executionInfo.stream,
    65  		skipSuiteEvents:      skipSuiteEvents,
    66  		currentExecutionInfo: ei,
    67  	}
    68  }
    69  
    70  func (e *simpleExecution) run() *result.SuiteResult {
    71  	e.start()
    72  	e.execute()
    73  	e.finish()
    74  	return e.suiteResult
    75  }
    76  
    77  func (e *simpleExecution) execute() {
    78  	e.suiteResult = result.NewSuiteResult(ExecuteTags, e.startTime)
    79  	defer func() {
    80  		e.suiteResult.UpdateExecTime(e.startTime)
    81  		e.suiteResult.SetSpecsSkippedCount()
    82  	}()
    83  	if !e.skipSuiteEvents {
    84  		logger.Debug(true, "Initialising suite data store.")
    85  		initSuiteDataStoreResult := e.initSuiteDataStore()
    86  		if initSuiteDataStoreResult.GetFailed() {
    87  			e.suiteResult.AddUnhandledError(fmt.Errorf("failed to initialize suite datastore. Error: %s", initSuiteDataStoreResult.GetErrorMessage()))
    88  			return
    89  		}
    90  		e.notifyBeforeSuite()
    91  	}
    92  
    93  	if !e.suiteResult.GetFailed() {
    94  		results := e.executeSpecs(e.specCollection)
    95  		e.suiteResult.AddSpecResults(results)
    96  	}
    97  
    98  	if !e.skipSuiteEvents {
    99  		e.notifyAfterSuite()
   100  	}
   101  }
   102  
   103  func (e *simpleExecution) start() {
   104  	e.startTime = time.Now()
   105  	event.Notify(event.NewExecutionEvent(event.SuiteStart, nil, nil, 0, &gauge_messages.ExecutionInfo{}))
   106  	e.pluginHandler = plugin.StartPlugins(e.manifest)
   107  }
   108  
   109  func (e *simpleExecution) finish() {
   110  	e.suiteResult = mergeDataTableSpecResults(e.suiteResult)
   111  	event.Notify(event.NewExecutionEvent(event.SuiteEnd, nil, e.suiteResult, 0, &gauge_messages.ExecutionInfo{}))
   112  	e.notifyExecutionResult()
   113  	e.stopAllPlugins()
   114  }
   115  
   116  func (e *simpleExecution) stopAllPlugins() {
   117  	e.notifyExecutionStop()
   118  	if err := e.runner.Kill(); err != nil {
   119  		logger.Errorf(true, "Failed to kill Runner: %s", err.Error())
   120  	}
   121  }
   122  
   123  func (e *simpleExecution) executeSpecs(sc *gauge.SpecCollection) (results []*result.SpecResult) {
   124  	for sc.HasNext() {
   125  		specs := sc.Next()
   126  		var preHookFailures, postHookFailures []*gauge_messages.ProtoHookFailure
   127  		var specResults []*result.SpecResult
   128  		var before, after = true, false
   129  		for i, spec := range specs {
   130  			if i == len(specs)-1 {
   131  				after = true
   132  			}
   133  			res := newSpecExecutor(spec, e.runner, e.pluginHandler, e.errMaps, e.stream).execute(before, preHookFailures == nil, after)
   134  			before = false
   135  			specResults = append(specResults, res)
   136  			preHookFailures = append(preHookFailures, res.GetPreHook()...)
   137  			postHookFailures = append(postHookFailures, res.GetPostHook()...)
   138  			res.ProtoSpec.PreHookFailures, res.ProtoSpec.PostHookFailures = []*gauge_messages.ProtoHookFailure{}, []*gauge_messages.ProtoHookFailure{}
   139  		}
   140  		for _, res := range specResults {
   141  			for _, preHook := range preHookFailures {
   142  				res.AddPreHook(&gauge_messages.ProtoHookFailure{
   143  					StackTrace:            preHook.StackTrace,
   144  					ErrorMessage:          preHook.ErrorMessage,
   145  					FailureScreenshotFile: preHook.FailureScreenshotFile,
   146  					TableRowIndex:         preHook.TableRowIndex,
   147  				})
   148  			}
   149  			for _, postHook := range postHookFailures {
   150  				res.AddPostHook(&gauge_messages.ProtoHookFailure{
   151  					StackTrace:            postHook.StackTrace,
   152  					ErrorMessage:          postHook.ErrorMessage,
   153  					FailureScreenshotFile: postHook.FailureScreenshotFile,
   154  					TableRowIndex:         postHook.TableRowIndex,
   155  				})
   156  			}
   157  			results = append(results, res)
   158  		}
   159  	}
   160  	return results
   161  }
   162  
   163  func (e *simpleExecution) notifyBeforeSuite() {
   164  	m := &gauge_messages.Message{MessageType: gauge_messages.Message_ExecutionStarting,
   165  		ExecutionStartingRequest: &gauge_messages.ExecutionStartingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
   166  	res := e.executeHook(m)
   167  	e.suiteResult.PreHookMessages = res.Message
   168  	e.suiteResult.PreHookScreenshotFiles = res.ScreenshotFiles
   169  	if res.GetFailed() {
   170  		handleHookFailure(e.suiteResult, res, result.AddPreHook)
   171  	}
   172  	m.ExecutionStartingRequest.SuiteResult = gauge.ConvertToProtoSuiteResult(e.suiteResult)
   173  	e.pluginHandler.NotifyPlugins(m)
   174  }
   175  
   176  func (e *simpleExecution) notifyAfterSuite() {
   177  	m := &gauge_messages.Message{MessageType: gauge_messages.Message_ExecutionEnding,
   178  		ExecutionEndingRequest: &gauge_messages.ExecutionEndingRequest{CurrentExecutionInfo: e.currentExecutionInfo, Stream: int32(e.stream)}}
   179  	res := e.executeHook(m)
   180  	e.suiteResult.PostHookMessages = res.Message
   181  	e.suiteResult.PostHookScreenshotFiles = res.ScreenshotFiles
   182  	if res.GetFailed() {
   183  		handleHookFailure(e.suiteResult, res, result.AddPostHook)
   184  	}
   185  	m.ExecutionEndingRequest.SuiteResult = gauge.ConvertToProtoSuiteResult(e.suiteResult)
   186  	e.pluginHandler.NotifyPlugins(m)
   187  }
   188  
   189  func (e *simpleExecution) initSuiteDataStore() *(gauge_messages.ProtoExecutionResult) {
   190  	m := &gauge_messages.Message{MessageType: gauge_messages.Message_SuiteDataStoreInit,
   191  		SuiteDataStoreInitRequest: &gauge_messages.SuiteDataStoreInitRequest{Stream: int32(e.stream)}}
   192  	return e.runner.ExecuteAndGetStatus(m)
   193  }
   194  
   195  func (e *simpleExecution) executeHook(m *gauge_messages.Message) *(gauge_messages.ProtoExecutionResult) {
   196  	e.pluginHandler.NotifyPlugins(m)
   197  	return e.runner.ExecuteAndGetStatus(m)
   198  }
   199  
   200  func (e *simpleExecution) notifyExecutionResult() {
   201  	m := &gauge_messages.Message{MessageType: gauge_messages.Message_SuiteExecutionResult,
   202  		SuiteExecutionResult: &gauge_messages.SuiteExecutionResult{SuiteResult: gauge.ConvertToProtoSuiteResult(e.suiteResult)}}
   203  	e.pluginHandler.NotifyPlugins(m)
   204  }
   205  
   206  func (e *simpleExecution) notifyExecutionStop() {
   207  	m := &gauge_messages.Message{MessageType: gauge_messages.Message_KillProcessRequest,
   208  		KillProcessRequest: &gauge_messages.KillProcessRequest{}}
   209  	e.pluginHandler.NotifyPlugins(m)
   210  	e.pluginHandler.GracefullyKillPlugins()
   211  }
   212  
   213  func handleHookFailure(hookResult result.Result, execResult *gauge_messages.ProtoExecutionResult, f func(result.Result, *gauge_messages.ProtoExecutionResult)) {
   214  	f(hookResult, execResult)
   215  }