1 /* 2 Ginkgo is a testing framework for Go designed to help you write expressive tests. 3 https://github.com/onsi/ginkgo 4 MIT-Licensed 5 6 The godoc documentation outlines Ginkgo's API. Since Ginkgo is a Domain-Specific Language it is important to 7 build a mental model for Ginkgo - the narrative documentation at https://onsi.github.io/ginkgo/ is designed to help you do that. 8 You should start there - even a brief skim will be helpful. At minimum you should skim through the https://onsi.github.io/ginkgo/#getting-started chapter. 9 10 Ginkgo's is best paired with the Gomega matcher library: https://github.com/onsi/gomega 11 12 You can run Ginkgo specs with go test - however we recommend using the ginkgo cli. It enables functionality 13 that go test does not (especially running suites in parallel). You can learn more at https://onsi.github.io/ginkgo/#ginkgo-cli-overview 14 or by running 'ginkgo help'. 15 */ 16 package ginkgo 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 "strings" 24 25 "github.com/go-logr/logr" 26 "github.com/onsi/ginkgo/v2/formatter" 27 "github.com/onsi/ginkgo/v2/internal" 28 "github.com/onsi/ginkgo/v2/internal/global" 29 "github.com/onsi/ginkgo/v2/internal/interrupt_handler" 30 "github.com/onsi/ginkgo/v2/internal/parallel_support" 31 "github.com/onsi/ginkgo/v2/reporters" 32 "github.com/onsi/ginkgo/v2/types" 33 ) 34 35 const GINKGO_VERSION = types.VERSION 36 37 var flagSet types.GinkgoFlagSet 38 var deprecationTracker = types.NewDeprecationTracker() 39 var suiteConfig = types.NewDefaultSuiteConfig() 40 var reporterConfig = types.NewDefaultReporterConfig() 41 var suiteDidRun = false 42 var outputInterceptor internal.OutputInterceptor 43 var client parallel_support.Client 44 45 func init() { 46 var err error 47 flagSet, err = types.BuildTestSuiteFlagSet(&suiteConfig, &reporterConfig) 48 exitIfErr(err) 49 writer := internal.NewWriter(os.Stdout) 50 GinkgoWriter = writer 51 GinkgoLogr = internal.GinkgoLogrFunc(writer) 52 } 53 54 func exitIfErr(err error) { 55 if err != nil { 56 if outputInterceptor != nil { 57 outputInterceptor.Shutdown() 58 } 59 if client != nil { 60 client.Close() 61 } 62 fmt.Fprintln(formatter.ColorableStdErr, err.Error()) 63 os.Exit(1) 64 } 65 } 66 67 func exitIfErrors(errors []error) { 68 if len(errors) > 0 { 69 if outputInterceptor != nil { 70 outputInterceptor.Shutdown() 71 } 72 if client != nil { 73 client.Close() 74 } 75 for _, err := range errors { 76 fmt.Fprintln(formatter.ColorableStdErr, err.Error()) 77 } 78 os.Exit(1) 79 } 80 } 81 82 // The interface implemented by GinkgoWriter 83 type GinkgoWriterInterface interface { 84 io.Writer 85 86 Print(a ...interface{}) 87 Printf(format string, a ...interface{}) 88 Println(a ...interface{}) 89 90 TeeTo(writer io.Writer) 91 ClearTeeWriters() 92 } 93 94 /* 95 SpecContext is the context object passed into nodes that are subject to a timeout or need to be notified of an interrupt. It implements the standard context.Context interface but also contains additional helpers to provide an extensibility point for Ginkgo. (As an example, Gomega's Eventually can use the methods defined on SpecContext to provide deeper integration with Ginkgo). 96 97 You can do anything with SpecContext that you do with a typical context.Context including wrapping it with any of the context.With* methods. 98 99 Ginkgo will cancel the SpecContext when a node is interrupted (e.g. by the user sending an interrupt signal) or when a node has exceeded its allowed run-time. Note, however, that even in cases where a node has a deadline, SpecContext will not return a deadline via .Deadline(). This is because Ginkgo does not use a WithDeadline() context to model node deadlines as Ginkgo needs control over the precise timing of the context cancellation to ensure it can provide an accurate progress report at the moment of cancellation. 100 */ 101 type SpecContext = internal.SpecContext 102 103 /* 104 GinkgoWriter implements a GinkgoWriterInterface and io.Writer 105 106 When running in verbose mode (ginkgo -v) any writes to GinkgoWriter will be immediately printed 107 to stdout. Otherwise, GinkgoWriter will buffer any writes produced during the current test and flush them to screen 108 only if the current test fails. 109 110 GinkgoWriter also provides convenience Print, Printf and Println methods and allows you to tee to a custom writer via GinkgoWriter.TeeTo(writer). 111 Writes to GinkgoWriter are immediately sent to any registered TeeTo() writers. You can unregister all TeeTo() Writers with GinkgoWriter.ClearTeeWriters() 112 113 You can learn more at https://onsi.github.io/ginkgo/#logging-output 114 */ 115 var GinkgoWriter GinkgoWriterInterface 116 117 /* 118 GinkgoLogr is a logr.Logger that writes to GinkgoWriter 119 */ 120 var GinkgoLogr logr.Logger 121 122 // The interface by which Ginkgo receives *testing.T 123 type GinkgoTestingT interface { 124 Fail() 125 } 126 127 /* 128 GinkgoConfiguration returns the configuration of the current suite. 129 130 The first return value is the SuiteConfig which controls aspects of how the suite runs, 131 the second return value is the ReporterConfig which controls aspects of how Ginkgo's default 132 reporter emits output. 133 134 Mutating the returned configurations has no effect. To reconfigure Ginkgo programmatically you need 135 to pass in your mutated copies into RunSpecs(). 136 137 You can learn more at https://onsi.github.io/ginkgo/#overriding-ginkgos-command-line-configuration-in-the-suite 138 */ 139 func GinkgoConfiguration() (types.SuiteConfig, types.ReporterConfig) { 140 return suiteConfig, reporterConfig 141 } 142 143 /* 144 GinkgoRandomSeed returns the seed used to randomize spec execution order. It is 145 useful for seeding your own pseudorandom number generators to ensure 146 consistent executions from run to run, where your tests contain variability (for 147 example, when selecting random spec data). 148 149 You can learn more at https://onsi.github.io/ginkgo/#spec-randomization 150 */ 151 func GinkgoRandomSeed() int64 { 152 return suiteConfig.RandomSeed 153 } 154 155 /* 156 GinkgoParallelProcess returns the parallel process number for the current ginkgo process 157 The process number is 1-indexed. You can use GinkgoParallelProcess() to shard access to shared 158 resources across your suites. You can learn more about patterns for sharding at https://onsi.github.io/ginkgo/#patterns-for-parallel-integration-specs 159 160 For more on how specs are parallelized in Ginkgo, see http://onsi.github.io/ginkgo/#spec-parallelization 161 */ 162 func GinkgoParallelProcess() int { 163 return suiteConfig.ParallelProcess 164 } 165 166 /* 167 GinkgoHelper marks the function it's called in as a test helper. When a failure occurs inside a helper function, Ginkgo will skip the helper when analyzing the stack trace to identify where the failure occurred. 168 169 This is an alternative, simpler, mechanism to passing in a skip offset when calling Fail or using Gomega. 170 */ 171 func GinkgoHelper() { 172 types.MarkAsHelper(1) 173 } 174 175 /* 176 GinkgoLabelFilter() returns the label filter configured for this suite via `--label-filter`. 177 178 You can use this to manually check if a set of labels would satisfy the filter via: 179 180 if (Label("cat", "dog").MatchesLabelFilter(GinkgoLabelFilter())) { 181 //... 182 } 183 */ 184 func GinkgoLabelFilter() string { 185 suiteConfig, _ := GinkgoConfiguration() 186 return suiteConfig.LabelFilter 187 } 188 189 /* 190 PauseOutputInterception() pauses Ginkgo's output interception. This is only relevant 191 when running in parallel and output to stdout/stderr is being intercepted. You generally 192 don't need to call this function - however there are cases when Ginkgo's output interception 193 mechanisms can interfere with external processes launched by the test process. 194 195 In particular, if an external process is launched that has cmd.Stdout/cmd.Stderr set to os.Stdout/os.Stderr 196 then Ginkgo's output interceptor will hang. To circumvent this, set cmd.Stdout/cmd.Stderr to GinkgoWriter. 197 If, for some reason, you aren't able to do that, you can PauseOutputInterception() before starting the process 198 then ResumeOutputInterception() after starting it. 199 200 Note that PauseOutputInterception() does not cause stdout writes to print to the console - 201 this simply stops intercepting and storing stdout writes to an internal buffer. 202 */ 203 func PauseOutputInterception() { 204 if outputInterceptor == nil { 205 return 206 } 207 outputInterceptor.PauseIntercepting() 208 } 209 210 // ResumeOutputInterception() - see docs for PauseOutputInterception() 211 func ResumeOutputInterception() { 212 if outputInterceptor == nil { 213 return 214 } 215 outputInterceptor.ResumeIntercepting() 216 } 217 218 /* 219 RunSpecs is the entry point for the Ginkgo spec runner. 220 221 You must call this within a Golang testing TestX(t *testing.T) function. 222 If you bootstrapped your suite with "ginkgo bootstrap" this is already 223 done for you. 224 225 Ginkgo is typically configured via command-line flags. This configuration 226 can be overridden, however, and passed into RunSpecs as optional arguments: 227 228 func TestMySuite(t *testing.T) { 229 RegisterFailHandler(gomega.Fail) 230 // fetch the current config 231 suiteConfig, reporterConfig := GinkgoConfiguration() 232 // adjust it 233 suiteConfig.SkipStrings = []string{"NEVER-RUN"} 234 reporterConfig.FullTrace = true 235 // pass it in to RunSpecs 236 RunSpecs(t, "My Suite", suiteConfig, reporterConfig) 237 } 238 239 Note that some configuration changes can lead to undefined behavior. For example, 240 you should not change ParallelProcess or ParallelTotal as the Ginkgo CLI is responsible 241 for setting these and orchestrating parallel specs across the parallel processes. See http://onsi.github.io/ginkgo/#spec-parallelization 242 for more on how specs are parallelized in Ginkgo. 243 244 You can also pass suite-level Label() decorators to RunSpecs. The passed-in labels will apply to all specs in the suite. 245 */ 246 func RunSpecs(t GinkgoTestingT, description string, args ...interface{}) bool { 247 if suiteDidRun { 248 exitIfErr(types.GinkgoErrors.RerunningSuite()) 249 } 250 suiteDidRun = true 251 err := global.PushClone() 252 if err != nil { 253 exitIfErr(err) 254 } 255 defer global.PopClone() 256 257 suiteLabels := extractSuiteConfiguration(args) 258 259 var reporter reporters.Reporter 260 if suiteConfig.ParallelTotal == 1 { 261 reporter = reporters.NewDefaultReporter(reporterConfig, formatter.ColorableStdOut) 262 outputInterceptor = internal.NoopOutputInterceptor{} 263 client = nil 264 } else { 265 reporter = reporters.NoopReporter{} 266 switch strings.ToLower(suiteConfig.OutputInterceptorMode) { 267 case "swap": 268 outputInterceptor = internal.NewOSGlobalReassigningOutputInterceptor() 269 case "none": 270 outputInterceptor = internal.NoopOutputInterceptor{} 271 default: 272 outputInterceptor = internal.NewOutputInterceptor() 273 } 274 client = parallel_support.NewClient(suiteConfig.ParallelHost) 275 if !client.Connect() { 276 client = nil 277 exitIfErr(types.GinkgoErrors.UnreachableParallelHost(suiteConfig.ParallelHost)) 278 } 279 defer client.Close() 280 } 281 282 writer := GinkgoWriter.(*internal.Writer) 283 if reporterConfig.Verbosity().GTE(types.VerbosityLevelVerbose) && suiteConfig.ParallelTotal == 1 { 284 writer.SetMode(internal.WriterModeStreamAndBuffer) 285 } else { 286 writer.SetMode(internal.WriterModeBufferOnly) 287 } 288 289 if reporterConfig.WillGenerateReport() { 290 registerReportAfterSuiteNodeForAutogeneratedReports(reporterConfig) 291 } 292 293 err = global.Suite.BuildTree() 294 exitIfErr(err) 295 suitePath, err := getwd() 296 exitIfErr(err) 297 suitePath, err = filepath.Abs(suitePath) 298 exitIfErr(err) 299 300 passed, hasFocusedTests := global.Suite.Run(description, suiteLabels, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig) 301 outputInterceptor.Shutdown() 302 303 flagSet.ValidateDeprecations(deprecationTracker) 304 if deprecationTracker.DidTrackDeprecations() { 305 fmt.Fprintln(formatter.ColorableStdErr, deprecationTracker.DeprecationsReport()) 306 } 307 308 if !passed { 309 t.Fail() 310 } 311 312 if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { 313 fmt.Println("PASS | FOCUSED") 314 os.Exit(types.GINKGO_FOCUS_EXIT_CODE) 315 } 316 return passed 317 } 318 319 func extractSuiteConfiguration(args []interface{}) Labels { 320 suiteLabels := Labels{} 321 configErrors := []error{} 322 for _, arg := range args { 323 switch arg := arg.(type) { 324 case types.SuiteConfig: 325 suiteConfig = arg 326 case types.ReporterConfig: 327 reporterConfig = arg 328 case Labels: 329 suiteLabels = append(suiteLabels, arg...) 330 default: 331 configErrors = append(configErrors, types.GinkgoErrors.UnknownTypePassedToRunSpecs(arg)) 332 } 333 } 334 exitIfErrors(configErrors) 335 336 configErrors = types.VetConfig(flagSet, suiteConfig, reporterConfig) 337 if len(configErrors) > 0 { 338 fmt.Fprintf(formatter.ColorableStdErr, formatter.F("{{red}}Ginkgo detected configuration issues:{{/}}\n")) 339 for _, err := range configErrors { 340 fmt.Fprintf(formatter.ColorableStdErr, err.Error()) 341 } 342 os.Exit(1) 343 } 344 345 return suiteLabels 346 } 347 348 func getwd() (string, error) { 349 if !strings.EqualFold(os.Getenv("GINKGO_PRESERVE_CACHE"), "true") { 350 // Getwd calls os.Getenv("PWD"), which breaks test caching if the cache 351 // is shared between two different directories with the same test code. 352 return os.Getwd() 353 } 354 return "", nil 355 } 356 357 /* 358 PreviewSpecs walks the testing tree and produces a report without actually invoking the specs. 359 See http://onsi.github.io/ginkgo/#previewing-specs for more information. 360 */ 361 func PreviewSpecs(description string, args ...any) Report { 362 err := global.PushClone() 363 if err != nil { 364 exitIfErr(err) 365 } 366 defer global.PopClone() 367 368 suiteLabels := extractSuiteConfiguration(args) 369 priorDryRun, priorParallelTotal, priorParallelProcess := suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess 370 suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess = true, 1, 1 371 defer func() { 372 suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess = priorDryRun, priorParallelTotal, priorParallelProcess 373 }() 374 reporter := reporters.NoopReporter{} 375 outputInterceptor = internal.NoopOutputInterceptor{} 376 client = nil 377 writer := GinkgoWriter.(*internal.Writer) 378 379 err = global.Suite.BuildTree() 380 exitIfErr(err) 381 suitePath, err := getwd() 382 exitIfErr(err) 383 suitePath, err = filepath.Abs(suitePath) 384 exitIfErr(err) 385 386 global.Suite.Run(description, suiteLabels, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig) 387 388 return global.Suite.GetPreviewReport() 389 } 390 391 /* 392 Skip instructs Ginkgo to skip the current spec 393 394 You can call Skip in any Setup or Subject node closure. 395 396 For more on how to filter specs in Ginkgo see https://onsi.github.io/ginkgo/#filtering-specs 397 */ 398 func Skip(message string, callerSkip ...int) { 399 skip := 0 400 if len(callerSkip) > 0 { 401 skip = callerSkip[0] 402 } 403 cl := types.NewCodeLocationWithStackTrace(skip + 1) 404 global.Failer.Skip(message, cl) 405 panic(types.GinkgoErrors.UncaughtGinkgoPanic(cl)) 406 } 407 408 /* 409 Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.) 410 411 Under the hood, Fail panics to end execution of the current spec. Ginkgo will catch this panic and proceed with 412 the subsequent spec. If you call Fail, or make an assertion, within a goroutine launched by your spec you must 413 add defer GinkgoRecover() to the goroutine to catch the panic emitted by Fail. 414 415 You can call Fail in any Setup or Subject node closure. 416 417 You can learn more about how Ginkgo manages failures here: https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-handles-failure 418 */ 419 func Fail(message string, callerSkip ...int) { 420 skip := 0 421 if len(callerSkip) > 0 { 422 skip = callerSkip[0] 423 } 424 425 cl := types.NewCodeLocationWithStackTrace(skip + 1) 426 global.Failer.Fail(message, cl) 427 panic(types.GinkgoErrors.UncaughtGinkgoPanic(cl)) 428 } 429 430 /* 431 AbortSuite instructs Ginkgo to fail the current spec and skip all subsequent specs, thereby aborting the suite. 432 433 You can call AbortSuite in any Setup or Subject node closure. 434 435 You can learn more about how Ginkgo handles suite interruptions here: https://onsi.github.io/ginkgo/#interrupting-aborting-and-timing-out-suites 436 */ 437 func AbortSuite(message string, callerSkip ...int) { 438 skip := 0 439 if len(callerSkip) > 0 { 440 skip = callerSkip[0] 441 } 442 443 cl := types.NewCodeLocationWithStackTrace(skip + 1) 444 global.Failer.AbortSuite(message, cl) 445 panic(types.GinkgoErrors.UncaughtGinkgoPanic(cl)) 446 } 447 448 /* 449 ignorablePanic is used by Gomega to signal to GinkgoRecover that Goemga is handling 450 the error associated with this panic. It i used when Eventually/Consistently are passed a func(g Gomega) and the resulting function launches a goroutines that makes a failed assertion. That failed assertion is registered by Gomega and then panics. Ordinarily the panic is captured by Gomega. In the case of a goroutine Gomega can't capture the panic - so we piggy back on GinkgoRecover so users have a single defer GinkgoRecover() pattern to follow. To do that we need to tell Ginkgo to ignore this panic and not register it as a panic on the global Failer. 451 */ 452 type ignorablePanic interface{ GinkgoRecoverShouldIgnoreThisPanic() } 453 454 /* 455 GinkgoRecover should be deferred at the top of any spawned goroutine that (may) call `Fail` 456 Since Gomega assertions call fail, you should throw a `defer GinkgoRecover()` at the top of any goroutine that 457 calls out to Gomega 458 459 Here's why: Ginkgo's `Fail` method records the failure and then panics to prevent 460 further assertions from running. This panic must be recovered. Normally, Ginkgo recovers the panic for you, 461 however if a panic originates on a goroutine *launched* from one of your specs there's no 462 way for Ginkgo to rescue the panic. To do this, you must remember to `defer GinkgoRecover()` at the top of such a goroutine. 463 464 You can learn more about how Ginkgo manages failures here: https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-handles-failure 465 */ 466 func GinkgoRecover() { 467 e := recover() 468 if e != nil { 469 if _, ok := e.(ignorablePanic); ok { 470 return 471 } 472 global.Failer.Panic(types.NewCodeLocationWithStackTrace(1), e) 473 } 474 } 475 476 // pushNode is used by the various test construction DSL methods to push nodes onto the suite 477 // it handles returned errors, emits a detailed error message to help the user learn what they may have done wrong, then exits 478 func pushNode(node internal.Node, errors []error) bool { 479 exitIfErrors(errors) 480 exitIfErr(global.Suite.PushNode(node)) 481 return true 482 } 483 484 /* 485 Describe nodes are Container nodes that allow you to organize your specs. A Describe node's closure can contain any number of 486 Setup nodes (e.g. BeforeEach, AfterEach, JustBeforeEach), and Subject nodes (i.e. It). 487 488 Context and When nodes are aliases for Describe - use whichever gives your suite a better narrative flow. It is idomatic 489 to Describe the behavior of an object or function and, within that Describe, outline a number of Contexts and Whens. 490 491 You can learn more at https://onsi.github.io/ginkgo/#organizing-specs-with-container-nodes 492 In addition, container nodes can be decorated with a variety of decorators. You can learn more here: https://onsi.github.io/ginkgo/#decorator-reference 493 */ 494 func Describe(text string, args ...interface{}) bool { 495 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...)) 496 } 497 498 /* 499 FDescribe focuses specs within the Describe block. 500 */ 501 func FDescribe(text string, args ...interface{}) bool { 502 args = append(args, internal.Focus) 503 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...)) 504 } 505 506 /* 507 PDescribe marks specs within the Describe block as pending. 508 */ 509 func PDescribe(text string, args ...interface{}) bool { 510 args = append(args, internal.Pending) 511 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, text, args...)) 512 } 513 514 /* 515 XDescribe marks specs within the Describe block as pending. 516 517 XDescribe is an alias for PDescribe 518 */ 519 var XDescribe = PDescribe 520 521 /* Context is an alias for Describe - it generates the exact same kind of Container node */ 522 var Context, FContext, PContext, XContext = Describe, FDescribe, PDescribe, XDescribe 523 524 /* When is an alias for Describe - it generates the exact same kind of Container node */ 525 func When(text string, args ...interface{}) bool { 526 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...)) 527 } 528 529 /* When is an alias for Describe - it generates the exact same kind of Container node */ 530 func FWhen(text string, args ...interface{}) bool { 531 args = append(args, internal.Focus) 532 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...)) 533 } 534 535 /* When is an alias for Describe - it generates the exact same kind of Container node */ 536 func PWhen(text string, args ...interface{}) bool { 537 args = append(args, internal.Pending) 538 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeContainer, "when "+text, args...)) 539 } 540 541 var XWhen = PWhen 542 543 /* 544 It nodes are Subject nodes that contain your spec code and assertions. 545 546 Each It node corresponds to an individual Ginkgo spec. You cannot nest any other Ginkgo nodes within an It node's closure. 547 548 You can pass It nodes bare functions (func() {}) or functions that receive a SpecContext or context.Context: func(ctx SpecContext) {} and func (ctx context.Context) {}. If the function takes a context then the It is deemed interruptible and Ginkgo will cancel the context in the event of a timeout (configured via the SpecTimeout() or NodeTimeout() decorators) or of an interrupt signal. 549 550 You can learn more at https://onsi.github.io/ginkgo/#spec-subjects-it 551 In addition, subject nodes can be decorated with a variety of decorators. You can learn more here: https://onsi.github.io/ginkgo/#decorator-reference 552 */ 553 func It(text string, args ...interface{}) bool { 554 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...)) 555 } 556 557 /* 558 FIt allows you to focus an individual It. 559 */ 560 func FIt(text string, args ...interface{}) bool { 561 args = append(args, internal.Focus) 562 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...)) 563 } 564 565 /* 566 PIt allows you to mark an individual It as pending. 567 */ 568 func PIt(text string, args ...interface{}) bool { 569 args = append(args, internal.Pending) 570 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeIt, text, args...)) 571 } 572 573 /* 574 XIt allows you to mark an individual It as pending. 575 576 XIt is an alias for PIt 577 */ 578 var XIt = PIt 579 580 /* 581 Specify is an alias for It - it can allow for more natural wording in some context. 582 */ 583 var Specify, FSpecify, PSpecify, XSpecify = It, FIt, PIt, XIt 584 585 /* 586 By allows you to better document complex Specs. 587 588 Generally you should try to keep your Its short and to the point. This is not always possible, however, 589 especially in the context of integration tests that capture complex or lengthy workflows. 590 591 By allows you to document such flows. By may be called within a Setup or Subject node (It, BeforeEach, etc...) 592 and will simply log the passed in text to the GinkgoWriter. If By is handed a function it will immediately run the function. 593 594 By will also generate and attach a ReportEntry to the spec. This will ensure that By annotations appear in Ginkgo's machine-readable reports. 595 596 Note that By does not generate a new Ginkgo node - rather it is simply syntactic sugar around GinkgoWriter and AddReportEntry 597 You can learn more about By here: https://onsi.github.io/ginkgo/#documenting-complex-specs-by 598 */ 599 func By(text string, callback ...func()) { 600 exitIfErr(global.Suite.By(text, callback...)) 601 } 602 603 /* 604 BeforeSuite nodes are suite-level Setup nodes that run just once before any specs are run. 605 When running in parallel, each parallel process will call BeforeSuite. 606 607 You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. 608 609 BeforeSuite can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 610 611 You cannot nest any other Ginkgo nodes within a BeforeSuite node's closure. 612 You can learn more here: https://onsi.github.io/ginkgo/#suite-setup-and-cleanup-beforesuite-and-aftersuite 613 */ 614 func BeforeSuite(body interface{}, args ...interface{}) bool { 615 combinedArgs := []interface{}{body} 616 combinedArgs = append(combinedArgs, args...) 617 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeSuite, "", combinedArgs...)) 618 } 619 620 /* 621 AfterSuite nodes are suite-level Setup nodes run after all specs have finished - regardless of whether specs have passed or failed. 622 AfterSuite node closures always run, even if Ginkgo receives an interrupt signal (^C), in order to ensure cleanup occurs. 623 624 When running in parallel, each parallel process will call AfterSuite. 625 626 You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level. 627 628 AfterSuite can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 629 630 You cannot nest any other Ginkgo nodes within an AfterSuite node's closure. 631 You can learn more here: https://onsi.github.io/ginkgo/#suite-setup-and-cleanup-beforesuite-and-aftersuite 632 */ 633 func AfterSuite(body interface{}, args ...interface{}) bool { 634 combinedArgs := []interface{}{body} 635 combinedArgs = append(combinedArgs, args...) 636 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterSuite, "", combinedArgs...)) 637 } 638 639 /* 640 SynchronizedBeforeSuite nodes allow you to perform some of the suite setup just once - on parallel process #1 - and then pass information 641 from that setup to the rest of the suite setup on all processes. This is useful for performing expensive or singleton setup once, then passing 642 information from that setup to all parallel processes. 643 644 SynchronizedBeforeSuite accomplishes this by taking *two* function arguments and passing data between them. 645 The first function is only run on parallel process #1. The second is run on all processes, but *only* after the first function completes successfully. The functions have the following signatures: 646 647 The first function (which only runs on process #1) can have any of the following the signatures: 648 649 func() 650 func(ctx context.Context) 651 func(ctx SpecContext) 652 func() []byte 653 func(ctx context.Context) []byte 654 func(ctx SpecContext) []byte 655 656 The byte array returned by the first function (if present) is then passed to the second function, which can have any of the following signature: 657 658 func() 659 func(ctx context.Context) 660 func(ctx SpecContext) 661 func(data []byte) 662 func(ctx context.Context, data []byte) 663 func(ctx SpecContext, data []byte) 664 665 If either function receives a context.Context/SpecContext it is considered interruptible. 666 667 You cannot nest any other Ginkgo nodes within an SynchronizedBeforeSuite node's closure. 668 You can learn more, and see some examples, here: https://onsi.github.io/ginkgo/#parallel-suite-setup-and-cleanup-synchronizedbeforesuite-and-synchronizedaftersuite 669 */ 670 func SynchronizedBeforeSuite(process1Body interface{}, allProcessBody interface{}, args ...interface{}) bool { 671 combinedArgs := []interface{}{process1Body, allProcessBody} 672 combinedArgs = append(combinedArgs, args...) 673 674 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeSynchronizedBeforeSuite, "", combinedArgs...)) 675 } 676 677 /* 678 SynchronizedAfterSuite nodes complement the SynchronizedBeforeSuite nodes in solving the problem of splitting clean up into a piece that runs on all processes 679 and a piece that must only run once - on process #1. 680 681 SynchronizedAfterSuite accomplishes this by taking *two* function arguments. The first runs on all processes. The second runs only on parallel process #1 682 and *only* after all other processes have finished and exited. This ensures that process #1, and any resources it is managing, remain alive until 683 all other processes are finished. These two functions can be bare functions (func()) or interruptible (func(context.Context)/func(SpecContext)) 684 685 Note that you can also use DeferCleanup() in SynchronizedBeforeSuite to accomplish similar results. 686 687 You cannot nest any other Ginkgo nodes within an SynchronizedAfterSuite node's closure. 688 You can learn more, and see some examples, here: https://onsi.github.io/ginkgo/#parallel-suite-setup-and-cleanup-synchronizedbeforesuite-and-synchronizedaftersuite 689 */ 690 func SynchronizedAfterSuite(allProcessBody interface{}, process1Body interface{}, args ...interface{}) bool { 691 combinedArgs := []interface{}{allProcessBody, process1Body} 692 combinedArgs = append(combinedArgs, args...) 693 694 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeSynchronizedAfterSuite, "", combinedArgs...)) 695 } 696 697 /* 698 BeforeEach nodes are Setup nodes whose closures run before It node closures. When multiple BeforeEach nodes 699 are defined in nested Container nodes the outermost BeforeEach node closures are run first. 700 701 BeforeEach can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 702 703 You cannot nest any other Ginkgo nodes within a BeforeEach node's closure. 704 You can learn more here: https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach 705 */ 706 func BeforeEach(args ...interface{}) bool { 707 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeEach, "", args...)) 708 } 709 710 /* 711 JustBeforeEach nodes are similar to BeforeEach nodes, however they are guaranteed to run *after* all BeforeEach node closures - just before the It node closure. 712 This can allow you to separate configuration from creation of resources for a spec. 713 714 JustBeforeEach can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 715 716 You cannot nest any other Ginkgo nodes within a JustBeforeEach node's closure. 717 You can learn more and see some examples here: https://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach 718 */ 719 func JustBeforeEach(args ...interface{}) bool { 720 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeJustBeforeEach, "", args...)) 721 } 722 723 /* 724 AfterEach nodes are Setup nodes whose closures run after It node closures. When multiple AfterEach nodes 725 are defined in nested Container nodes the innermost AfterEach node closures are run first. 726 727 Note that you can also use DeferCleanup() in other Setup or Subject nodes to accomplish similar results. 728 729 AfterEach can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 730 731 You cannot nest any other Ginkgo nodes within an AfterEach node's closure. 732 You can learn more here: https://onsi.github.io/ginkgo/#spec-cleanup-aftereach-and-defercleanup 733 */ 734 func AfterEach(args ...interface{}) bool { 735 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterEach, "", args...)) 736 } 737 738 /* 739 JustAfterEach nodes are similar to AfterEach nodes, however they are guaranteed to run *before* all AfterEach node closures - just after the It node closure. This can allow you to separate diagnostics collection from teardown for a spec. 740 741 JustAfterEach can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 742 743 You cannot nest any other Ginkgo nodes within a JustAfterEach node's closure. 744 You can learn more and see some examples here: https://onsi.github.io/ginkgo/#separating-diagnostics-collection-and-teardown-justaftereach 745 */ 746 func JustAfterEach(args ...interface{}) bool { 747 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeJustAfterEach, "", args...)) 748 } 749 750 /* 751 BeforeAll nodes are Setup nodes that can occur inside Ordered containers. They run just once before any specs in the Ordered container run. 752 753 Multiple BeforeAll nodes can be defined in a given Ordered container however they cannot be nested inside any other container. 754 755 BeforeAll can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 756 757 You cannot nest any other Ginkgo nodes within a BeforeAll node's closure. 758 You can learn more about Ordered Containers at: https://onsi.github.io/ginkgo/#ordered-containers 759 And you can learn more about BeforeAll at: https://onsi.github.io/ginkgo/#setup-in-ordered-containers-beforeall-and-afterall 760 */ 761 func BeforeAll(args ...interface{}) bool { 762 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeBeforeAll, "", args...)) 763 } 764 765 /* 766 AfterAll nodes are Setup nodes that can occur inside Ordered containers. They run just once after all specs in the Ordered container have run. 767 768 Multiple AfterAll nodes can be defined in a given Ordered container however they cannot be nested inside any other container. 769 770 Note that you can also use DeferCleanup() in a BeforeAll node to accomplish similar behavior. 771 772 AfterAll can take a func() body, or an interruptible func(SpecContext)/func(context.Context) body. 773 774 You cannot nest any other Ginkgo nodes within an AfterAll node's closure. 775 You can learn more about Ordered Containers at: https://onsi.github.io/ginkgo/#ordered-containers 776 And you can learn more about AfterAll at: https://onsi.github.io/ginkgo/#setup-in-ordered-containers-beforeall-and-afterall 777 */ 778 func AfterAll(args ...interface{}) bool { 779 return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeAfterAll, "", args...)) 780 } 781 782 /* 783 DeferCleanup can be called within any Setup or Subject node to register a cleanup callback that Ginkgo will call at the appropriate time to cleanup after the spec. 784 785 DeferCleanup can be passed: 786 1. A function that takes no arguments and returns no values. 787 2. A function that returns multiple values. `DeferCleanup` will ignore all these return values except for the last one. If this last return value is a non-nil error `DeferCleanup` will fail the spec). 788 3. A function that takes a context.Context or SpecContext (and optionally returns multiple values). The resulting cleanup node is deemed interruptible and the passed-in context will be cancelled in the event of a timeout or interrupt. 789 4. A function that takes arguments (and optionally returns multiple values) followed by a list of arguments to pass to the function. 790 5. A function that takes SpecContext and a list of arguments (and optionally returns multiple values) followed by a list of arguments to pass to the function. 791 792 For example: 793 794 BeforeEach(func() { 795 DeferCleanup(os.Setenv, "FOO", os.GetEnv("FOO")) 796 os.Setenv("FOO", "BAR") 797 }) 798 799 will register a cleanup handler that will set the environment variable "FOO" to its current value (obtained by os.GetEnv("FOO")) after the spec runs and then sets the environment variable "FOO" to "BAR" for the current spec. 800 801 Similarly: 802 803 BeforeEach(func() { 804 DeferCleanup(func(ctx SpecContext, path) { 805 req, err := http.NewRequestWithContext(ctx, "POST", path, nil) 806 Expect(err).NotTo(HaveOccured()) 807 _, err := http.DefaultClient.Do(req) 808 Expect(err).NotTo(HaveOccured()) 809 }, "example.com/cleanup", NodeTimeout(time.Second*3)) 810 }) 811 812 will register a cleanup handler that will have three seconds to successfully complete a request to the specified path. Note that we do not specify a context in the list of arguments passed to DeferCleanup - only in the signature of the function we pass in. Ginkgo will detect the requested context and supply a SpecContext when it invokes the cleanup node. If you want to pass in your own context in addition to the Ginkgo-provided SpecContext you must specify the SpecContext as the first argument (e.g. func(ctx SpecContext, otherCtx context.Context)). 813 814 When DeferCleanup is called in BeforeEach, JustBeforeEach, It, AfterEach, or JustAfterEach the registered callback will be invoked when the spec completes (i.e. it will behave like an AfterEach node) 815 When DeferCleanup is called in BeforeAll or AfterAll the registered callback will be invoked when the ordered container completes (i.e. it will behave like an AfterAll node) 816 When DeferCleanup is called in BeforeSuite, SynchronizedBeforeSuite, AfterSuite, or SynchronizedAfterSuite the registered callback will be invoked when the suite completes (i.e. it will behave like an AfterSuite node) 817 818 Note that DeferCleanup does not represent a node but rather dynamically generates the appropriate type of cleanup node based on the context in which it is called. As such you must call DeferCleanup within a Setup or Subject node, and not within a Container node. 819 You can learn more about DeferCleanup here: https://onsi.github.io/ginkgo/#cleaning-up-our-cleanup-code-defercleanup 820 */ 821 func DeferCleanup(args ...interface{}) { 822 fail := func(message string, cl types.CodeLocation) { 823 global.Failer.Fail(message, cl) 824 } 825 pushNode(internal.NewCleanupNode(deprecationTracker, fail, args...)) 826 } 827 828 /* 829 AttachProgressReporter allows you to register a function that will be called whenever Ginkgo generates a Progress Report. The contents returned by the function will be included in the report. 830 831 **This is an experimental feature and the public-facing interface may change in a future minor version of Ginkgo** 832 833 Progress Reports are generated: 834 - whenever the user explicitly requests one (via `SIGINFO` or `SIGUSR1`) 835 - on nodes decorated with PollProgressAfter 836 - on suites run with --poll-progress-after 837 - whenever a test times out 838 839 Ginkgo uses Progress Reports to convey the current state of the test suite, including any running goroutines. By attaching a progress reporter you are able to supplement these reports with additional information. 840 841 # AttachProgressReporter returns a function that can be called to detach the progress reporter 842 843 You can learn more about AttachProgressReporter here: https://onsi.github.io/ginkgo/#attaching-additional-information-to-progress-reports 844 */ 845 func AttachProgressReporter(reporter func() string) func() { 846 return global.Suite.AttachProgressReporter(reporter) 847 } 848