Merge pull request #26880 from containers/renovate/github.com-onsi-ginkgo-v2-2.x

fix(deps): update module github.com/onsi/ginkgo/v2 to v2.25.0
This commit is contained in:
openshift-merge-bot[bot]
2025-08-21 13:13:55 +00:00
committed by GitHub
12 changed files with 222 additions and 15 deletions

View File

@ -1,3 +1,29 @@
## 2.25.0
### `AroundNode`
This release introduces a new decorator to support more complex spec setup usecases.
`AroundNode` registers a function that runs before each individual node. This is considered a more advanced decorator.
Please read the [docs](https://onsi.github.io/ginkgo/#advanced-around-node) for more information and some examples.
Allowed signatures:
- `AroundNode(func())` - `func` will be called before the node is run.
- `AroundNode(func(ctx context.Context) context.Context)` - `func` can wrap the passed in context and return a new one which will be passed on to the node.
- `AroundNode(func(ctx context.Context, body func(ctx context.Context)))` - `ctx` is the context for the node and `body` is a function that must be called to run the node. This gives you complete control over what runs before and after the node.
Multiple `AroundNode` decorators can be applied to a single node and they will run in the order they are applied.
Unlike setup nodes like `BeforeEach` and `DeferCleanup`, `AroundNode` is guaranteed to run in the same goroutine as the decorated node. This is necessary when working with lower-level libraries that must run on a single thread (you can call `runtime.LockOSThread()` in the `AroundNode` to ensure that the node runs on a single thread).
Since `AroundNode` allows you to modify the context you can also use `AroundNode` to implement shared setup that attaches values to the context.
If applied to a container, `AroundNode` will run before every node in the container. Including setup nodes like `BeforeEach` and `DeferCleanup`.
`AroundNode` can also be applied to `RunSpecs` to run before every node in the suite. This opens up new mechanisms for instrumenting individual nodes across an entire suite.
## 2.24.0
### Features

View File

@ -268,7 +268,7 @@ func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
}
defer global.PopClone()
suiteLabels, suiteSemVerConstraints := extractSuiteConfiguration(args)
suiteLabels, suiteSemVerConstraints, suiteAroundNodes := extractSuiteConfiguration(args)
var reporter reporters.Reporter
if suiteConfig.ParallelTotal == 1 {
@ -311,7 +311,7 @@ func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
suitePath, err = filepath.Abs(suitePath)
exitIfErr(err)
passed, hasFocusedTests := global.Suite.Run(description, suiteLabels, suiteSemVerConstraints, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig)
passed, hasFocusedTests := global.Suite.Run(description, suiteLabels, suiteSemVerConstraints, suiteAroundNodes, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig)
outputInterceptor.Shutdown()
flagSet.ValidateDeprecations(deprecationTracker)
@ -330,9 +330,10 @@ func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
return passed
}
func extractSuiteConfiguration(args []any) (Labels, SemVerConstraints) {
func extractSuiteConfiguration(args []any) (Labels, SemVerConstraints, types.AroundNodes) {
suiteLabels := Labels{}
suiteSemVerConstraints := SemVerConstraints{}
aroundNodes := types.AroundNodes{}
configErrors := []error{}
for _, arg := range args {
switch arg := arg.(type) {
@ -344,6 +345,8 @@ func extractSuiteConfiguration(args []any) (Labels, SemVerConstraints) {
suiteLabels = append(suiteLabels, arg...)
case SemVerConstraints:
suiteSemVerConstraints = append(suiteSemVerConstraints, arg...)
case types.AroundNodeDecorator:
aroundNodes = append(aroundNodes, arg)
default:
configErrors = append(configErrors, types.GinkgoErrors.UnknownTypePassedToRunSpecs(arg))
}
@ -359,7 +362,7 @@ func extractSuiteConfiguration(args []any) (Labels, SemVerConstraints) {
os.Exit(1)
}
return suiteLabels, suiteSemVerConstraints
return suiteLabels, suiteSemVerConstraints, aroundNodes
}
func getwd() (string, error) {
@ -382,7 +385,7 @@ func PreviewSpecs(description string, args ...any) Report {
}
defer global.PopClone()
suiteLabels, suiteSemVerConstraints := extractSuiteConfiguration(args)
suiteLabels, suiteSemVerConstraints, suiteAroundNodes := extractSuiteConfiguration(args)
priorDryRun, priorParallelTotal, priorParallelProcess := suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess
suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess = true, 1, 1
defer func() {
@ -400,7 +403,7 @@ func PreviewSpecs(description string, args ...any) Report {
suitePath, err = filepath.Abs(suitePath)
exitIfErr(err)
global.Suite.Run(description, suiteLabels, suiteSemVerConstraints, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig)
global.Suite.Run(description, suiteLabels, suiteSemVerConstraints, suiteAroundNodes, suitePath, global.Failer, reporter, writer, outputInterceptor, interrupt_handler.NewInterruptHandler(client), client, internal.RegisterForProgressSignal, suiteConfig)
return global.Suite.GetPreviewReport()
}

View File

@ -2,6 +2,7 @@ package ginkgo
import (
"github.com/onsi/ginkgo/v2/internal"
"github.com/onsi/ginkgo/v2/types"
)
/*
@ -158,3 +159,28 @@ SuppressProgressReporting is a decorator that allows you to disable progress rep
if you have a `ReportAfterEach` node that is running for every skipped spec and is generating lots of progress reports.
*/
const SuppressProgressReporting = internal.SuppressProgressReporting
/*
AroundNode registers a function that runs before each individual node. This is considered a more advanced decorator.
Please read the [docs](https://onsi.github.io/ginkgo/#advanced-around-node) for more information.
Allowed signatures:
- AroundNode(func()) - func will be called before the node is run.
- AroundNode(func(ctx context.Context) context.Context) - func can wrap the passed in context and return a new one which will be passed on to the node.
- AroundNode(func(ctx context.Context, body func(ctx context.Context))) - ctx is the context for the node and body is a function that must be called to run the node. This gives you complete control over what runs before and after the node.
Multiple AroundNode decorators can be applied to a single node and they will run in the order they are applied.
Unlike setup nodes like BeforeEach and DeferCleanup, AroundNode is guaranteed to run in the same goroutine as the decorated node. This is necessary when working with lower-level libraries that must run on a single thread (you can call runtime.LockOSThread() in the AroundNode to ensure that the node runs on a single thread).
Since AroundNode allows you to modify the context you can also use AroundNode to implement shared setup that attaches values to the context. You must return a context that inherits from the passed in context.
If applied to a container, AroundNode will run before every node in the container. Including setup nodes like BeforeEach and DeferCleanup.
AroundNode can also be applied to RunSpecs to run before every node in the suite.
*/
func AroundNode[F types.AroundNodeAllowedFuncs](f F) types.AroundNodeDecorator {
return types.AroundNode(f, types.NewCodeLocation(1))
}

View File

@ -0,0 +1,34 @@
package internal
import (
"github.com/onsi/ginkgo/v2/types"
)
func ComputeAroundNodes(specs Specs) Specs {
out := Specs{}
for _, spec := range specs {
nodes := Nodes{}
currentNestingLevel := 0
aroundNodes := types.AroundNodes{}
nestingLevelIndices := []int{}
for _, node := range spec.Nodes {
switch node.NodeType {
case types.NodeTypeContainer:
currentNestingLevel = node.NestingLevel + 1
nestingLevelIndices = append(nestingLevelIndices, len(aroundNodes))
aroundNodes = aroundNodes.Append(node.AroundNodes...)
nodes = append(nodes, node)
default:
if currentNestingLevel > node.NestingLevel {
currentNestingLevel = node.NestingLevel
aroundNodes = aroundNodes[:nestingLevelIndices[currentNestingLevel]]
}
node.AroundNodes = types.AroundNodes{}.Append(aroundNodes...).Append(node.AroundNodes...)
nodes = append(nodes, node)
}
}
spec.Nodes = nodes
out = append(out, spec)
}
return out
}

View File

@ -61,6 +61,7 @@ type Node struct {
NodeTimeout time.Duration
SpecTimeout time.Duration
GracePeriod time.Duration
AroundNodes types.AroundNodes
NodeIDWhereCleanupWasGenerated uint
}
@ -86,18 +87,20 @@ type FlakeAttempts uint
type MustPassRepeatedly uint
type Offset uint
type Done chan<- any // Deprecated Done Channel for asynchronous testing
type Labels []string
type SemVerConstraints []string
type PollProgressInterval time.Duration
type PollProgressAfter time.Duration
type NodeTimeout time.Duration
type SpecTimeout time.Duration
type GracePeriod time.Duration
type Labels []string
func (l Labels) MatchesLabelFilter(query string) bool {
return types.MustParseLabelFilter(query)(l)
}
type SemVerConstraints []string
func (svc SemVerConstraints) MatchesSemVerFilter(version string) bool {
return types.MustParseSemVerFilter(version)(svc)
}
@ -177,6 +180,8 @@ func isDecoration(arg any) bool {
return true
case t == reflect.TypeOf(GracePeriod(0)):
return true
case t == reflect.TypeOf(types.AroundNodeDecorator{}):
return true
case t.Kind() == reflect.Slice && isSliceOfDecorations(arg):
return true
default:
@ -317,6 +322,8 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
if nodeType.Is(types.NodeTypeContainer) {
appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "GracePeriod"))
}
case t == reflect.TypeOf(types.AroundNodeDecorator{}):
node.AroundNodes = append(node.AroundNodes, arg.(types.AroundNodeDecorator))
case t == reflect.TypeOf(Labels{}):
if !nodeType.Is(types.NodeTypesForContainerAndIt) {
appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Label"))

View File

@ -2,6 +2,7 @@ package internal
import (
"context"
"reflect"
"github.com/onsi/ginkgo/v2/types"
)
@ -11,6 +12,7 @@ type SpecContext interface {
SpecReport() types.SpecReport
AttachProgressReporter(func() string) func()
WrappedContext() context.Context
}
type specContext struct {
@ -45,3 +47,28 @@ func NewSpecContext(suite *Suite) *specContext {
func (sc *specContext) SpecReport() types.SpecReport {
return sc.suite.CurrentSpecReport()
}
func (sc *specContext) WrappedContext() context.Context {
return sc.Context
}
/*
The user is allowed to wrap `SpecContext` in a new context.Context when using AroundNodes. But body functions expect SpecContext.
We support this by taking their context.Context and returning a SpecContext that wraps it.
*/
func wrapContextChain(ctx context.Context) SpecContext {
if ctx == nil {
return nil
}
if reflect.TypeOf(ctx) == reflect.TypeOf(&specContext{}) {
return ctx.(*specContext)
} else if sc, ok := ctx.Value("GINKGO_SPEC_CONTEXT").(*specContext); ok {
return &specContext{
Context: ctx,
ProgressReporterManager: sc.ProgressReporterManager,
cancel: sc.cancel,
suite: sc.suite,
}
}
return nil
}

View File

@ -32,6 +32,7 @@ type Suite struct {
suiteNodes Nodes
cleanupNodes Nodes
aroundNodes types.AroundNodes
failer *Failer
reporter reporters.Reporter
@ -87,6 +88,7 @@ func (suite *Suite) Clone() (*Suite, error) {
ProgressReporterManager: NewProgressReporterManager(),
topLevelContainers: suite.topLevelContainers.Clone(),
suiteNodes: suite.suiteNodes.Clone(),
aroundNodes: suite.aroundNodes.Clone(),
selectiveLock: &sync.Mutex{},
}, nil
}
@ -104,13 +106,14 @@ func (suite *Suite) BuildTree() error {
return nil
}
func (suite *Suite) Run(description string, suiteLabels Labels, suiteSemVerConstraints SemVerConstraints, suitePath string, failer *Failer, reporter reporters.Reporter, writer WriterInterface, outputInterceptor OutputInterceptor, interruptHandler interrupt_handler.InterruptHandlerInterface, client parallel_support.Client, progressSignalRegistrar ProgressSignalRegistrar, suiteConfig types.SuiteConfig) (bool, bool) {
func (suite *Suite) Run(description string, suiteLabels Labels, suiteSemVerConstraints SemVerConstraints, suiteAroundNodes types.AroundNodes, suitePath string, failer *Failer, reporter reporters.Reporter, writer WriterInterface, outputInterceptor OutputInterceptor, interruptHandler interrupt_handler.InterruptHandlerInterface, client parallel_support.Client, progressSignalRegistrar ProgressSignalRegistrar, suiteConfig types.SuiteConfig) (bool, bool) {
if suite.phase != PhaseBuildTree {
panic("cannot run before building the tree = call suite.BuildTree() first")
}
ApplyNestedFocusPolicyToTree(suite.tree)
specs := GenerateSpecsFromTreeRoot(suite.tree)
specs, hasProgrammaticFocus := ApplyFocusToSpecs(specs, description, suiteLabels, suiteSemVerConstraints, suiteConfig)
specs = ComputeAroundNodes(specs)
suite.phase = PhaseRun
suite.client = client
@ -120,6 +123,7 @@ func (suite *Suite) Run(description string, suiteLabels Labels, suiteSemVerConst
suite.outputInterceptor = outputInterceptor
suite.interruptHandler = interruptHandler
suite.config = suiteConfig
suite.aroundNodes = suiteAroundNodes
if suite.config.Timeout > 0 {
suite.deadline = time.Now().Add(suite.config.Timeout)
@ -259,6 +263,7 @@ func (suite *Suite) pushCleanupNode(node Node) error {
node.NodeIDWhereCleanupWasGenerated = suite.currentNode.ID
node.NestingLevel = suite.currentNode.NestingLevel
node.AroundNodes = types.AroundNodes{}.Append(suite.currentNode.AroundNodes...).Append(node.AroundNodes...)
suite.selectiveLock.Lock()
suite.cleanupNodes = append(suite.cleanupNodes, node)
suite.selectiveLock.Unlock()
@ -892,7 +897,30 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
failureC <- failureFromRun
}()
node.Body(sc)
aroundNodes := types.AroundNodes{}.Append(suite.aroundNodes...).Append(node.AroundNodes...)
if len(aroundNodes) > 0 {
i := 0
var f func(context.Context)
f = func(c context.Context) {
sc := wrapContextChain(c)
if sc == nil {
suite.failer.Fail("An AroundNode failed to pass a valid Ginkgo SpecContext in. You must always pass in a context derived from the context passed to you.", aroundNodes[i].CodeLocation)
return
}
i++
if i < len(aroundNodes) {
aroundNodes[i].Body(sc, f)
} else {
node.Body(sc)
}
}
aroundNodes[0].Body(sc, f)
if i != len(aroundNodes) {
suite.failer.Fail("An AroundNode failed to call the passed in function.", aroundNodes[i].CodeLocation)
}
} else {
node.Body(sc)
}
finished = true
}()

56
vendor/github.com/onsi/ginkgo/v2/types/around_node.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
package types
import (
"context"
)
type AroundNodeAllowedFuncs interface {
~func(context.Context, func(context.Context)) | ~func(context.Context) context.Context | ~func()
}
type AroundNodeFunc func(ctx context.Context, body func(ctx context.Context))
func AroundNode[F AroundNodeAllowedFuncs](f F, cl CodeLocation) AroundNodeDecorator {
if f == nil {
panic("BuildAroundNode cannot be called with a nil function.")
}
var aroundNodeFunc func(context.Context, func(context.Context))
switch x := any(f).(type) {
case func(context.Context, func(context.Context)):
aroundNodeFunc = x
case func(context.Context) context.Context:
aroundNodeFunc = func(ctx context.Context, body func(context.Context)) {
ctx = x(ctx)
body(ctx)
}
case func():
aroundNodeFunc = func(ctx context.Context, body func(context.Context)) {
x()
body(ctx)
}
}
return AroundNodeDecorator{
Body: aroundNodeFunc,
CodeLocation: cl,
}
}
type AroundNodeDecorator struct {
Body AroundNodeFunc
CodeLocation CodeLocation
}
type AroundNodes []AroundNodeDecorator
func (an AroundNodes) Clone() AroundNodes {
out := make(AroundNodes, len(an))
copy(out, an)
return out
}
func (an AroundNodes) Append(other ...AroundNodeDecorator) AroundNodes {
out := make(AroundNodes, len(an)+len(other))
copy(out, an)
copy(out[len(an):], other)
return out
}

View File

@ -1,3 +1,3 @@
package types
const VERSION = "2.24.0"
const VERSION = "2.25.0"