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 }