...

Source file src/github.com/onsi/ginkgo/v2/core_dsl.go

Documentation: github.com/onsi/ginkgo/v2

     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  

View as plain text