vendor: bump github.com/onsi/ginkgo/v2 to v2.32.0

This update adds a a new command-line flag to tests, --sleep-on-failure,
which pauses execution at the first failed assertion. This gives time to
debug the state of the system before the After* hooks teardown any
state.

Signed-off-by: Casey Callendrello <c1@caseyc.net>
This commit is contained in:
Casey Callendrello
2026-07-09 14:19:52 +02:00
parent 864d210eb4
commit baed0ea45d
20 changed files with 389 additions and 18 deletions

2
go.mod
View File

@@ -40,7 +40,7 @@ require (
github.com/moby/ipvs v1.1.0
github.com/moby/sys/userns v0.1.0
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
github.com/onsi/ginkgo/v2 v2.28.3
github.com/onsi/ginkgo/v2 v2.32.0
github.com/onsi/gomega v1.40.0
github.com/opencontainers/cgroups v0.0.6
github.com/opencontainers/selinux v1.13.1

4
go.sum
View File

@@ -245,8 +245,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4=
github.com/onsi/ginkgo/v2 v2.28.3/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E=
github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc=
github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
github.com/opencontainers/cgroups v0.0.6 h1:tfZFWTIIGaUUFImTyuTg+Mr5x8XRiSdZESgEBW7UxuI=

View File

@@ -20,7 +20,7 @@ require (
github.com/lithammer/dedent v1.1.0
github.com/mitchellh/go-wordwrap v1.0.1
github.com/moby/term v0.5.2
github.com/onsi/ginkgo/v2 v2.28.3
github.com/onsi/ginkgo/v2 v2.32.0
github.com/onsi/gomega v1.40.0
github.com/russross/blackfriday/v2 v2.1.0
github.com/spf13/cobra v1.10.2

View File

@@ -141,8 +141,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4=
github.com/onsi/ginkgo/v2 v2.28.3/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E=
github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc=
github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=

View File

@@ -1,6 +1,7 @@
.DS_Store
TODO
tmp/**/*
integration/tmp_*/
*.coverprofile
.vscode
.idea/

View File

@@ -1,3 +1,34 @@
## 2.32.0
`-fd` generate RSpec-style documentation output. Thank @woodie !
--sleep-on-failure pauses a failed spec before teardown. Thanks @qinqon !
## 2.31.0
Add a bunch of Claude Skills via the marketplace:
```
/plugin marketplace add onsi/ginkgo
/plugin install ginkgo@ginkgo
```
## 2.30.0
### Features
Ginkgo now allows `extentions/global.Reset` to support running multiple suites from within a single process. This may take some massaging on your part (see [1672](https://github.com/onsi/ginkgo/issues/1672)) but can dramatically speed up codebases with O(hundreds) of test suites.
Thanks @lawrencejones !
### Fixes
- Fix nested --github-output group for progress report nested inside timeline [4f62d7a]
## 2.29.0
`GinkgoHelperGo` makes it easier to write test helpers that need to run in goroutines. Specifically, it makes managing the failure state and capturing failure panics correctly straightforward.
`ginkgo outline` now includes entries defined in `DescribeTableSubtree`
## 2.28.3
### Maintenance

View File

@@ -106,6 +106,19 @@ And that's just Ginkgo! [Gomega](https://onsi.github.io/gomega/) brings a rich,
Happy Testing!
## Using Ginkgo with Claude Code
Ginkgo ships a set of [Claude Code](https://claude.com/claude-code) skills as a plugin, with this repo doubling as the marketplace, so an agent writing specs in *your* project has Ginkgo's idioms, decorators, and gotchas on hand. From inside Claude Code:
```
/plugin marketplace add onsi/ginkgo
/plugin install ginkgo@ginkgo
```
(or non-interactively: `claude plugin marketplace add onsi/ginkgo` then `claude plugin install ginkgo@ginkgo`)
This installs a family of `ginkgo:*` skills that activate automatically while you write and run specs. Start with `ginkgo:overview`; see the [plugin README](plugins/ginkgo/README.md) for the full list.
## License
Ginkgo is MIT-Licensed

View File

@@ -39,7 +39,6 @@ var flagSet types.GinkgoFlagSet
var deprecationTracker = types.NewDeprecationTracker()
var suiteConfig = types.NewDefaultSuiteConfig()
var reporterConfig = types.NewDefaultReporterConfig()
var suiteDidRun = false
var outputInterceptor internal.OutputInterceptor
var client parallel_support.Client
@@ -259,10 +258,10 @@ for more on how specs are parallelized in Ginkgo.
You can also pass suite-level Label() decorators to RunSpecs. The passed-in labels will apply to all specs in the suite.
*/
func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
if suiteDidRun {
if global.SuiteDidRun {
exitIfErr(types.GinkgoErrors.RerunningSuite())
}
suiteDidRun = true
global.SuiteDidRun = true
err := global.PushClone()
if err != nil {
exitIfErr(err)

View File

@@ -163,17 +163,17 @@ func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackage
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "Context", "Describe", "When", "DescribeTable":
case "Context", "Describe", "When", "DescribeTable", "DescribeTableSubtree":
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
n.Pending = pendingFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "FContext", "FDescribe", "FWhen", "FDescribeTable":
case "FContext", "FDescribe", "FWhen", "FDescribeTable", "FDescribeTableSubtree":
n.Focused = true
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable":
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable", "PDescribeTableSubtree", "XDescribeTableSubtree":
n.Pending = true
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)

View File

@@ -54,6 +54,7 @@ func FromASTFile(fset *token.FileSet, src *ast.File) (*outline, error) {
// Node is not a Ginkgo spec or container, so it was not pushed onto the stack, continue
return true
}
expandSubtree(lastVisitedGinkgoNode)
stack = stack[0 : len(stack)-1]
return true
})
@@ -128,3 +129,29 @@ func (o *outline) StringIndent(width int) string {
return b.String()
}
// expandSubtree restructures a DescribeTableSubtree node so that each Entry
// child gets a copy of the subtree's spec nodes as its children. This mirrors
// the runtime behavior where each Entry generates a container with the specs
// defined in the DescribeTableSubtree body.
func expandSubtree(gn *ginkgoNode) {
if !strings.Contains(gn.Name, "DescribeTableSubtree") {
return
}
subNodes, entries := splitSubtreeSubnodes(gn.Nodes)
gn.Nodes = entries
for _, entry := range entries {
entry.Nodes = subNodes
}
}
// splitSubtreeSubnodes splits the child nodes of a DescribeTableSubtree into
// spec/container nodes (defined in the body) and Entry nodes.
func splitSubtreeSubnodes(nodes []*ginkgoNode) ([]*ginkgoNode, []*ginkgoNode) {
for i, node := range nodes {
if strings.Contains(node.Name, "Entry") {
return nodes[:i], nodes[i:]
}
}
return nodes, nil
}

View File

@@ -39,6 +39,10 @@ func BuildRunCommand() command.Command {
cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig)
command.AbortIfErrors("Ginkgo detected configuration issues:", errors)
if types.ReconcileFdOutputConfiguration(reporterConfig, &suiteConfig, &cliConfig) {
fmt.Println("--fd is incompatible with parallel runs (-p/-procs) and -randomize-all; ignoring those flags and running specs in series, in declaration order.")
}
runner := &SpecRunner{
cliConfig: cliConfig,
goFlagsConfig: goFlagsConfig,

View File

@@ -37,6 +37,10 @@ func BuildWatchCommand() command.Command {
cliConfig, goFlagsConfig, errors = types.VetAndInitializeCLIAndGoConfig(cliConfig, goFlagsConfig)
command.AbortIfErrors("Ginkgo detected configuration issues:", errors)
if types.ReconcileFdOutputConfiguration(reporterConfig, &suiteConfig, &cliConfig) {
fmt.Println("--fd is incompatible with parallel runs (-p/-procs) and -randomize-all; ignoring those flags and running specs in series, in declaration order.")
}
watcher := &SpecWatcher{
cliConfig: cliConfig,
goFlagsConfig: goFlagsConfig,

143
vendor/github.com/onsi/ginkgo/v2/helpergo_dsl.go generated vendored Normal file
View File

@@ -0,0 +1,143 @@
package ginkgo
import (
"github.com/onsi/ginkgo/v2/internal/global"
ginkgotypes "github.com/onsi/ginkgo/v2/types"
)
// GinkgoHelperGo synchronously calls the specified “helper” function in a new
// go routine and with a “defer GinkgoRecover()” already in place, passing the
// function a “helper Fail”. GinkgoHelperGo is typically called from custom test
// helpers that in turn need to synchronously execute caller-supplied custom
// test code in a new Go routine while waiting for this new Go routine to
// terminate (either successfully or failing).
//
// GinkgoHelperGo hides the non-trivial details of correctly unblocking the
// caller's waiting go routine as well as reporting the correct call sites,
// depending on whether the test helper failed, or the caller-supplied function
// had its assertions failing or panicked.
//
// Let's take the following example of a test helper named “EnsureSprockets”
// that runs a set of caller-supplied assertions synchronously on a new Go
// routine and waits for the outcome before returning to the caller of the test
// helper. This is just using Ginkgo:
//
// func EnsureSprockets(sprockets int, assertions func()) {
// GinkgoHelper()
// GinkgoHelperGo(func(helperFail func(string, ...int)) {
// if sprockets == 0 {
// helperFail("sprockets must not be zero")
// }
// assertions()
// })
// }
//
// And now for an example that additionally uses Gomega assertions.
//
// func EnsureSprockets(sprockets int, assertions func()) {
// GinkgoHelper()
// GinkgoHelperGo(func(helperFail func(string, ...int)) {
// g := gomega.NewGomega(helperFail)
// g.Expect(sprockets).Not(BeZero())
// assertions()
// })
// }
//
// The called helper function should make any custom helper-related assertions
// using the passed “helper Fail”. Gomega users will want to create a new Gomega
// wired into this helper Fail. It is expected for the helper function at some
// point to call into a user-supplied function that might contain its own
// assertions. In the example above, that would be the function passed as
// assertions.
//
// Any failing assertion using the helper Gomega in the helper function will be
// reported as a fail at the call site of GinkgoHelperGo. Preferably, only
// custom test helpers call GinkgoHelperGo and thus mark themselves as
// [GinkgoHelper] also: in this case, the fail will be shown at the call site of
// the custom test helper.
//
// Any other failing assertions inside the caller-supplied custom test code and
// thus inside the helper function will instead be reported at the location of
// the failed assertion.
//
// If the caller-supplied custom test code panics, GinkgoHelperGo will fail at
// its call site, or at the call site of the custom test helper if it uses
// GinkgoHelper, reporting the usual stack trace for the panic, as a plain
// GinkgoRecover would also do.
//
// Important: the Gomega passed to the called function must only be used in
// assertions belonging to the test helper, but not any user test code called
// from the test helper. Thus, do not pass the Gomega passed to the helper
// function further on to any user test code functions.
func GinkgoHelperGo(fn func(fail func(message string, callerSkip ...int))) {
// userPanicked signals that the called user code panicked, such as due to a
// failed Gomega assertion.
type userPanicked struct{}
// helperPanicked signals that some helper code assertion panicked in the
// separate Go routine and we are expected to Fail the current test with that
// reason, but on the caller's Go routine.
type helperPanicked string
GinkgoHelper()
// possible types of values sent over the result channel:
// - nil (untyped): no problem at all, proceed.
// - helperPanicked: the message with which to (re)fail in the caller's
// go routine.
// - userPanicked: indication to (also) fail on the caller's go routine;
// the message doesn't matter as the user code fail takes precedence.
ch := make(chan any)
go func() {
isHelperPanic := false
helperFail := func(message string, callerSkip ...int) {
isHelperPanic = true
Fail(message, callerSkip...)
}
// Please note that we cannot simply recover a helper panic before
// GinkgoRecover kicks in as then GinkgoRecover would always report the
// stack trace only from the place of rethrown panic ... and that's
// pretty useless, because it would just consist of the panic rethrow.
defer func() {
// We need to unblock and immediately fail the waiting caller's
// go routine either for a reason, or just "because" when
// GinkgoRecover has already failed the current test on the
// separate go routine.
if global.Failer.GetState() != ginkgotypes.SpecStatePassed {
if isHelperPanic {
_, failure := global.Failer.Drain()
ch <- helperPanicked(failure.Message)
} else {
// keep the panic failure already recorded by GinkgoRecover.
ch <- userPanicked{}
}
}
close(ch) // causes a nil in case there were no panics anywhere.
}()
// Nota bene: GinkgoRecover always eats any user panic and channel the
// panic value into Ginkgo's Failer.Panic(). We can peek at the last
// failure recorded, which should be nil if GinkgoRecover didn't swallow
// a user code panic. The "problem" with GinkgoRecover is that it turns
// any panic value into a string message, so we loose any specific
// typing.
defer GinkgoRecover()
fn(helperFail)
}()
// Did we run into trouble?
switch v := (<-ch).(type) {
case userPanicked:
// The message actually is irrelevant, as it comes only second to
// the already registered user panic message. We just need Fail to
// panic on the caller's go routine in order to unblock the test.
Fail("fn panicked", 1)
case helperPanicked:
// Report the failure on the new go routine instead on the caller's go
// routine.
Fail(string(v), 1)
default:
// It's all fine!
}
}

View File

@@ -8,6 +8,12 @@ var Suite *internal.Suite
var Failer *internal.Failer
var backupSuite *internal.Suite
// SuiteDidRun tracks whether RunSpecs has already been invoked for the current global
// suite. It lives here (rather than in package ginkgo) so that InitializeGlobals can
// clear it, allowing extensions/globals.Reset to support running multiple suites
// sequentially in a single process.
var SuiteDidRun bool
func init() {
InitializeGlobals()
}
@@ -15,6 +21,7 @@ func init() {
func InitializeGlobals() {
Failer = internal.NewFailer()
Suite = internal.NewSuite()
SuiteDidRun = false
}
func PushClone() error {

View File

@@ -996,6 +996,7 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
} else {
failure.Message, failure.Location, failure.ForwardedPanic, failure.TimelineLocation = failureFromRun.Message, failureFromRun.Location, failureFromRun.ForwardedPanic, failureFromRun.TimelineLocation
suite.reporter.EmitFailure(outcomeFromRun, failure)
suite.pauseOnFailureIfRequested(node)
return outcomeFromRun, failure
}
case <-gracePeriodChannel:
@@ -1079,6 +1080,42 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
}
}
// pauseOnFailureIfRequested pauses the suite at the moment a failure is identified,
// when the user has set --sleep-on-failure. This hooks directly into runNode's failure
// path so the pause happens immediately at the point of failure - before any teardown
// or cleanup runs - leaving the system live for inspection.
//
// We only pause for failures in setup and subject nodes (It, Before*, BeforeSuite...),
// i.e. nodes that run before teardown. Pausing on a failure in a teardown/cleanup or
// reporting node would be pointless (the system is already being torn down) and could
// interfere with interrupt handling, so those are skipped.
//
// The pause is interruptible: pressing ^C (or any interrupt) ends the pause early and
// the suite proceeds to run cleanup as usual. It is a no-op if the feature is disabled.
func (suite *Suite) pauseOnFailureIfRequested(node Node) {
if suite.config.SleepOnFailure <= 0 {
return
}
// only pause before teardown - skip teardown/cleanup/reporting nodes
if node.NodeType.Is(types.NodeTypesAllowedDuringCleanupInterrupt | types.NodeTypesAllowedDuringReportInterrupt) {
return
}
duration := suite.config.SleepOnFailure
report := suite.generateProgressReport(false)
report.Message = fmt.Sprintf("{{bold}}{{orange}}Paused on failure for up to %s.{{/}}\nThe spec failed and Ginkgo has paused before running any teardown so you can inspect the live system.\nPress {{bold}}^C{{/}} to end the pause and proceed to cleanup.", duration)
suite.emitProgressReport(report)
timer := time.NewTimer(duration)
defer timer.Stop()
// wait for the pause to elapse, or for the user to interrupt - in which case we end
// the pause early and let runNode return so cleanup can proceed
select {
case <-timer.C:
case <-suite.interruptHandler.Status().Channel:
}
}
// TODO: search for usages and consider if reporter.EmitFailure() is necessary
func (suite *Suite) failureForLeafNodeWithMessage(node Node, message string) types.Failure {
return types.Failure{

View File

@@ -31,6 +31,7 @@ type DefaultReporter struct {
specDenoter string
retryDenoter string
formatter formatter.Formatter
fdHierarchy []string
runningInParallel bool
lock *sync.Mutex
@@ -82,6 +83,8 @@ func (r *DefaultReporter) SuiteWillBegin(report types.Report) {
if report.SuiteConfig.ParallelTotal > 1 {
r.emit(r.f("- %d procs ", report.SuiteConfig.ParallelTotal))
}
} else if r.conf.FdOutput {
return
} else {
banner := r.f("Running Suite: %s - %s", report.SuiteDescription, report.SuitePath)
r.emitBlock(banner)
@@ -124,7 +127,7 @@ func (r *DefaultReporter) SuiteWillBegin(report types.Report) {
func (r *DefaultReporter) SuiteDidEnd(report types.Report) {
failures := report.SpecReports.WithState(types.SpecStateFailureStates)
if len(failures) > 0 {
if !r.conf.FdOutput && len(failures) > 0 {
r.emitBlock("\n")
if len(failures) > 1 {
r.emitBlock(r.f("{{red}}{{bold}}Summarizing %d Failures:{{/}}", len(failures)))
@@ -227,6 +230,10 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
return
}
if r.conf.FdOutput {
r.didRunFd(report)
return
}
header := r.specDenoter
if report.LeafNodeType.Is(types.NodeTypesForSuiteLevelNodes) {
header = fmt.Sprintf("[%s]", report.LeafNodeType)
@@ -358,6 +365,51 @@ func (r *DefaultReporter) DidRun(report types.SpecReport) {
r.emitDelimiter(0)
}
func (r *DefaultReporter) didRunFd(report types.SpecReport) {
r.lock.Lock()
defer r.lock.Unlock()
if !report.LeafNodeType.Is(types.NodeTypeIt) {
return
}
hierarchy := report.ContainerHierarchyTexts
// blank line when top-level container changes
if len(r.fdHierarchy) > 0 &&
(len(hierarchy) == 0 || hierarchy[0] != r.fdHierarchy[0]) {
fmt.Fprintln(r.writer)
}
// emit newly-diverged container lines
divergeAt := 0
for divergeAt < len(r.fdHierarchy) && divergeAt < len(hierarchy) &&
r.fdHierarchy[divergeAt] == hierarchy[divergeAt] {
divergeAt++
}
for i := divergeAt; i < len(hierarchy); i++ {
fmt.Fprintf(r.writer, "%s%s\n", strings.Repeat(" ", i), hierarchy[i])
}
// leaf label
depth := len(hierarchy)
indent := strings.Repeat(" ", depth)
label := report.LeafNodeText
switch report.State {
case types.SpecStateFailed, types.SpecStatePanicked:
label = fmt.Sprintf("%s (FAILED)", label)
case types.SpecStatePending:
label = fmt.Sprintf("%s (PENDING)", label)
case types.SpecStateSkipped:
label = fmt.Sprintf("%s (SKIPPED)", label)
}
color := r.highlightColorForState(report.State)
fmt.Fprintf(r.writer, "%s%s\n", indent, r.f(color+"%s{{/}}", label))
r.fdHierarchy = hierarchy
}
func (r *DefaultReporter) highlightColorForState(state types.SpecState) string {
switch state {
case types.SpecStatePassed:
@@ -423,7 +475,7 @@ func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, tim
case types.ReportEntry:
r.emitReportEntry(indent, x)
case types.ProgressReport:
r.emitProgressReport(indent, false, isVeryVerbose, x)
r.emitProgressReport(indent, isVeryVerbose, false, x)
case types.SpecEvent:
if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents {
r.emitSpecEvent(indent, x, isVeryVerbose)
@@ -533,6 +585,7 @@ func (r *DefaultReporter) emitProgressReport(indent uint, emitGinkgoWriterOutput
indent -= 1
}
// Emit only top-level groups because github logging cannot handle nested groups correctly.
if r.conf.GithubOutput && emitGroup {
r.emitBlock(r.fi(indent, "::group::Progress Report"))
}

View File

@@ -38,6 +38,7 @@ type SuiteConfig struct {
OutputInterceptorMode string
SourceRoots []string
GracePeriod time.Duration
SleepOnFailure time.Duration
ParallelProcess int
ParallelTotal int
@@ -94,6 +95,7 @@ type ReporterConfig struct {
GithubOutput bool
SilenceSkips bool
ForceNewlines bool
FdOutput bool
JSONReport string
GoJSONReport string
@@ -293,6 +295,8 @@ var SuiteConfigFlags = GinkgoFlags{
Usage: "Make up to this many attempts to run each spec. If any of the attempts succeed, the suite will not be failed."},
{KeyPath: "S.FailOnEmpty", Name: "fail-on-empty", SectionKey: "failure",
Usage: "If set, ginkgo will mark the test suite as failed if no specs are run."},
{KeyPath: "S.SleepOnFailure", Name: "sleep-on-failure", SectionKey: "failure", UsageDefaultValue: "0 - disabled",
Usage: "If set, ginkgo will pause for this duration after a spec fails - before its teardown (AfterEach/JustAfterEach/DeferCleanup) runs - so you can inspect the live system. Press ^C to end the pause early and proceed to cleanup. Serial only: cannot be combined with -p/--procs."},
{KeyPath: "S.DryRun", Name: "dry-run", SectionKey: "debug", DeprecatedName: "dryRun", DeprecatedDocLink: "changed-command-line-flags",
Usage: "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v."},
@@ -358,7 +362,8 @@ var ReporterConfigFlags = GinkgoFlags{
Usage: "If set, default reporter will not print out skipped tests."},
{KeyPath: "R.ForceNewlines", Name: "force-newlines", SectionKey: "output",
Usage: "If set, default reporter will ensure a newline appears after each test."},
{KeyPath: "R.FdOutput", Name: "fd", SectionKey: "output",
Usage: "If set, emits RSpec-style 'format documentation' output instead of Ginkgo's default output. --fd is exclusive: it overrides -p/-procs and -randomize-all, forcing specs to run serially in declaration order, since fd's hierarchical output can't be rendered sensibly when specs are parallelized or randomized."},
{KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output",
Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."},
{KeyPath: "R.GoJSONReport", Name: "gojson-report", UsageArgument: "filename.json", SectionKey: "output",
@@ -429,6 +434,14 @@ func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig Re
errors = append(errors, GinkgoErrors.GracePeriodCannotBeZero())
}
if suiteConfig.SleepOnFailure < 0 {
errors = append(errors, GinkgoErrors.InvalidSleepOnFailureConfiguration())
}
if suiteConfig.SleepOnFailure > 0 && suiteConfig.ParallelTotal > 1 {
errors = append(errors, GinkgoErrors.SleepOnFailureInParallelConfiguration())
}
if len(suiteConfig.FocusFiles) > 0 {
_, err := ParseFileFilters(suiteConfig.FocusFiles)
if err != nil {
@@ -476,6 +489,30 @@ func VetConfig(flagSet GinkgoFlagSet, suiteConfig SuiteConfig, reporterConfig Re
return errors
}
// ReconcileFdOutputConfiguration forces serial, in-order execution when --fd is combined with
// -p, -procs (>1), or -randomize-all. fd's RSpec-style hierarchical output assumes specs run
// one at a time, in declaration order -- parallelism and randomization both break that assumption
// and produce fragmented, hard-to-read output. Rather than refusing to run, Ginkgo ignores those
// flags and runs in series instead. suiteConfig and cliConfig are mutated in place; the return
// value is true if anything was overridden, so callers can let the user know what was ignored.
func ReconcileFdOutputConfiguration(reporterConfig ReporterConfig, suiteConfig *SuiteConfig, cliConfig *CLIConfig) bool {
if !reporterConfig.FdOutput {
return false
}
changed := false
if cliConfig.ComputedProcs() > 1 {
cliConfig.Procs = 1
cliConfig.Parallel = false
changed = true
}
if suiteConfig.RandomizeAllSpecs {
suiteConfig.RandomizeAllSpecs = false
changed = true
}
return changed
}
// GinkgoCLISharedFlags provides flags shared by the Ginkgo CLI's build, watch, and run commands
var GinkgoCLISharedFlags = GinkgoFlags{
{KeyPath: "C.Recurse", Name: "r", SectionKey: "multiple-suites",

View File

@@ -455,7 +455,7 @@ func (g ginkgoErrors) InvalidEmptyComponentForSemVerConstraint(cl CodeLocation)
Heading: "Invalid Empty Component for ComponentSemVerConstraint",
Message: "ComponentSemVerConstraint requires a non-empty component name",
CodeLocation: cl,
DocLink: "spec-semantic-version-filtering",
DocLink: "spec-semantic-version-filtering",
}
}
@@ -621,6 +621,21 @@ func (g ginkgoErrors) GracePeriodCannotBeZero() error {
}
}
func (g ginkgoErrors) InvalidSleepOnFailureConfiguration() error {
return GinkgoError{
Heading: "Ginkgo requires a non-negative --sleep-on-failure.",
Message: "Please set --sleep-on-failure to a positive duration (e.g. 5m), or 0 to disable it.",
}
}
func (g ginkgoErrors) SleepOnFailureInParallelConfiguration() error {
return GinkgoError{
Heading: "Ginkgo only supports --sleep-on-failure in serial mode.",
Message: "--sleep-on-failure pauses a failed spec on a live system for inspection, which only makes sense when the suite runs serially. Please run again without -p or --procs, or unset --sleep-on-failure.",
DocLink: "spec-timeouts-and-interruptible-nodes",
}
}
func (g ginkgoErrors) ConflictingVerbosityConfiguration() error {
return GinkgoError{
Heading: "Conflicting reporter verbosity settings.",

View File

@@ -1,3 +1,3 @@
package types
const VERSION = "2.28.3"
const VERSION = "2.32.0"

2
vendor/modules.txt vendored
View File

@@ -421,7 +421,7 @@ github.com/munnerz/goautoneg
# github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f
## explicit
github.com/mxk/go-flowrate/flowrate
# github.com/onsi/ginkgo/v2 v2.28.3
# github.com/onsi/ginkgo/v2 v2.32.0
## explicit; go 1.25.0
github.com/onsi/ginkgo/v2
github.com/onsi/ginkgo/v2/config