github.com/getgauge/gauge@v1.6.9/cmd/run.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 cmd 8 9 import ( 10 "errors" 11 "github.com/getgauge/gauge/config" 12 "github.com/getgauge/gauge/env" 13 "github.com/getgauge/gauge/execution" 14 "github.com/getgauge/gauge/execution/rerun" 15 gauge "github.com/getgauge/gauge/gauge" 16 "github.com/getgauge/gauge/logger" 17 "github.com/getgauge/gauge/plugin/install" 18 "github.com/getgauge/gauge/util" 19 "github.com/spf13/cobra" 20 "github.com/spf13/pflag" 21 "os" 22 "strconv" 23 "strings" 24 ) 25 26 const ( 27 verboseDefault = false 28 simpleConsoleDefault = false 29 failedDefault = false 30 repeatDefault = false 31 parallelDefault = false 32 sortDefault = false 33 installPluginsDefault = true 34 environmentDefault = "default" 35 tagsDefault = "" 36 rowsDefault = "" 37 strategyDefault = "lazy" 38 onlyDefault = "" 39 groupDefault = -1 40 maxRetriesCountDefault = 1 41 retryOnlyTagsDefault = "" 42 failSafeDefault = false 43 skipCommandSaveDefault = false 44 45 verboseName = "verbose" 46 simpleConsoleName = "simple-console" 47 failedName = "failed" 48 repeatName = "repeat" 49 parallelName = "parallel" 50 sortName = "sort" 51 installPluginsName = "install-plugins" 52 environmentName = "env" 53 tagsName = "tags" 54 rowsName = "table-rows" 55 strategyName = "strategy" 56 groupName = "group" 57 maxRetriesCountName = "max-retries-count" 58 retryOnlyTagsName = "retry-only" 59 streamsName = "n" 60 onlyName = "only" 61 failSafeName = "fail-safe" 62 skipCommandSaveName = "skip-save" 63 scenarioName = "scenario" 64 ) 65 66 var overrideRerunFlags = []string{verboseName, simpleConsoleName, machineReadableName, dirName, logLevelName} 67 var streamsDefault = util.NumberOfCores() 68 69 var ( 70 runCmd = &cobra.Command{ 71 Use: "run [flags] [args]", 72 Short: "Run specs", 73 Long: `Run specs.`, 74 Example: ` gauge run specs/ 75 gauge run --tags "login" -s -p specs/`, 76 Run: func(cmd *cobra.Command, args []string) { 77 logger.Debugf(true, "gauge %s %v", cmd.Name(), strings.Join(args, " ")) 78 if err := config.SetProjectRoot(args); err != nil { 79 exit(err, "") 80 } 81 if er := handleConflictingParams(cmd.Flags(), args); er != nil { 82 exit(er, "") 83 } 84 if repeat { 85 repeatLastExecution(cmd) 86 } else if failed { 87 executeFailed(cmd) 88 } else { 89 addFlagsToExecutionArgs(cmd.Flags()) 90 execute(cmd, args) 91 } 92 }, 93 DisableAutoGenTag: true, 94 } 95 verbose bool 96 simpleConsole bool 97 failed bool 98 repeat bool 99 parallel bool 100 sort bool 101 installPlugins bool 102 environment string 103 tags string 104 tagsToFilterForParallelRun string 105 rows string 106 strategy string 107 streams int 108 maxRetriesCount int 109 retryOnlyTags string 110 group int 111 failSafe bool 112 skipCommandSave bool 113 scenarios []string 114 scenarioNameDefault []string 115 ) 116 117 func init() { 118 GaugeCmd.AddCommand(runCmd) 119 f := runCmd.Flags() 120 f.BoolVarP(&verbose, verboseName, "v", verboseDefault, "Enable step level reporting on console, default being scenario level") 121 f.BoolVarP(&simpleConsole, simpleConsoleName, "", simpleConsoleDefault, "Removes colouring and simplifies the console output") 122 f.StringVarP(&environment, environmentName, "e", environmentDefault, "Specifies the environment to use") 123 f.StringVarP(&tags, tagsName, "t", tagsDefault, "Executes the specs and scenarios tagged with given tags") 124 f.StringVarP(&rows, rowsName, "r", rowsDefault, "Executes the specs and scenarios only for the selected rows. It can be specified by range as 2-4 or as list 2,4") 125 f.BoolVarP(¶llel, parallelName, "p", parallelDefault, "Execute specs in parallel") 126 f.IntVarP(&streams, streamsName, "n", streamsDefault, "Specify number of parallel execution streams") 127 f.IntVarP(&maxRetriesCount, maxRetriesCountName, "c", maxRetriesCountDefault, "Max count of iterations for failed scenario") 128 f.StringVarP(&retryOnlyTags, retryOnlyTagsName, "", retryOnlyTagsDefault, "Retries the specs and scenarios tagged with given tags") 129 f.StringVarP(&tagsToFilterForParallelRun, onlyName, "o", onlyDefault, "Execute only the specs and scenarios tagged with given tags in parallel, rest will be run in serial. Applicable only if run in parallel.") 130 err := f.MarkHidden(onlyName) 131 if err != nil { 132 logger.Errorf(false, "Unable to mark '%s' flag as hidden: %s", onlyName, err.Error()) 133 } 134 f.IntVarP(&group, groupName, "g", groupDefault, "Specify which group of specification to execute based on -n flag") 135 f.StringVarP(&strategy, strategyName, "", strategyDefault, "Set the parallelization strategy for execution. Possible options are: `eager`, `lazy`") 136 f.BoolVarP(&sort, sortName, "s", sortDefault, "Run specs in Alphabetical Order") 137 f.BoolVarP(&installPlugins, installPluginsName, "i", installPluginsDefault, "Install All Missing Plugins") 138 f.BoolVarP(&failed, failedName, "f", failedDefault, "Run only the scenarios failed in previous run. This cannot be used in conjunction with any other argument") 139 f.BoolVarP(&repeat, repeatName, "", repeatDefault, "Repeat last run. This cannot be used in conjunction with any other argument") 140 f.BoolVarP(&hideSuggestion, hideSuggestionName, "", hideSuggestionDefault, "Hide step implementation stub for every unimplemented step") 141 f.BoolVarP(&failSafe, failSafeName, "", failSafeDefault, "Force return 0 exit code, even in case of failures.") 142 f.BoolVarP(&skipCommandSave, skipCommandSaveName, "", skipCommandSaveDefault, "Skip saving last command in lastRunCmd.json") 143 err = f.MarkHidden(skipCommandSaveName) 144 if err != nil { 145 logger.Errorf(false, "Unable to mark '%s' flag as hidden: %s", skipCommandSaveName, err.Error()) 146 } 147 148 f.StringArrayVar(&scenarios, scenarioName, scenarioNameDefault, "Set scenarios for running specs with scenario name") 149 } 150 151 func executeFailed(cmd *cobra.Command) { 152 lastState, err := rerun.GetLastFailedState() 153 if err != nil { 154 exit(err, "") 155 } 156 if !skipCommandSave { 157 rerun.WritePrevArgs(os.Args) 158 } 159 handleFlags(cmd, append([]string{"gauge"}, lastState...)) 160 err = cmd.Flags().Set(skipCommandSaveName, "true") 161 if err != nil { 162 logger.Errorf(false, "Unable to set '%s' flag as 'true': %s", skipCommandSaveName, err.Error()) 163 } 164 logger.Debugf(true, "Executing => %s\n", strings.Join(os.Args, " ")) 165 err = cmd.Execute() 166 if err != nil { 167 logger.Errorf(true, "Unable to execute command %s: %s", cmd.Name(), err.Error()) 168 } 169 } 170 171 func handleFlags(cmd *cobra.Command, args []string) { 172 cmd.Flags().Visit(func(flag *pflag.Flag) { 173 if !util.ListContains(overrideRerunFlags, flag.Name) && flag.Changed { 174 err := flag.Value.Set(flag.DefValue) 175 if err != nil { 176 logger.Errorf(false, "Unable to set default value in '%s' flag: %s", flag.Name, err.Error()) 177 } 178 } 179 }) 180 181 for i := 0; i <= len(args)-1; i++ { 182 f := lookupFlagFromArgs(cmd, args[i]) 183 if f == nil { 184 continue 185 } 186 v := f.Value.String() 187 _, err := strconv.ParseBool(v) 188 if err != nil && f.Changed { 189 if strings.Contains(args[i], "=") { 190 args[i] = strings.SplitAfter(args[i], "=")[0] + f.Value.String() 191 } else { 192 args[i+1] = v 193 i = i + 1 194 } 195 } 196 } 197 os.Args = args 198 } 199 200 func lookupFlagFromArgs(cmd *cobra.Command, arg string) *pflag.Flag { 201 fName := strings.Split(strings.TrimLeft(arg, "-"), "=")[0] 202 flags := cmd.Flags() 203 f := flags.Lookup(fName) 204 if f == nil && len(fName) == 1 { 205 f = flags.ShorthandLookup(fName) 206 } 207 return f 208 } 209 210 func addFlagsToExecutionArgs(flags *pflag.FlagSet) { 211 flags.Visit(func(flag *pflag.Flag) { 212 execution.ExecutionArgs = append(execution.ExecutionArgs, &gauge.ExecutionArg{ 213 Name: flag.Name, 214 Value: []string{flag.Value.String()}, 215 }) 216 }) 217 } 218 219 func installMissingPlugins(flag, languageOnly bool) { 220 if flag && os.Getenv("GAUGE_PLUGIN_INSTALL") != "false" { 221 install.AllPlugins(machineReadable, languageOnly) 222 } 223 } 224 225 func execute(cmd *cobra.Command, args []string) { 226 for _, arg := range execution.ExecutionArgs { 227 logger.Debugf(true, "%s : %v", arg.Name, arg.Value) 228 } 229 loadEnvAndReinitLogger(cmd) 230 ensureScreenshotsDir() 231 if parallel && tagsToFilterForParallelRun != "" && !env.AllowFilteredParallelExecution() { 232 logger.Fatal(true, "Filtered parallel execution is a experimental feature. It can be enabled via allow_filtered_parallel_execution property.") 233 } 234 specs := getSpecsDir(args) 235 rerun.SaveState(os.Args[1:], specs) 236 237 if !skipCommandSave { 238 rerun.WritePrevArgs(os.Args) 239 } 240 installMissingPlugins(installPlugins, false) 241 exitCode := execution.ExecuteSpecs(specs) 242 if failSafe && exitCode != execution.ParseFailed { 243 exitCode = 0 244 } 245 os.Exit(exitCode) 246 } 247 248 var repeatLastExecution = func(cmd *cobra.Command) { 249 lastState := rerun.ReadPrevArgs() 250 handleFlags(cmd, lastState) 251 logger.Debugf(true, "Executing => %s\n", strings.Join(lastState, " ")) 252 err := cmd.Execute() 253 if err != nil { 254 logger.Errorf(true, "Unable to execute command %s: %s", cmd.Name(), err.Error()) 255 } 256 } 257 258 func handleConflictingParams(setFlags *pflag.FlagSet, args []string) error { 259 flagDiffCount := 0 260 setFlags.Visit(func(flag *pflag.Flag) { 261 if !util.ListContains(overrideRerunFlags, flag.Name) && flag.DefValue != flag.Value.String() { 262 flagDiffCount++ 263 } 264 }) 265 if repeat && len(args)+flagDiffCount > 1 { 266 return errors.New("Invalid Command. Usage: gauge run --repeat") 267 } 268 if failed && len(args)+flagDiffCount > 1 { 269 return errors.New("Invalid Command. Usage: gauge run --failed") 270 } 271 if !parallel && tagsToFilterForParallelRun != "" { 272 return errors.New("Invalid Command. flag --only can be used only with --parallel") 273 } 274 if maxRetriesCount == 1 && retryOnlyTags != "" { 275 return errors.New("Invalid Command. flag --retry-only can be used only with --max-retry-count") 276 } 277 return nil 278 }