...

Source file src/github.com/onsi/ginkgo/v2/types/config.go

Documentation: github.com/onsi/ginkgo/v2/types

     1  /*
     2  Ginkgo accepts a number of configuration options.
     3  These are documented [here](http://onsi.github.io/ginkgo/#the-ginkgo-cli)
     4  */
     5  
     6  package types
     7  
     8  import (
     9  	"flag"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  // Configuration controlling how an individual test suite is run
    19  type SuiteConfig struct {
    20  	RandomSeed            int64
    21  	RandomizeAllSpecs     bool
    22  	FocusStrings          []string
    23  	SkipStrings           []string
    24  	FocusFiles            []string
    25  	SkipFiles             []string
    26  	LabelFilter           string
    27  	FailOnPending         bool
    28  	FailFast              bool
    29  	FlakeAttempts         int
    30  	MustPassRepeatedly    int
    31  	DryRun                bool
    32  	PollProgressAfter     time.Duration
    33  	PollProgressInterval  time.Duration
    34  	Timeout               time.Duration
    35  	EmitSpecProgress      bool // this is deprecated but its removal is causing compile issue for some users that were setting it manually
    36  	OutputInterceptorMode string
    37  	SourceRoots           []string
    38  	GracePeriod           time.Duration
    39  
    40  	ParallelProcess int
    41  	ParallelTotal   int
    42  	ParallelHost    string
    43  }
    44  
    45  func NewDefaultSuiteConfig() SuiteConfig {
    46  	return SuiteConfig{
    47  		RandomSeed:      time.Now().Unix(),
    48  		Timeout:         time.Hour,
    49  		ParallelProcess: 1,
    50  		ParallelTotal:   1,
    51  		GracePeriod:     30 * time.Second,
    52  	}
    53  }
    54  
    55  type VerbosityLevel uint
    56  
    57  const (
    58  	VerbosityLevelSuccinct VerbosityLevel = iota
    59  	VerbosityLevelNormal
    60  	VerbosityLevelVerbose
    61  	VerbosityLevelVeryVerbose
    62  )
    63  
    64  func (vl VerbosityLevel) GT(comp VerbosityLevel) bool {
    65  	return vl > comp
    66  }
    67  
    68  func (vl VerbosityLevel) GTE(comp VerbosityLevel) bool {
    69  	return vl >= comp
    70  }
    71  
    72  func (vl VerbosityLevel) Is(comp VerbosityLevel) bool {
    73  	return vl == comp
    74  }
    75  
    76  func (vl VerbosityLevel) LTE(comp VerbosityLevel) bool {
    77  	return vl <= comp
    78  }
    79  
    80  func (vl VerbosityLevel) LT(comp VerbosityLevel) bool {
    81  	return vl < comp
    82  }
    83  
    84  // Configuration for Ginkgo's reporter
    85  type ReporterConfig struct {
    86  	NoColor        bool
    87  	Succinct       bool
    88  	Verbose        bool
    89  	VeryVerbose    bool
    90  	FullTrace      bool
    91  	ShowNodeEvents bool
    92  	GithubOutput   bool
    93  
    94  	JSONReport     string
    95  	JUnitReport    string
    96  	TeamcityReport string
    97  }
    98  
    99  func (rc ReporterConfig) Verbosity() VerbosityLevel {
   100  	if rc.Succinct {
   101  		return VerbosityLevelSuccinct
   102  	} else if rc.Verbose {
   103  		return VerbosityLevelVerbose
   104  	} else if rc.VeryVerbose {
   105  		return VerbosityLevelVeryVerbose
   106  	}
   107  	return VerbosityLevelNormal
   108  }
   109  
   110  func (rc ReporterConfig) WillGenerateReport() bool {
   111  	return rc.JSONReport != "" || rc.JUnitReport != "" || rc.TeamcityReport != ""
   112  }
   113  
   114  func NewDefaultReporterConfig() ReporterConfig {
   115  	return ReporterConfig{}
   116  }
   117  
   118  // Configuration for the Ginkgo CLI
   119  type CLIConfig struct {
   120  	//for build, run, and watch
   121  	Recurse      bool
   122  	SkipPackage  string
   123  	RequireSuite bool
   124  	NumCompilers int
   125  
   126  	//for run and watch only
   127  	Procs                     int
   128  	Parallel                  bool
   129  	AfterRunHook              string
   130  	OutputDir                 string
   131  	KeepSeparateCoverprofiles bool
   132  	KeepSeparateReports       bool
   133  
   134  	//for run only
   135  	KeepGoing       bool
   136  	UntilItFails    bool
   137  	Repeat          int
   138  	RandomizeSuites bool
   139  
   140  	//for watch only
   141  	Depth       int
   142  	WatchRegExp string
   143  }
   144  
   145  func NewDefaultCLIConfig() CLIConfig {
   146  	return CLIConfig{
   147  		Depth:       1,
   148  		WatchRegExp: `\.go$`,
   149  	}
   150  }
   151  
   152  func (g CLIConfig) ComputedProcs() int {
   153  	if g.Procs > 0 {
   154  		return g.Procs
   155  	}
   156  
   157  	n := 1
   158  	if g.Parallel {
   159  		n = runtime.NumCPU()
   160  		if n > 4 {
   161  			n = n - 1
   162  		}
   163  	}
   164  	return n
   165  }
   166  
   167  func (g CLIConfig) ComputedNumCompilers() int {
   168  	if g.NumCompilers > 0 {
   169  		return g.NumCompilers
   170  	}
   171  
   172  	return runtime.NumCPU()
   173  }
   174  
   175  // Configuration for the Ginkgo CLI capturing available go flags
   176  // A subset of Go flags are exposed by Ginkgo.  Some are available at compile time (e.g. ginkgo build) and others only at run time (e.g. ginkgo run - which has both build and run time flags).
   177  // More details can be found at:
   178  // https://docs.google.com/spreadsheets/d/1zkp-DS4hU4sAJl5eHh1UmgwxCPQhf3s5a8fbiOI8tJU/
   179  type GoFlagsConfig struct {
   180  	//build-time flags for code-and-performance analysis
   181  	Race      bool
   182  	Cover     bool
   183  	CoverMode string
   184  	CoverPkg  string
   185  	Vet       string
   186  
   187  	//run-time flags for code-and-performance analysis
   188  	BlockProfile         string
   189  	BlockProfileRate     int
   190  	CoverProfile         string
   191  	CPUProfile           string
   192  	MemProfile           string
   193  	MemProfileRate       int
   194  	MutexProfile         string
   195  	MutexProfileFraction int
   196  	Trace                string
   197  
   198  	//build-time flags for building
   199  	A             bool
   200  	ASMFlags      string
   201  	BuildMode     string
   202  	Compiler      string
   203  	GCCGoFlags    string
   204  	GCFlags       string
   205  	InstallSuffix string
   206  	LDFlags       string
   207  	LinkShared    bool
   208  	Mod           string
   209  	N             bool
   210  	ModFile       string
   211  	ModCacheRW    bool
   212  	MSan          bool
   213  	PkgDir        string
   214  	Tags          string
   215  	TrimPath      bool
   216  	ToolExec      string
   217  	Work          bool
   218  	X             bool
   219  }
   220  
   221  func NewDefaultGoFlagsConfig() GoFlagsConfig {
   222  	return GoFlagsConfig{}
   223  }
   224  
   225  func (g GoFlagsConfig) BinaryMustBePreserved() bool {
   226  	return g.BlockProfile != "" || g.CPUProfile != "" || g.MemProfile != "" || g.MutexProfile != ""
   227  }
   228  
   229  // Configuration that were deprecated in 2.0
   230  type deprecatedConfig struct {
   231  	DebugParallel                   bool
   232  	NoisySkippings                  bool
   233  	NoisyPendings                   bool
   234  	RegexScansFilePath              bool
   235  	SlowSpecThresholdWithFLoatUnits float64
   236  	Stream                          bool
   237  	Notify                          bool
   238  	EmitSpecProgress                bool
   239  	SlowSpecThreshold               time.Duration
   240  	AlwaysEmitGinkgoWriter          bool
   241  }
   242  
   243  // Flags
   244  
   245  // Flags sections used by both the CLI and the Ginkgo test process
   246  var FlagSections = GinkgoFlagSections{
   247  	{Key: "multiple-suites", Style: "{{dark-green}}", Heading: "Running Multiple Test Suites"},
   248  	{Key: "order", Style: "{{green}}", Heading: "Controlling Test Order"},
   249  	{Key: "parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism"},
   250  	{Key: "low-level-parallel", Style: "{{yellow}}", Heading: "Controlling Test Parallelism",
   251  		Description: "These are set by the Ginkgo CLI, {{red}}{{bold}}do not set them manually{{/}} via go test.\nUse ginkgo -p or ginkgo -procs=N instead."},
   252  	{Key: "filter", Style: "{{cyan}}", Heading: "Filtering Tests"},
   253  	{Key: "failure", Style: "{{red}}", Heading: "Failure Handling"},
   254  	{Key: "output", Style: "{{magenta}}", Heading: "Controlling Output Formatting"},
   255  	{Key: "code-and-coverage-analysis", Style: "{{orange}}", Heading: "Code and Coverage Analysis"},
   256  	{Key: "performance-analysis", Style: "{{coral}}", Heading: "Performance Analysis"},
   257  	{Key: "debug", Style: "{{blue}}", Heading: "Debugging Tests",
   258  		Description: "In addition to these flags, Ginkgo supports a few debugging environment variables.  To change the parallel server protocol set {{blue}}GINKGO_PARALLEL_PROTOCOL{{/}} to {{bold}}HTTP{{/}}.  To avoid pruning callstacks set {{blue}}GINKGO_PRUNE_STACK{{/}} to {{bold}}FALSE{{/}}."},
   259  	{Key: "watch", Style: "{{light-yellow}}", Heading: "Controlling Ginkgo Watch"},
   260  	{Key: "misc", Style: "{{light-gray}}", Heading: "Miscellaneous"},
   261  	{Key: "go-build", Style: "{{light-gray}}", Heading: "Go Build Flags", Succinct: true,
   262  		Description: "These flags are inherited from go build.  Run {{bold}}ginkgo help build{{/}} for more detailed flag documentation."},
   263  }
   264  
   265  // SuiteConfigFlags provides flags for the Ginkgo test process, and CLI
   266  var SuiteConfigFlags = GinkgoFlags{
   267  	{KeyPath: "S.RandomSeed", Name: "seed", SectionKey: "order", UsageDefaultValue: "randomly generated by Ginkgo",
   268  		Usage: "The seed used to randomize the spec suite.", AlwaysExport: true},
   269  	{KeyPath: "S.RandomizeAllSpecs", Name: "randomize-all", SectionKey: "order", DeprecatedName: "randomizeAllSpecs", DeprecatedDocLink: "changed-command-line-flags",
   270  		Usage: "If set, ginkgo will randomize all specs together.  By default, ginkgo only randomizes the top level Describe, Context and When containers."},
   271  
   272  	{KeyPath: "S.FailOnPending", Name: "fail-on-pending", SectionKey: "failure", DeprecatedName: "failOnPending", DeprecatedDocLink: "changed-command-line-flags",
   273  		Usage: "If set, ginkgo will mark the test suite as failed if any specs are pending."},
   274  	{KeyPath: "S.FailFast", Name: "fail-fast", SectionKey: "failure", DeprecatedName: "failFast", DeprecatedDocLink: "changed-command-line-flags",
   275  		Usage: "If set, ginkgo will stop running a test suite after a failure occurs."},
   276  	{KeyPath: "S.FlakeAttempts", Name: "flake-attempts", SectionKey: "failure", UsageDefaultValue: "0 - failed tests are not retried", DeprecatedName: "flakeAttempts", DeprecatedDocLink: "changed-command-line-flags",
   277  		Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."},
   278  
   279  	{KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags",
   280  		Usage: "If set, ginkgo will walk the test hierarchy without actually running anything.  Best paired with -v."},
   281  	{KeyPath: "S.PollProgressAfter", Name: "poll-progress-after", SectionKey: "debug", UsageDefaultValue: "0",
   282  		Usage: "Emit node progress reports periodically if node hasn't completed after this duration."},
   283  	{KeyPath: "S.PollProgressInterval", Name: "poll-progress-interval", SectionKey: "debug", UsageDefaultValue: "10s",
   284  		Usage: "The rate at which to emit node progress reports after poll-progress-after has elapsed."},
   285  	{KeyPath: "S.SourceRoots", Name: "source-root", SectionKey: "debug",
   286  		Usage: "The location to look for source code when generating progress reports.  You can pass multiple --source-root flags."},
   287  	{KeyPath: "S.Timeout", Name: "timeout", SectionKey: "debug", UsageDefaultValue: "1h",
   288  		Usage: "Test suite fails if it does not complete within the specified timeout."},
   289  	{KeyPath: "S.GracePeriod", Name: "grace-period", SectionKey: "debug", UsageDefaultValue: "30s",
   290  		Usage: "When interrupted, Ginkgo will wait for GracePeriod for the current running node to exit before moving on to the next one."},
   291  	{KeyPath: "S.OutputInterceptorMode", Name: "output-interceptor-mode", SectionKey: "debug", UsageArgument: "dup, swap, or none",
   292  		Usage: "If set, ginkgo will use the specified output interception strategy when running in parallel.  Defaults to dup on unix and swap on windows."},
   293  
   294  	{KeyPath: "S.LabelFilter", Name: "label-filter", SectionKey: "filter", UsageArgument: "expression",
   295  		Usage: "If set, ginkgo will only run specs with labels that match the label-filter.  The passed-in expression can include boolean operations (!, &&, ||, ','), groupings via '()', and regular expressions '/regexp/'.  e.g. '(cat || dog) && !fruit'"},
   296  	{KeyPath: "S.FocusStrings", Name: "focus", SectionKey: "filter",
   297  		Usage: "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed."},
   298  	{KeyPath: "S.SkipStrings", Name: "skip", SectionKey: "filter",
   299  		Usage: "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed."},
   300  	{KeyPath: "S.FocusFiles", Name: "focus-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line",
   301  		Usage: "If set, ginkgo will only run specs in matching files. Can be specified multiple times, values are ORed."},
   302  	{KeyPath: "S.SkipFiles", Name: "skip-file", SectionKey: "filter", UsageArgument: "file (regexp) | file:line | file:lineA-lineB | file:line,line,line",
   303  		Usage: "If set, ginkgo will skip specs in matching files. Can be specified multiple times, values are ORed."},
   304  
   305  	{KeyPath: "D.RegexScansFilePath", DeprecatedName: "regexScansFilePath", DeprecatedDocLink: "removed--regexscansfilepath", DeprecatedVersion: "2.0.0"},
   306  	{KeyPath: "D.DebugParallel", DeprecatedName: "debug", DeprecatedDocLink: "removed--debug", DeprecatedVersion: "2.0.0"},
   307  	{KeyPath: "D.EmitSpecProgress", DeprecatedName: "progress", SectionKey: "debug",
   308  		DeprecatedVersion: "2.5.0", Usage: ".  The functionality provided by --progress was confusing and is no longer needed.  Use --show-node-events instead to see node entry and exit events included in the timeline of failed and verbose specs.  Or you can run with -vv to always see all node events.  Lastly, --poll-progress-after and the PollProgressAfter decorator now provide a better mechanism for debugging specs that tend to get stuck."},
   309  }
   310  
   311  // ParallelConfigFlags provides flags for the Ginkgo test process (not the CLI)
   312  var ParallelConfigFlags = GinkgoFlags{
   313  	{KeyPath: "S.ParallelProcess", Name: "parallel.process", SectionKey: "low-level-parallel", UsageDefaultValue: "1",
   314  		Usage: "This worker process's (one-indexed) process number.  For running specs in parallel."},
   315  	{KeyPath: "S.ParallelTotal", Name: "parallel.total", SectionKey: "low-level-parallel", UsageDefaultValue: "1",
   316  		Usage: "The total number of worker processes.  For running specs in parallel."},
   317  	{KeyPath: "S.ParallelHost", Name: "parallel.host", SectionKey: "low-level-parallel", UsageDefaultValue: "set by Ginkgo CLI",
   318  		Usage: "The address for the server that will synchronize the processes."},
   319  }
   320  
   321  // ReporterConfigFlags provides flags for the Ginkgo test process, and CLI
   322  var ReporterConfigFlags = GinkgoFlags{
   323  	{KeyPath: "R.NoColor", Name: "no-color", SectionKey: "output", DeprecatedName: "noColor", DeprecatedDocLink: "changed-command-line-flags",
   324  		Usage: "If set, suppress color output in default reporter."},
   325  	{KeyPath: "R.Verbose", Name: "v", SectionKey: "output",
   326  		Usage: "If set, emits more output including GinkgoWriter contents."},
   327  	{KeyPath: "R.VeryVerbose", Name: "vv", SectionKey: "output",
   328  		Usage: "If set, emits with maximal verbosity - includes skipped and pending tests."},
   329  	{KeyPath: "R.Succinct", Name: "succinct", SectionKey: "output",
   330  		Usage: "If set, default reporter prints out a very succinct report"},
   331  	{KeyPath: "R.FullTrace", Name: "trace", SectionKey: "output",
   332  		Usage: "If set, default reporter prints out the full stack trace when a failure occurs"},
   333  	{KeyPath: "R.ShowNodeEvents", Name: "show-node-events", SectionKey: "output",
   334  		Usage: "If set, default reporter prints node > Enter and < Exit events when specs fail"},
   335  	{KeyPath: "R.GithubOutput", Name: "github-output", SectionKey: "output",
   336  		Usage: "If set, default reporter prints easier to manage output in Github Actions."},
   337  
   338  	{KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output",
   339  		Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."},
   340  	{KeyPath: "R.JUnitReport", Name: "junit-report", UsageArgument: "filename.xml", SectionKey: "output", DeprecatedName: "reportFile", DeprecatedDocLink: "improved-reporting-infrastructure",
   341  		Usage: "If set, Ginkgo will generate a conformant junit test report in the specified file."},
   342  	{KeyPath: "R.TeamcityReport", Name: "teamcity-report", UsageArgument: "filename", SectionKey: "output",
   343  		Usage: "If set, Ginkgo will generate a Teamcity-formatted test report at the specified location."},
   344  
   345  	{KeyPath: "D.SlowSpecThresholdWithFLoatUnits", DeprecatedName: "slowSpecThreshold", DeprecatedDocLink: "changed--slowspecthreshold",
   346  		Usage: "use --slow-spec-threshold instead and pass in a duration string (e.g. '5s', not '5.0')"},
   347  	{KeyPath: "D.NoisyPendings", DeprecatedName: "noisyPendings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"},
   348  	{KeyPath: "D.NoisySkippings", DeprecatedName: "noisySkippings", DeprecatedDocLink: "removed--noisypendings-and--noisyskippings", DeprecatedVersion: "2.0.0"},
   349  	{KeyPath: "D.SlowSpecThreshold", DeprecatedName: "slow-spec-threshold", SectionKey: "output", Usage: "--slow-spec-threshold has been deprecated and will be removed in a future version of Ginkgo.  This feature has proved to be more noisy than useful.  You can use --poll-progress-after, instead, to get more actionable feedback about potentially slow specs and understand where they might be getting stuck.", DeprecatedVersion: "2.5.0"},
   350  	{KeyPath: "D.AlwaysEmitGinkgoWriter", DeprecatedName: "always-emit-ginkgo-writer", SectionKey: "output", Usage: " - use -v instead, or one of Ginkgo's machine-readable report formats to get GinkgoWriter output for passing specs."},
   351  }
   352  
   353  // BuildTestSuiteFlagSet attaches to the CommandLine flagset and provides flags for the Ginkgo test process
   354  func BuildTestSuiteFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig) (GinkgoFlagSet, error) {
   355  	flags := SuiteConfigFlags.CopyAppend(ParallelConfigFlags...).CopyAppend(ReporterConfigFlags...)
   356  	flags = flags.WithPrefix("ginkgo")
   357  	bindings := map[string]interface{}{
   358  		"S": suiteConfig,
   359  		"R": reporterConfig,
   360  		"D": &deprecatedConfig{},
   361  	}
   362  	extraGoFlagsSection := GinkgoFlagSection{Style: "{{gray}}", Heading: "Go test flags"}
   363  
   364  	return NewAttachedGinkgoFlagSet(flag.CommandLine, flags, bindings, FlagSections, extraGoFlagsSection)
   365  }
   366  
   367  // VetConfig validates that the Ginkgo test process' configuration is sound
   368  func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig ReporterConfig) []error {
   369  	errors := []error{}
   370  
   371  	if flagSet.WasSet("count") || flagSet.WasSet("test.count") {
   372  		flag := flagSet.Lookup("count")
   373  		if flag == nil {
   374  			flag = flagSet.Lookup("test.count")
   375  		}
   376  		count, err := strconv.Atoi(flag.Value.String())
   377  		if err != nil || count != 1 {
   378  			errors = append(errors, GinkgoErrors.InvalidGoFlagCount())
   379  		}
   380  	}
   381  
   382  	if flagSet.WasSet("parallel") || flagSet.WasSet("test.parallel") {
   383  		errors = append(errors, GinkgoErrors.InvalidGoFlagParallel())
   384  	}
   385  
   386  	if suiteConfig.ParallelTotal < 1 {
   387  		errors = append(errors, GinkgoErrors.InvalidParallelTotalConfiguration())
   388  	}
   389  
   390  	if suiteConfig.ParallelProcess > suiteConfig.ParallelTotal || suiteConfig.ParallelProcess < 1 {
   391  		errors = append(errors, GinkgoErrors.InvalidParallelProcessConfiguration())
   392  	}
   393  
   394  	if suiteConfig.ParallelTotal > 1 && suiteConfig.ParallelHost == "" {
   395  		errors = append(errors, GinkgoErrors.MissingParallelHostConfiguration())
   396  	}
   397  
   398  	if suiteConfig.DryRun && suiteConfig.ParallelTotal > 1 {
   399  		errors = append(errors, GinkgoErrors.DryRunInParallelConfiguration())
   400  	}
   401  
   402  	if suiteConfig.GracePeriod <= 0 {
   403  		errors = append(errors, GinkgoErrors.GracePeriodCannotBeZero())
   404  	}
   405  
   406  	if len(suiteConfig.FocusFiles) > 0 {
   407  		_, err := ParseFileFilters(suiteConfig.FocusFiles)
   408  		if err != nil {
   409  			errors = append(errors, err)
   410  		}
   411  	}
   412  
   413  	if len(suiteConfig.SkipFiles) > 0 {
   414  		_, err := ParseFileFilters(suiteConfig.SkipFiles)
   415  		if err != nil {
   416  			errors = append(errors, err)
   417  		}
   418  	}
   419  
   420  	if suiteConfig.LabelFilter != "" {
   421  		_, err := ParseLabelFilter(suiteConfig.LabelFilter)
   422  		if err != nil {
   423  			errors = append(errors, err)
   424  		}
   425  	}
   426  
   427  	switch strings.ToLower(suiteConfig.OutputInterceptorMode) {
   428  	case "", "dup", "swap", "none":
   429  	default:
   430  		errors = append(errors, GinkgoErrors.InvalidOutputInterceptorModeConfiguration(suiteConfig.OutputInterceptorMode))
   431  	}
   432  
   433  	numVerbosity := 0
   434  	for _, v := range []bool{reporterConfig.Succinct, reporterConfig.Verbose, reporterConfig.VeryVerbose} {
   435  		if v {
   436  			numVerbosity++
   437  		}
   438  	}
   439  	if numVerbosity > 1 {
   440  		errors = append(errors, GinkgoErrors.ConflictingVerbosityConfiguration())
   441  	}
   442  
   443  	return errors
   444  }
   445  
   446  // GinkgoCLISharedFlags provides flags shared by the Ginkgo CLI's build, watch, and run commands
   447  var GinkgoCLISharedFlags = GinkgoFlags{
   448  	{KeyPath: "C.Recurse", Name: "r", SectionKey: "multiple-suites",
   449  		Usage: "If set, ginkgo finds and runs test suites under the current directory recursively."},
   450  	{KeyPath: "C.SkipPackage", Name: "skip-package", SectionKey: "multiple-suites", DeprecatedName: "skipPackage", DeprecatedDocLink: "changed-command-line-flags",
   451  		UsageArgument: "comma-separated list of packages",
   452  		Usage:         "A comma-separated list of package names to be skipped.  If any part of the package's path matches, that package is ignored."},
   453  	{KeyPath: "C.RequireSuite", Name: "require-suite", SectionKey: "failure", DeprecatedName: "requireSuite", DeprecatedDocLink: "changed-command-line-flags",
   454  		Usage: "If set, Ginkgo fails if there are ginkgo tests in a directory but no invocation of RunSpecs."},
   455  	{KeyPath: "C.NumCompilers", Name: "compilers", SectionKey: "multiple-suites", UsageDefaultValue: "0 (will autodetect)",
   456  		Usage: "When running multiple packages, the number of concurrent compilations to perform."},
   457  }
   458  
   459  // GinkgoCLIRunAndWatchFlags provides flags shared by the Ginkgo CLI's build and watch commands (but not run)
   460  var GinkgoCLIRunAndWatchFlags = GinkgoFlags{
   461  	{KeyPath: "C.Procs", Name: "procs", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)",
   462  		Usage: "The number of parallel test nodes to run."},
   463  	{KeyPath: "C.Procs", Name: "nodes", SectionKey: "parallel", UsageDefaultValue: "1 (run in series)",
   464  		Usage: "--nodes is an alias for --procs"},
   465  	{KeyPath: "C.Parallel", Name: "p", SectionKey: "parallel",
   466  		Usage: "If set, ginkgo will run in parallel with an auto-detected number of nodes."},
   467  	{KeyPath: "C.AfterRunHook", Name: "after-run-hook", SectionKey: "misc", DeprecatedName: "afterSuiteHook", DeprecatedDocLink: "changed-command-line-flags",
   468  		Usage: "Command to run when a test suite completes."},
   469  	{KeyPath: "C.OutputDir", Name: "output-dir", SectionKey: "output", UsageArgument: "directory", DeprecatedName: "outputdir", DeprecatedDocLink: "improved-profiling-support",
   470  		Usage: "A location to place all generated profiles and reports."},
   471  	{KeyPath: "C.KeepSeparateCoverprofiles", Name: "keep-separate-coverprofiles", SectionKey: "code-and-coverage-analysis",
   472  		Usage: "If set, Ginkgo does not merge coverprofiles into one monolithic coverprofile.  The coverprofiles will remain in their respective package directories or in -output-dir if set."},
   473  	{KeyPath: "C.KeepSeparateReports", Name: "keep-separate-reports", SectionKey: "output",
   474  		Usage: "If set, Ginkgo does not merge per-suite reports (e.g. -json-report) into one monolithic report for the entire testrun.  The reports will remain in their respective package directories or in -output-dir if set."},
   475  
   476  	{KeyPath: "D.Stream", DeprecatedName: "stream", DeprecatedDocLink: "removed--stream", DeprecatedVersion: "2.0.0"},
   477  	{KeyPath: "D.Notify", DeprecatedName: "notify", DeprecatedDocLink: "removed--notify", DeprecatedVersion: "2.0.0"},
   478  }
   479  
   480  // GinkgoCLIRunFlags provides flags for Ginkgo CLI's run command that aren't shared by any other commands
   481  var GinkgoCLIRunFlags = GinkgoFlags{
   482  	{KeyPath: "C.KeepGoing", Name: "keep-going", SectionKey: "multiple-suites", DeprecatedName: "keepGoing", DeprecatedDocLink: "changed-command-line-flags",
   483  		Usage: "If set, failures from earlier test suites do not prevent later test suites from running."},
   484  	{KeyPath: "C.UntilItFails", Name: "until-it-fails", SectionKey: "debug", DeprecatedName: "untilItFails", DeprecatedDocLink: "changed-command-line-flags",
   485  		Usage: "If set, ginkgo will keep rerunning test suites until a failure occurs."},
   486  	{KeyPath: "C.Repeat", Name: "repeat", SectionKey: "debug", UsageArgument: "n", UsageDefaultValue: "0 - i.e. no repetition, run only once",
   487  		Usage: "The number of times to re-run a test-suite.  Useful for debugging flaky tests.  If set to N the suite will be run N+1 times and will be required to pass each time."},
   488  	{KeyPath: "C.RandomizeSuites", Name: "randomize-suites", SectionKey: "order", DeprecatedName: "randomizeSuites", DeprecatedDocLink: "changed-command-line-flags",
   489  		Usage: "If set, ginkgo will randomize the order in which test suites run."},
   490  }
   491  
   492  // GinkgoCLIRunFlags provides flags for Ginkgo CLI's watch command that aren't shared by any other commands
   493  var GinkgoCLIWatchFlags = GinkgoFlags{
   494  	{KeyPath: "C.Depth", Name: "depth", SectionKey: "watch",
   495  		Usage: "Ginkgo will watch dependencies down to this depth in the dependency tree."},
   496  	{KeyPath: "C.WatchRegExp", Name: "watch-regexp", SectionKey: "watch", DeprecatedName: "watchRegExp", DeprecatedDocLink: "changed-command-line-flags",
   497  		UsageArgument:     "Regular Expression",
   498  		UsageDefaultValue: `\.go$`,
   499  		Usage:             "Only files matching this regular expression will be watched for changes."},
   500  }
   501  
   502  // GoBuildFlags provides flags for the Ginkgo CLI build, run, and watch commands that capture go's build-time flags.  These are passed to go test -c by the ginkgo CLI
   503  var GoBuildFlags = GinkgoFlags{
   504  	{KeyPath: "Go.Race", Name: "race", SectionKey: "code-and-coverage-analysis",
   505  		Usage: "enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, linux/ppc64le and linux/arm64 (only for 48-bit VMA)."},
   506  	{KeyPath: "Go.Vet", Name: "vet", UsageArgument: "list", SectionKey: "code-and-coverage-analysis",
   507  		Usage: `Configure the invocation of "go vet" during "go test" to use the comma-separated list of vet checks.  If list is empty, "go test" runs "go vet" with a curated list of checks believed to be always worth addressing.  If list is "off", "go test" does not run "go vet" at all.  Available checks can be found by running 'go doc cmd/vet'`},
   508  	{KeyPath: "Go.Cover", Name: "cover", SectionKey: "code-and-coverage-analysis",
   509  		Usage: "Enable coverage analysis.	Note that because coverage works by annotating the source code before compilation, compilation and test failures with coverage enabled may report line numbers that don't correspond to the original sources."},
   510  	{KeyPath: "Go.CoverMode", Name: "covermode", UsageArgument: "set,count,atomic", SectionKey: "code-and-coverage-analysis",
   511  		Usage: `Set the mode for coverage analysis for the package[s] being tested. 'set': does this statement run? 'count': how many times does this statement run? 'atomic': like count, but correct in multithreaded tests and more expensive (must use atomic with -race). Sets -cover`},
   512  	{KeyPath: "Go.CoverPkg", Name: "coverpkg", UsageArgument: "pattern1,pattern2,pattern3", SectionKey: "code-and-coverage-analysis",
   513  		Usage: "Apply coverage analysis in each test to packages matching the patterns. 	The default is for each test to analyze only the package being tested. See 'go help packages' for a description of package patterns. Sets -cover."},
   514  
   515  	{KeyPath: "Go.A", Name: "a", SectionKey: "go-build",
   516  		Usage: "force rebuilding of packages that are already up-to-date."},
   517  	{KeyPath: "Go.ASMFlags", Name: "asmflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build",
   518  		Usage: "arguments to pass on each go tool asm invocation."},
   519  	{KeyPath: "Go.BuildMode", Name: "buildmode", UsageArgument: "mode", SectionKey: "go-build",
   520  		Usage: "build mode to use. See 'go help buildmode' for more."},
   521  	{KeyPath: "Go.Compiler", Name: "compiler", UsageArgument: "name", SectionKey: "go-build",
   522  		Usage: "name of compiler to use, as in runtime.Compiler (gccgo or gc)."},
   523  	{KeyPath: "Go.GCCGoFlags", Name: "gccgoflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build",
   524  		Usage: "arguments to pass on each gccgo compiler/linker invocation."},
   525  	{KeyPath: "Go.GCFlags", Name: "gcflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build",
   526  		Usage: "arguments to pass on each go tool compile invocation."},
   527  	{KeyPath: "Go.InstallSuffix", Name: "installsuffix", SectionKey: "go-build",
   528  		Usage: "a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to raceor, if set explicitly, has _race appended to it. Likewise for the -msan flag.  Using a -buildmode option that requires non-default compile flags has a similar effect."},
   529  	{KeyPath: "Go.LDFlags", Name: "ldflags", UsageArgument: "'[pattern=]arg list'", SectionKey: "go-build",
   530  		Usage: "arguments to pass on each go tool link invocation."},
   531  	{KeyPath: "Go.LinkShared", Name: "linkshared", SectionKey: "go-build",
   532  		Usage: "build code that will be linked against shared libraries previously created with -buildmode=shared."},
   533  	{KeyPath: "Go.Mod", Name: "mod", UsageArgument: "mode (readonly, vendor, or mod)", SectionKey: "go-build",
   534  		Usage: "module download mode to use: readonly, vendor, or mod.  See 'go help modules' for more."},
   535  	{KeyPath: "Go.ModCacheRW", Name: "modcacherw", SectionKey: "go-build",
   536  		Usage: "leave newly-created directories in the module cache read-write instead of making them read-only."},
   537  	{KeyPath: "Go.ModFile", Name: "modfile", UsageArgument: "file", SectionKey: "go-build",
   538  		Usage: `in module aware mode, read (and possibly write) an alternate go.mod file instead of the one in the module root directory. A file named go.mod must still be present in order to determine the module root directory, but it is not accessed. When -modfile is specified, an alternate go.sum file is also used: its path is derived from the -modfile flag by trimming the ".mod" extension and appending ".sum".`},
   539  	{KeyPath: "Go.MSan", Name: "msan", SectionKey: "go-build",
   540  		Usage: "enable interoperation with memory sanitizer. Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. On linux/arm64, pie build mode will be used."},
   541  	{KeyPath: "Go.N", Name: "n", SectionKey: "go-build",
   542  		Usage: "print the commands but do not run them."},
   543  	{KeyPath: "Go.PkgDir", Name: "pkgdir", UsageArgument: "dir", SectionKey: "go-build",
   544  		Usage: "install and load all packages from dir instead of the usual locations. For example, when building with a non-standard configuration, use -pkgdir to keep generated packages in a separate location."},
   545  	{KeyPath: "Go.Tags", Name: "tags", UsageArgument: "tag,list", SectionKey: "go-build",
   546  		Usage: "a comma-separated list of build tags to consider satisfied during the build. For more information about build tags, see the description of build constraints in the documentation for the go/build package. (Earlier versions of Go used a space-separated list, and that form is deprecated but still recognized.)"},
   547  	{KeyPath: "Go.TrimPath", Name: "trimpath", SectionKey: "go-build",
   548  		Usage: `remove all file system paths from the resulting executable. Instead of absolute file system paths, the recorded file names will begin with either "go" (for the standard library), or a module path@version (when using modules), or a plain import path (when using GOPATH).`},
   549  	{KeyPath: "Go.ToolExec", Name: "toolexec", UsageArgument: "'cmd args'", SectionKey: "go-build",
   550  		Usage: "a program to use to invoke toolchain programs like vet and asm. For example, instead of running asm, the go command will run cmd args /path/to/asm <arguments for asm>'."},
   551  	{KeyPath: "Go.Work", Name: "work", SectionKey: "go-build",
   552  		Usage: "print the name of the temporary work directory and do not delete it when exiting."},
   553  	{KeyPath: "Go.X", Name: "x", SectionKey: "go-build",
   554  		Usage: "print the commands."},
   555  }
   556  
   557  // GoRunFlags provides flags for the Ginkgo CLI  run, and watch commands that capture go's run-time flags.  These are passed to the compiled test binary by the ginkgo CLI
   558  var GoRunFlags = GinkgoFlags{
   559  	{KeyPath: "Go.CoverProfile", Name: "coverprofile", UsageArgument: "file", SectionKey: "code-and-coverage-analysis",
   560  		Usage: `Write a coverage profile to the file after all tests have passed. Sets -cover.`},
   561  	{KeyPath: "Go.BlockProfile", Name: "blockprofile", UsageArgument: "file", SectionKey: "performance-analysis",
   562  		Usage: `Write a goroutine blocking profile to the specified file when all tests are complete. Preserves test binary.`},
   563  	{KeyPath: "Go.BlockProfileRate", Name: "blockprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis",
   564  		Usage: `Control the detail provided in goroutine blocking profiles by calling runtime.SetBlockProfileRate with rate. See 'go doc runtime.SetBlockProfileRate'. The profiler aims to sample, on average, one blocking event every n nanoseconds the program spends blocked. By default, if -test.blockprofile is set without this flag, all blocking events are recorded, equivalent to -test.blockprofilerate=1.`},
   565  	{KeyPath: "Go.CPUProfile", Name: "cpuprofile", UsageArgument: "file", SectionKey: "performance-analysis",
   566  		Usage: `Write a CPU profile to the specified file before exiting. Preserves test binary.`},
   567  	{KeyPath: "Go.MemProfile", Name: "memprofile", UsageArgument: "file", SectionKey: "performance-analysis",
   568  		Usage: `Write an allocation profile to the file after all tests have passed. Preserves test binary.`},
   569  	{KeyPath: "Go.MemProfileRate", Name: "memprofilerate", UsageArgument: "rate", SectionKey: "performance-analysis",
   570  		Usage: `Enable more precise (and expensive) memory allocation profiles by setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1.`},
   571  	{KeyPath: "Go.MutexProfile", Name: "mutexprofile", UsageArgument: "file", SectionKey: "performance-analysis",
   572  		Usage: `Write a mutex contention profile to the specified file when all tests are complete. Preserves test binary.`},
   573  	{KeyPath: "Go.MutexProfileFraction", Name: "mutexprofilefraction", UsageArgument: "n", SectionKey: "performance-analysis",
   574  		Usage: `if >= 0, calls runtime.SetMutexProfileFraction()	Sample 1 in n stack traces of goroutines holding a contended mutex.`},
   575  	{KeyPath: "Go.Trace", Name: "execution-trace", UsageArgument: "file", ExportAs: "trace", SectionKey: "performance-analysis",
   576  		Usage: `Write an execution trace to the specified file before exiting.`},
   577  }
   578  
   579  // VetAndInitializeCLIAndGoConfig validates that the Ginkgo CLI's configuration is sound
   580  // It returns a potentially mutated copy of the config that rationalizes the configuration to ensure consistency for downstream consumers
   581  func VetAndInitializeCLIAndGoConfig(cliConfig CLIConfig, goFlagsConfig GoFlagsConfig) (CLIConfig, GoFlagsConfig, []error) {
   582  	errors := []error{}
   583  
   584  	if cliConfig.Repeat > 0 && cliConfig.UntilItFails {
   585  		errors = append(errors, GinkgoErrors.BothRepeatAndUntilItFails())
   586  	}
   587  
   588  	//initialize the output directory
   589  	if cliConfig.OutputDir != "" {
   590  		err := os.MkdirAll(cliConfig.OutputDir, 0777)
   591  		if err != nil {
   592  			errors = append(errors, err)
   593  		}
   594  	}
   595  
   596  	//ensure cover mode is configured appropriately
   597  	if goFlagsConfig.CoverMode != "" || goFlagsConfig.CoverPkg != "" || goFlagsConfig.CoverProfile != "" {
   598  		goFlagsConfig.Cover = true
   599  	}
   600  	if goFlagsConfig.Cover && goFlagsConfig.CoverProfile == "" {
   601  		goFlagsConfig.CoverProfile = "coverprofile.out"
   602  	}
   603  
   604  	return cliConfig, goFlagsConfig, errors
   605  }
   606  
   607  // GenerateGoTestCompileArgs is used by the Ginkgo CLI to generate command line arguments to pass to the go test -c command when compiling the test
   608  func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, packageToBuild string, pathToInvocationPath string) ([]string, error) {
   609  	// if the user has set the CoverProfile run-time flag make sure to set the build-time cover flag to make sure
   610  	// the built test binary can generate a coverprofile
   611  	if goFlagsConfig.CoverProfile != "" {
   612  		goFlagsConfig.Cover = true
   613  	}
   614  
   615  	if goFlagsConfig.CoverPkg != "" {
   616  		coverPkgs := strings.Split(goFlagsConfig.CoverPkg, ",")
   617  		adjustedCoverPkgs := make([]string, len(coverPkgs))
   618  		for i, coverPkg := range coverPkgs {
   619  			coverPkg = strings.Trim(coverPkg, " ")
   620  			if strings.HasPrefix(coverPkg, "./") {
   621  				// this is a relative coverPkg - we need to reroot it
   622  				adjustedCoverPkgs[i] = "./" + filepath.Join(pathToInvocationPath, strings.TrimPrefix(coverPkg, "./"))
   623  			} else {
   624  				// this is a package name - don't touch it
   625  				adjustedCoverPkgs[i] = coverPkg
   626  			}
   627  		}
   628  		goFlagsConfig.CoverPkg = strings.Join(adjustedCoverPkgs, ",")
   629  	}
   630  
   631  	args := []string{"test", "-c", "-o", destination, packageToBuild}
   632  	goArgs, err := GenerateFlagArgs(
   633  		GoBuildFlags,
   634  		map[string]interface{}{
   635  			"Go": &goFlagsConfig,
   636  		},
   637  	)
   638  
   639  	if err != nil {
   640  		return []string{}, err
   641  	}
   642  	args = append(args, goArgs...)
   643  	return args, nil
   644  }
   645  
   646  // GenerateGinkgoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled Ginkgo test binary
   647  func GenerateGinkgoTestRunArgs(suiteConfig SuiteConfig, reporterConfig ReporterConfig, goFlagsConfig GoFlagsConfig) ([]string, error) {
   648  	var flags GinkgoFlags
   649  	flags = SuiteConfigFlags.WithPrefix("ginkgo")
   650  	flags = flags.CopyAppend(ParallelConfigFlags.WithPrefix("ginkgo")...)
   651  	flags = flags.CopyAppend(ReporterConfigFlags.WithPrefix("ginkgo")...)
   652  	flags = flags.CopyAppend(GoRunFlags.WithPrefix("test")...)
   653  	bindings := map[string]interface{}{
   654  		"S":  &suiteConfig,
   655  		"R":  &reporterConfig,
   656  		"Go": &goFlagsConfig,
   657  	}
   658  
   659  	return GenerateFlagArgs(flags, bindings)
   660  }
   661  
   662  // GenerateGoTestRunArgs is used by the Ginkgo CLI to generate command line arguments to pass to the compiled non-Ginkgo test binary
   663  func GenerateGoTestRunArgs(goFlagsConfig GoFlagsConfig) ([]string, error) {
   664  	flags := GoRunFlags.WithPrefix("test")
   665  	bindings := map[string]interface{}{
   666  		"Go": &goFlagsConfig,
   667  	}
   668  
   669  	args, err := GenerateFlagArgs(flags, bindings)
   670  	if err != nil {
   671  		return args, err
   672  	}
   673  	args = append(args, "--test.v")
   674  	return args, nil
   675  }
   676  
   677  // BuildRunCommandFlagSet builds the FlagSet for the `ginkgo run` command
   678  func BuildRunCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) {
   679  	flags := SuiteConfigFlags
   680  	flags = flags.CopyAppend(ReporterConfigFlags...)
   681  	flags = flags.CopyAppend(GinkgoCLISharedFlags...)
   682  	flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...)
   683  	flags = flags.CopyAppend(GinkgoCLIRunFlags...)
   684  	flags = flags.CopyAppend(GoBuildFlags...)
   685  	flags = flags.CopyAppend(GoRunFlags...)
   686  
   687  	bindings := map[string]interface{}{
   688  		"S":  suiteConfig,
   689  		"R":  reporterConfig,
   690  		"C":  cliConfig,
   691  		"Go": goFlagsConfig,
   692  		"D":  &deprecatedConfig{},
   693  	}
   694  
   695  	return NewGinkgoFlagSet(flags, bindings, FlagSections)
   696  }
   697  
   698  // BuildWatchCommandFlagSet builds the FlagSet for the `ginkgo watch` command
   699  func BuildWatchCommandFlagSet(suiteConfig *SuiteConfig, reporterConfig *ReporterConfig, cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) {
   700  	flags := SuiteConfigFlags
   701  	flags = flags.CopyAppend(ReporterConfigFlags...)
   702  	flags = flags.CopyAppend(GinkgoCLISharedFlags...)
   703  	flags = flags.CopyAppend(GinkgoCLIRunAndWatchFlags...)
   704  	flags = flags.CopyAppend(GinkgoCLIWatchFlags...)
   705  	flags = flags.CopyAppend(GoBuildFlags...)
   706  	flags = flags.CopyAppend(GoRunFlags...)
   707  
   708  	bindings := map[string]interface{}{
   709  		"S":  suiteConfig,
   710  		"R":  reporterConfig,
   711  		"C":  cliConfig,
   712  		"Go": goFlagsConfig,
   713  		"D":  &deprecatedConfig{},
   714  	}
   715  
   716  	return NewGinkgoFlagSet(flags, bindings, FlagSections)
   717  }
   718  
   719  // BuildBuildCommandFlagSet builds the FlagSet for the `ginkgo build` command
   720  func BuildBuildCommandFlagSet(cliConfig *CLIConfig, goFlagsConfig *GoFlagsConfig) (GinkgoFlagSet, error) {
   721  	flags := GinkgoCLISharedFlags
   722  	flags = flags.CopyAppend(GoBuildFlags...)
   723  
   724  	bindings := map[string]interface{}{
   725  		"C":  cliConfig,
   726  		"Go": goFlagsConfig,
   727  		"D":  &deprecatedConfig{},
   728  	}
   729  
   730  	flagSections := make(GinkgoFlagSections, len(FlagSections))
   731  	copy(flagSections, FlagSections)
   732  	for i := range flagSections {
   733  		if flagSections[i].Key == "multiple-suites" {
   734  			flagSections[i].Heading = "Building Multiple Suites"
   735  		}
   736  		if flagSections[i].Key == "go-build" {
   737  			flagSections[i] = GinkgoFlagSection{Key: "go-build", Style: "{{/}}", Heading: "Go Build Flags",
   738  				Description: "These flags are inherited from go build."}
   739  		}
   740  	}
   741  
   742  	return NewGinkgoFlagSet(flags, bindings, flagSections)
   743  }
   744  
   745  func BuildLabelsCommandFlagSet(cliConfig *CLIConfig) (GinkgoFlagSet, error) {
   746  	flags := GinkgoCLISharedFlags.SubsetWithNames("r", "skip-package")
   747  
   748  	bindings := map[string]interface{}{
   749  		"C": cliConfig,
   750  	}
   751  
   752  	flagSections := make(GinkgoFlagSections, len(FlagSections))
   753  	copy(flagSections, FlagSections)
   754  	for i := range flagSections {
   755  		if flagSections[i].Key == "multiple-suites" {
   756  			flagSections[i].Heading = "Fetching Labels from Multiple Suites"
   757  		}
   758  	}
   759  
   760  	return NewGinkgoFlagSet(flags, bindings, flagSections)
   761  }
   762  

View as plain text