1
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
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
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
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
119 type CLIConfig struct {
120
121 Recurse bool
122 SkipPackage string
123 RequireSuite bool
124 NumCompilers int
125
126
127 Procs int
128 Parallel bool
129 AfterRunHook string
130 OutputDir string
131 KeepSeparateCoverprofiles bool
132 KeepSeparateReports bool
133
134
135 KeepGoing bool
136 UntilItFails bool
137 Repeat int
138 RandomizeSuites bool
139
140
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
176
177
178
179 type GoFlagsConfig struct {
180
181 Race bool
182 Cover bool
183 CoverMode string
184 CoverPkg string
185 Vet string
186
187
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
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
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
244
245
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
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
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
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
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
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
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
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
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
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
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
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
580
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
589 if cliConfig.OutputDir != "" {
590 err := os.MkdirAll(cliConfig.OutputDir, 0777)
591 if err != nil {
592 errors = append(errors, err)
593 }
594 }
595
596
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
608 func GenerateGoTestCompileArgs(goFlagsConfig GoFlagsConfig, destination string, packageToBuild string, pathToInvocationPath string) ([]string, error) {
609
610
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
622 adjustedCoverPkgs[i] = "./" + filepath.Join(pathToInvocationPath, strings.TrimPrefix(coverPkg, "./"))
623 } else {
624
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
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
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
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
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
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