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(&parallel, 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  }