Merge pull request #1267 from containers/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.6.0

build(deps): bump github.com/onsi/ginkgo/v2 from 2.5.1 to 2.6.0
This commit is contained in:
OpenShift Merge Robot 2022-12-12 15:03:14 -05:00 committed by GitHub
commit fdd51bbec5
17 changed files with 1538 additions and 81 deletions

View File

@ -24,7 +24,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/jinzhu/copier v0.3.5 github.com/jinzhu/copier v0.3.5
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/onsi/ginkgo/v2 v2.5.1 github.com/onsi/ginkgo/v2 v2.6.0
github.com/onsi/gomega v1.24.1 github.com/onsi/gomega v1.24.1
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc2 github.com/opencontainers/image-spec v1.1.0-rc2

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,13 @@
## 2.6.0
### Features
- `ReportBeforeSuite` provides access to the suite report before the suite begins.
- Add junit config option for omitting leafnodetype (#1088) [956e6d2]
- Add support to customize junit report config to omit spec labels (#1087) [de44005]
### Fixes
- Fix stack trace pruning so that it has a chance of working on windows [2165648]
## 2.5.1 ## 2.5.1
### Fixes ### Fixes

View File

@ -44,8 +44,8 @@ type Node struct {
SynchronizedAfterSuiteProc1Body func(SpecContext) SynchronizedAfterSuiteProc1Body func(SpecContext)
SynchronizedAfterSuiteProc1BodyHasContext bool SynchronizedAfterSuiteProc1BodyHasContext bool
ReportEachBody func(types.SpecReport) ReportEachBody func(types.SpecReport)
ReportAfterSuiteBody func(types.Report) ReportSuiteBody func(types.Report)
MarkedFocus bool MarkedFocus bool
MarkedPending bool MarkedPending bool
@ -317,9 +317,9 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
trackedFunctionError = true trackedFunctionError = true
break break
} }
} else if nodeType.Is(types.NodeTypeReportAfterSuite) { } else if nodeType.Is(types.NodeTypeReportBeforeSuite | types.NodeTypeReportAfterSuite) {
if node.ReportAfterSuiteBody == nil { if node.ReportSuiteBody == nil {
node.ReportAfterSuiteBody = arg.(func(types.Report)) node.ReportSuiteBody = arg.(func(types.Report))
} else { } else {
appendError(types.GinkgoErrors.MultipleBodyFunctions(node.CodeLocation, nodeType)) appendError(types.GinkgoErrors.MultipleBodyFunctions(node.CodeLocation, nodeType))
trackedFunctionError = true trackedFunctionError = true
@ -392,7 +392,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
appendError(types.GinkgoErrors.InvalidTimeoutOrGracePeriodForNonContextNode(node.CodeLocation, nodeType)) appendError(types.GinkgoErrors.InvalidTimeoutOrGracePeriodForNonContextNode(node.CodeLocation, nodeType))
} }
if !node.NodeType.Is(types.NodeTypeReportBeforeEach|types.NodeTypeReportAfterEach|types.NodeTypeSynchronizedBeforeSuite|types.NodeTypeSynchronizedAfterSuite|types.NodeTypeReportAfterSuite) && node.Body == nil && !node.MarkedPending && !trackedFunctionError { if !node.NodeType.Is(types.NodeTypeReportBeforeEach|types.NodeTypeReportAfterEach|types.NodeTypeSynchronizedBeforeSuite|types.NodeTypeSynchronizedAfterSuite|types.NodeTypeReportBeforeSuite|types.NodeTypeReportAfterSuite) && node.Body == nil && !node.MarkedPending && !trackedFunctionError {
appendError(types.GinkgoErrors.MissingBodyFunction(node.CodeLocation, nodeType)) appendError(types.GinkgoErrors.MissingBodyFunction(node.CodeLocation, nodeType))
} }

View File

@ -42,6 +42,8 @@ type Client interface {
PostSuiteWillBegin(report types.Report) error PostSuiteWillBegin(report types.Report) error
PostDidRun(report types.SpecReport) error PostDidRun(report types.SpecReport) error
PostSuiteDidEnd(report types.Report) error PostSuiteDidEnd(report types.Report) error
PostReportBeforeSuiteCompleted(state types.SpecState) error
BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error)
PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error
BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error) BlockUntilSynchronizedBeforeSuiteData() (types.SpecState, []byte, error)
BlockUntilNonprimaryProcsHaveFinished() error BlockUntilNonprimaryProcsHaveFinished() error

View File

@ -98,6 +98,19 @@ func (client *httpClient) PostEmitProgressReport(report types.ProgressReport) er
return client.post("/progress-report", report) return client.post("/progress-report", report)
} }
func (client *httpClient) PostReportBeforeSuiteCompleted(state types.SpecState) error {
return client.post("/report-before-suite-completed", state)
}
func (client *httpClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) {
var state types.SpecState
err := client.poll("/report-before-suite-state", &state)
if err == ErrorGone {
return types.SpecStateFailed, nil
}
return state, err
}
func (client *httpClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { func (client *httpClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error {
beforeSuiteState := BeforeSuiteState{ beforeSuiteState := BeforeSuiteState{
State: state, State: state,

View File

@ -26,7 +26,7 @@ type httpServer struct {
handler *ServerHandler handler *ServerHandler
} }
//Create a new server, automatically selecting a port // Create a new server, automatically selecting a port
func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) { func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer, error) {
listener, err := net.Listen("tcp", "127.0.0.1:0") listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
@ -38,7 +38,7 @@ func newHttpServer(parallelTotal int, reporter reporters.Reporter) (*httpServer,
}, nil }, nil
} }
//Start the server. You don't need to `go s.Start()`, just `s.Start()` // Start the server. You don't need to `go s.Start()`, just `s.Start()`
func (server *httpServer) Start() { func (server *httpServer) Start() {
httpServer := &http.Server{} httpServer := &http.Server{}
mux := http.NewServeMux() mux := http.NewServeMux()
@ -52,6 +52,8 @@ func (server *httpServer) Start() {
mux.HandleFunc("/progress-report", server.emitProgressReport) mux.HandleFunc("/progress-report", server.emitProgressReport)
//synchronization endpoints //synchronization endpoints
mux.HandleFunc("/report-before-suite-completed", server.handleReportBeforeSuiteCompleted)
mux.HandleFunc("/report-before-suite-state", server.handleReportBeforeSuiteState)
mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted) mux.HandleFunc("/before-suite-completed", server.handleBeforeSuiteCompleted)
mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState) mux.HandleFunc("/before-suite-state", server.handleBeforeSuiteState)
mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished) mux.HandleFunc("/have-nonprimary-procs-finished", server.handleHaveNonprimaryProcsFinished)
@ -63,12 +65,12 @@ func (server *httpServer) Start() {
go httpServer.Serve(server.listener) go httpServer.Serve(server.listener)
} }
//Stop the server // Stop the server
func (server *httpServer) Close() { func (server *httpServer) Close() {
server.listener.Close() server.listener.Close()
} }
//The address the server can be reached it. Pass this into the `ForwardingReporter`. // The address the server can be reached it. Pass this into the `ForwardingReporter`.
func (server *httpServer) Address() string { func (server *httpServer) Address() string {
return "http://" + server.listener.Addr().String() return "http://" + server.listener.Addr().String()
} }
@ -93,7 +95,7 @@ func (server *httpServer) RegisterAlive(node int, alive func() bool) {
// Streaming Endpoints // Streaming Endpoints
// //
//The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters` // The server will forward all received messages to Ginkgo reporters registered with `RegisterReporters`
func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool { func (server *httpServer) decode(writer http.ResponseWriter, request *http.Request, object interface{}) bool {
defer request.Body.Close() defer request.Body.Close()
if json.NewDecoder(request.Body).Decode(object) != nil { if json.NewDecoder(request.Body).Decode(object) != nil {
@ -164,6 +166,23 @@ func (server *httpServer) emitProgressReport(writer http.ResponseWriter, request
server.handleError(server.handler.EmitProgressReport(report, voidReceiver), writer) server.handleError(server.handler.EmitProgressReport(report, voidReceiver), writer)
} }
func (server *httpServer) handleReportBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) {
var state types.SpecState
if !server.decode(writer, request, &state) {
return
}
server.handleError(server.handler.ReportBeforeSuiteCompleted(state, voidReceiver), writer)
}
func (server *httpServer) handleReportBeforeSuiteState(writer http.ResponseWriter, request *http.Request) {
var state types.SpecState
if server.handleError(server.handler.ReportBeforeSuiteState(voidSender, &state), writer) {
return
}
json.NewEncoder(writer).Encode(state)
}
func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) { func (server *httpServer) handleBeforeSuiteCompleted(writer http.ResponseWriter, request *http.Request) {
var beforeSuiteState BeforeSuiteState var beforeSuiteState BeforeSuiteState
if !server.decode(writer, request, &beforeSuiteState) { if !server.decode(writer, request, &beforeSuiteState) {

View File

@ -76,6 +76,19 @@ func (client *rpcClient) PostEmitProgressReport(report types.ProgressReport) err
return client.client.Call("Server.EmitProgressReport", report, voidReceiver) return client.client.Call("Server.EmitProgressReport", report, voidReceiver)
} }
func (client *rpcClient) PostReportBeforeSuiteCompleted(state types.SpecState) error {
return client.client.Call("Server.ReportBeforeSuiteCompleted", state, voidReceiver)
}
func (client *rpcClient) BlockUntilReportBeforeSuiteCompleted() (types.SpecState, error) {
var state types.SpecState
err := client.poll("Server.ReportBeforeSuiteState", &state)
if err == ErrorGone {
return types.SpecStateFailed, nil
}
return state, err
}
func (client *rpcClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error { func (client *rpcClient) PostSynchronizedBeforeSuiteCompleted(state types.SpecState, data []byte) error {
beforeSuiteState := BeforeSuiteState{ beforeSuiteState := BeforeSuiteState{
State: state, State: state,

View File

@ -18,16 +18,17 @@ var voidSender Void
// It handles all the business logic to avoid duplication between the two servers // It handles all the business logic to avoid duplication between the two servers
type ServerHandler struct { type ServerHandler struct {
done chan interface{} done chan interface{}
outputDestination io.Writer outputDestination io.Writer
reporter reporters.Reporter reporter reporters.Reporter
alives []func() bool alives []func() bool
lock *sync.Mutex lock *sync.Mutex
beforeSuiteState BeforeSuiteState beforeSuiteState BeforeSuiteState
parallelTotal int reportBeforeSuiteState types.SpecState
counter int parallelTotal int
counterLock *sync.Mutex counter int
shouldAbort bool counterLock *sync.Mutex
shouldAbort bool
numSuiteDidBegins int numSuiteDidBegins int
numSuiteDidEnds int numSuiteDidEnds int
@ -37,11 +38,12 @@ type ServerHandler struct {
func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHandler { func newServerHandler(parallelTotal int, reporter reporters.Reporter) *ServerHandler {
return &ServerHandler{ return &ServerHandler{
reporter: reporter, reporter: reporter,
lock: &sync.Mutex{}, lock: &sync.Mutex{},
counterLock: &sync.Mutex{}, counterLock: &sync.Mutex{},
alives: make([]func() bool, parallelTotal), alives: make([]func() bool, parallelTotal),
beforeSuiteState: BeforeSuiteState{Data: nil, State: types.SpecStateInvalid}, beforeSuiteState: BeforeSuiteState{Data: nil, State: types.SpecStateInvalid},
parallelTotal: parallelTotal, parallelTotal: parallelTotal,
outputDestination: os.Stdout, outputDestination: os.Stdout,
done: make(chan interface{}), done: make(chan interface{}),
@ -140,6 +142,29 @@ func (handler *ServerHandler) haveNonprimaryProcsFinished() bool {
return true return true
} }
func (handler *ServerHandler) ReportBeforeSuiteCompleted(reportBeforeSuiteState types.SpecState, _ *Void) error {
handler.lock.Lock()
defer handler.lock.Unlock()
handler.reportBeforeSuiteState = reportBeforeSuiteState
return nil
}
func (handler *ServerHandler) ReportBeforeSuiteState(_ Void, reportBeforeSuiteState *types.SpecState) error {
proc1IsAlive := handler.procIsAlive(1)
handler.lock.Lock()
defer handler.lock.Unlock()
if handler.reportBeforeSuiteState == types.SpecStateInvalid {
if proc1IsAlive {
return ErrorEarly
} else {
return ErrorGone
}
}
*reportBeforeSuiteState = handler.reportBeforeSuiteState
return nil
}
func (handler *ServerHandler) BeforeSuiteCompleted(beforeSuiteState BeforeSuiteState, _ *Void) error { func (handler *ServerHandler) BeforeSuiteCompleted(beforeSuiteState BeforeSuiteState, _ *Void) error {
handler.lock.Lock() handler.lock.Lock()
defer handler.lock.Unlock() defer handler.lock.Unlock()

View File

@ -183,7 +183,6 @@ func extractRunningGoroutines() ([]types.Goroutine, error) {
break break
} }
} }
r := bufio.NewReader(bytes.NewReader(stack)) r := bufio.NewReader(bytes.NewReader(stack))
out := []types.Goroutine{} out := []types.Goroutine{}
idx := -1 idx := -1
@ -231,12 +230,12 @@ func extractRunningGoroutines() ([]types.Goroutine, error) {
return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid function call: %s -- missing file name and line number", functionCall.Function)) return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid function call: %s -- missing file name and line number", functionCall.Function))
} }
line = strings.TrimLeft(line, " \t") line = strings.TrimLeft(line, " \t")
fields := strings.SplitN(line, ":", 2) delimiterIdx := strings.LastIndex(line, ":")
if len(fields) != 2 { if delimiterIdx == -1 {
return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid filename nad line number: %s", line)) return nil, types.GinkgoErrors.FailedToParseStackTrace(fmt.Sprintf("Invalid filename and line number: %s", line))
} }
functionCall.Filename = fields[0] functionCall.Filename = line[:delimiterIdx]
line = strings.Split(fields[1], " ")[0] line = strings.Split(line[delimiterIdx+1:], " ")[0]
lineNumber, err := strconv.ParseInt(line, 10, 64) lineNumber, err := strconv.ParseInt(line, 10, 64)
functionCall.Line = int(lineNumber) functionCall.Line = int(lineNumber)
if err != nil { if err != nil {

View File

@ -129,7 +129,7 @@ func (suite *Suite) PushNode(node Node) error {
return suite.pushCleanupNode(node) return suite.pushCleanupNode(node)
} }
if node.NodeType.Is(types.NodeTypeBeforeSuite | types.NodeTypeAfterSuite | types.NodeTypeSynchronizedBeforeSuite | types.NodeTypeSynchronizedAfterSuite | types.NodeTypeReportAfterSuite) { if node.NodeType.Is(types.NodeTypeBeforeSuite | types.NodeTypeAfterSuite | types.NodeTypeSynchronizedBeforeSuite | types.NodeTypeSynchronizedAfterSuite | types.NodeTypeBeforeSuite | types.NodeTypeReportBeforeSuite | types.NodeTypeReportAfterSuite) {
return suite.pushSuiteNode(node) return suite.pushSuiteNode(node)
} }
@ -222,7 +222,7 @@ func (suite *Suite) pushCleanupNode(node Node) error {
node.NodeType = types.NodeTypeCleanupAfterSuite node.NodeType = types.NodeTypeCleanupAfterSuite
case types.NodeTypeBeforeAll, types.NodeTypeAfterAll: case types.NodeTypeBeforeAll, types.NodeTypeAfterAll:
node.NodeType = types.NodeTypeCleanupAfterAll node.NodeType = types.NodeTypeCleanupAfterAll
case types.NodeTypeReportBeforeEach, types.NodeTypeReportAfterEach, types.NodeTypeReportAfterSuite: case types.NodeTypeReportBeforeEach, types.NodeTypeReportAfterEach, types.NodeTypeReportBeforeSuite, types.NodeTypeReportAfterSuite:
return types.GinkgoErrors.PushingCleanupInReportingNode(node.CodeLocation, suite.currentNode.NodeType) return types.GinkgoErrors.PushingCleanupInReportingNode(node.CodeLocation, suite.currentNode.NodeType)
case types.NodeTypeCleanupInvalid, types.NodeTypeCleanupAfterEach, types.NodeTypeCleanupAfterAll, types.NodeTypeCleanupAfterSuite: case types.NodeTypeCleanupInvalid, types.NodeTypeCleanupAfterEach, types.NodeTypeCleanupAfterAll, types.NodeTypeCleanupAfterSuite:
return types.GinkgoErrors.PushingCleanupInCleanupNode(node.CodeLocation) return types.GinkgoErrors.PushingCleanupInCleanupNode(node.CodeLocation)
@ -408,7 +408,13 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
} }
suite.report.SuiteSucceeded = true suite.report.SuiteSucceeded = true
suite.runBeforeSuite(numSpecsThatWillBeRun)
suite.runReportSuiteNodesIfNeedBe(types.NodeTypeReportBeforeSuite)
ranBeforeSuite := suite.report.SuiteSucceeded
if suite.report.SuiteSucceeded {
suite.runBeforeSuite(numSpecsThatWillBeRun)
}
if suite.report.SuiteSucceeded { if suite.report.SuiteSucceeded {
groupedSpecIndices, serialGroupedSpecIndices := OrderSpecs(specs, suite.config) groupedSpecIndices, serialGroupedSpecIndices := OrderSpecs(specs, suite.config)
@ -447,7 +453,9 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
} }
} }
suite.runAfterSuiteCleanup(numSpecsThatWillBeRun) if ranBeforeSuite {
suite.runAfterSuiteCleanup(numSpecsThatWillBeRun)
}
interruptStatus := suite.interruptHandler.Status() interruptStatus := suite.interruptHandler.Status()
if interruptStatus.Interrupted() { if interruptStatus.Interrupted() {
@ -461,9 +469,7 @@ func (suite *Suite) runSpecs(description string, suiteLabels Labels, suitePath s
suite.report.SuiteSucceeded = false suite.report.SuiteSucceeded = false
} }
if suite.config.ParallelProcess == 1 { suite.runReportSuiteNodesIfNeedBe(types.NodeTypeReportAfterSuite)
suite.runReportAfterSuite()
}
suite.reporter.SuiteDidEnd(suite.report) suite.reporter.SuiteDidEnd(suite.report)
if suite.isRunningInParallel() { if suite.isRunningInParallel() {
suite.client.PostSuiteDidEnd(suite.report) suite.client.PostSuiteDidEnd(suite.report)
@ -530,24 +536,6 @@ func (suite *Suite) runAfterSuiteCleanup(numSpecsThatWillBeRun int) {
} }
} }
func (suite *Suite) runReportAfterSuite() {
for _, node := range suite.suiteNodes.WithType(types.NodeTypeReportAfterSuite) {
suite.selectiveLock.Lock()
suite.currentSpecReport = types.SpecReport{
LeafNodeType: node.NodeType,
LeafNodeLocation: node.CodeLocation,
LeafNodeText: node.Text,
ParallelProcess: suite.config.ParallelProcess,
RunningInParallel: suite.isRunningInParallel(),
}
suite.selectiveLock.Unlock()
suite.reporter.WillRun(suite.currentSpecReport)
suite.runReportAfterSuiteNode(node, suite.report)
suite.processCurrentSpecReport()
}
}
func (suite *Suite) reportEach(spec Spec, nodeType types.NodeType) { func (suite *Suite) reportEach(spec Spec, nodeType types.NodeType) {
nodes := spec.Nodes.WithType(nodeType) nodes := spec.Nodes.WithType(nodeType)
if nodeType == types.NodeTypeReportAfterEach { if nodeType == types.NodeTypeReportAfterEach {
@ -672,16 +660,57 @@ func (suite *Suite) runSuiteNode(node Node) {
suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime) suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime)
suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes()) suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes())
suite.currentSpecReport.CapturedStdOutErr += suite.outputInterceptor.StopInterceptingAndReturnOutput() suite.currentSpecReport.CapturedStdOutErr += suite.outputInterceptor.StopInterceptingAndReturnOutput()
return
} }
func (suite *Suite) runReportAfterSuiteNode(node Node, report types.Report) { func (suite *Suite) runReportSuiteNodesIfNeedBe(nodeType types.NodeType) {
nodes := suite.suiteNodes.WithType(nodeType)
// only run ReportAfterSuite on proc 1
if nodeType.Is(types.NodeTypeReportAfterSuite) && suite.config.ParallelProcess != 1 {
return
}
// if we're running ReportBeforeSuite on proc > 1 - we should wait until proc 1 has completed
if nodeType.Is(types.NodeTypeReportBeforeSuite) && suite.config.ParallelProcess != 1 && len(nodes) > 0 {
state, err := suite.client.BlockUntilReportBeforeSuiteCompleted()
if err != nil || state.Is(types.SpecStateFailed) {
suite.report.SuiteSucceeded = false
}
return
}
for _, node := range nodes {
suite.selectiveLock.Lock()
suite.currentSpecReport = types.SpecReport{
LeafNodeType: node.NodeType,
LeafNodeLocation: node.CodeLocation,
LeafNodeText: node.Text,
ParallelProcess: suite.config.ParallelProcess,
RunningInParallel: suite.isRunningInParallel(),
}
suite.selectiveLock.Unlock()
suite.reporter.WillRun(suite.currentSpecReport)
suite.runReportSuiteNode(node, suite.report)
suite.processCurrentSpecReport()
}
// if we're running ReportBeforeSuite and we're running in parallel - we shuld tell the other procs that we're done
if nodeType.Is(types.NodeTypeReportBeforeSuite) && suite.isRunningInParallel() && len(nodes) > 0 {
if suite.report.SuiteSucceeded {
suite.client.PostReportBeforeSuiteCompleted(types.SpecStatePassed)
} else {
suite.client.PostReportBeforeSuiteCompleted(types.SpecStateFailed)
}
}
}
func (suite *Suite) runReportSuiteNode(node Node, report types.Report) {
suite.writer.Truncate() suite.writer.Truncate()
suite.outputInterceptor.StartInterceptingOutput() suite.outputInterceptor.StartInterceptingOutput()
suite.currentSpecReport.StartTime = time.Now() suite.currentSpecReport.StartTime = time.Now()
if suite.config.ParallelTotal > 1 { // if we're running a ReportAfterSuite in parallel (on proc 1) we (a) wait until other procs have exited and
// (b) always fetch the latest report as prior ReportAfterSuites will contribute to it
if node.NodeType.Is(types.NodeTypeReportAfterSuite) && suite.isRunningInParallel() {
aggregatedReport, err := suite.client.BlockUntilAggregatedNonprimaryProcsReport() aggregatedReport, err := suite.client.BlockUntilAggregatedNonprimaryProcsReport()
if err != nil { if err != nil {
suite.currentSpecReport.State, suite.currentSpecReport.Failure = types.SpecStateFailed, suite.failureForLeafNodeWithMessage(node, err.Error()) suite.currentSpecReport.State, suite.currentSpecReport.Failure = types.SpecStateFailed, suite.failureForLeafNodeWithMessage(node, err.Error())
@ -691,15 +720,13 @@ func (suite *Suite) runReportAfterSuiteNode(node Node, report types.Report) {
report = report.Add(aggregatedReport) report = report.Add(aggregatedReport)
} }
node.Body = func(SpecContext) { node.ReportAfterSuiteBody(report) } node.Body = func(SpecContext) { node.ReportSuiteBody(report) }
suite.currentSpecReport.State, suite.currentSpecReport.Failure = suite.runNode(node, time.Time{}, "") suite.currentSpecReport.State, suite.currentSpecReport.Failure = suite.runNode(node, time.Time{}, "")
suite.currentSpecReport.EndTime = time.Now() suite.currentSpecReport.EndTime = time.Now()
suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime) suite.currentSpecReport.RunTime = suite.currentSpecReport.EndTime.Sub(suite.currentSpecReport.StartTime)
suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes()) suite.currentSpecReport.CapturedGinkgoWriterOutput = string(suite.writer.Bytes())
suite.currentSpecReport.CapturedStdOutErr = suite.outputInterceptor.StopInterceptingAndReturnOutput() suite.currentSpecReport.CapturedStdOutErr = suite.outputInterceptor.StopInterceptingAndReturnOutput()
return
} }
func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (types.SpecState, types.Failure) { func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (types.SpecState, types.Failure) {

View File

@ -30,6 +30,12 @@ type JunitReportConfig struct {
//Enable OmitCapturedStdOutErr to prevent captured stdout/stderr appearing in system-out //Enable OmitCapturedStdOutErr to prevent captured stdout/stderr appearing in system-out
OmitCapturedStdOutErr bool OmitCapturedStdOutErr bool
// Enable OmitSpecLabels to prevent labels from appearing in the spec name
OmitSpecLabels bool
// Enable OmitLeafNodeType to prevent the spec leaf node type from appearing in the spec name
OmitLeafNodeType bool
} }
type JUnitTestSuites struct { type JUnitTestSuites struct {
@ -172,13 +178,17 @@ func GenerateJUnitReportWithConfig(report types.Report, dst string, config Junit
} }
for _, spec := range report.SpecReports { for _, spec := range report.SpecReports {
name := fmt.Sprintf("[%s]", spec.LeafNodeType) name := fmt.Sprintf("[%s]", spec.LeafNodeType)
if config.OmitLeafNodeType {
name = ""
}
if spec.FullText() != "" { if spec.FullText() != "" {
name = name + " " + spec.FullText() name = name + " " + spec.FullText()
} }
labels := spec.Labels() labels := spec.Labels()
if len(labels) > 0 { if len(labels) > 0 && !config.OmitSpecLabels {
name = name + " [" + strings.Join(labels, ", ") + "]" name = name + " [" + strings.Join(labels, ", ") + "]"
} }
name = strings.TrimSpace(name)
test := JUnitTestCase{ test := JUnitTestCase{
Name: name, Name: name,

View File

@ -35,7 +35,7 @@ func CurrentSpecReport() SpecReport {
} }
/* /*
ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter ReportEntryVisibility governs the visibility of ReportEntries in Ginkgo's console reporter
- ReportEntryVisibilityAlways: the default behavior - the ReportEntry is always emitted. - ReportEntryVisibilityAlways: the default behavior - the ReportEntry is always emitted.
- ReportEntryVisibilityFailureOrVerbose: the ReportEntry is only emitted if the spec fails or if the tests are run with -v (similar to GinkgoWriters behavior). - ReportEntryVisibilityFailureOrVerbose: the ReportEntry is only emitted if the spec fails or if the tests are run with -v (similar to GinkgoWriters behavior).
@ -50,9 +50,9 @@ const ReportEntryVisibilityAlways, ReportEntryVisibilityFailureOrVerbose, Report
/* /*
AddReportEntry generates and adds a new ReportEntry to the current spec's SpecReport. AddReportEntry generates and adds a new ReportEntry to the current spec's SpecReport.
It can take any of the following arguments: It can take any of the following arguments:
- A single arbitrary object to attach as the Value of the ReportEntry. This object will be included in any generated reports and will be emitted to the console when the report is emitted. - A single arbitrary object to attach as the Value of the ReportEntry. This object will be included in any generated reports and will be emitted to the console when the report is emitted.
- A ReportEntryVisibility enum to control the visibility of the ReportEntry - A ReportEntryVisibility enum to control the visibility of the ReportEntry
- An Offset or CodeLocation decoration to control the reported location of the ReportEntry - An Offset or CodeLocation decoration to control the reported location of the ReportEntry
If the Value object implements `fmt.Stringer`, it's `String()` representation is used when emitting to the console. If the Value object implements `fmt.Stringer`, it's `String()` representation is used when emitting to the console.
@ -100,6 +100,25 @@ func ReportAfterEach(body func(SpecReport), args ...interface{}) bool {
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...)) return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...))
} }
/*
ReportBeforeSuite nodes are run at the beginning of the suite. ReportBeforeSuite nodes take a function that receives a suite Report.
They are called at the beginning of the suite, before any specs have run and any BeforeSuite or SynchronizedBeforeSuite nodes, and are passed in the initial report for the suite.
ReportBeforeSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node)
# When running in parallel, Ginkgo ensures that only one of the parallel nodes runs the ReportBeforeSuite
You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports
*/
func ReportBeforeSuite(body func(Report), args ...interface{}) bool {
combinedArgs := []interface{}{body}
combinedArgs = append(combinedArgs, args...)
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeSuite, "", combinedArgs...))
}
/* /*
ReportAfterSuite nodes are run at the end of the suite. ReportAfterSuite nodes take a function that receives a suite Report. ReportAfterSuite nodes are run at the end of the suite. ReportAfterSuite nodes take a function that receives a suite Report.
@ -113,6 +132,7 @@ In addition to using ReportAfterSuite to programmatically generate suite reports
You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure. You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports You can learn more about Ginkgo's reporting infrastructure, including generating reports with the CLI here: https://onsi.github.io/ginkgo/#generating-machine-readable-reports
*/ */
func ReportAfterSuite(text string, body func(Report), args ...interface{}) bool { func ReportAfterSuite(text string, body func(Report), args ...interface{}) bool {

View File

@ -108,8 +108,8 @@ Please ensure all assertions are inside leaf nodes such as {{bold}}BeforeEach{{/
func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocation) error { func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocation) error {
docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite"
if nodeType.Is(NodeTypeReportAfterSuite) { if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) {
docLink = "reporting-nodes---reportaftersuite" docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite"
} }
return GinkgoError{ return GinkgoError{
@ -125,8 +125,8 @@ func (g ginkgoErrors) SuiteNodeInNestedContext(nodeType NodeType, cl CodeLocatio
func (g ginkgoErrors) SuiteNodeDuringRunPhase(nodeType NodeType, cl CodeLocation) error { func (g ginkgoErrors) SuiteNodeDuringRunPhase(nodeType NodeType, cl CodeLocation) error {
docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite" docLink := "suite-setup-and-cleanup-beforesuite-and-aftersuite"
if nodeType.Is(NodeTypeReportAfterSuite) { if nodeType.Is(NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite) {
docLink = "reporting-nodes---reportaftersuite" docLink = "reporting-nodes---reportbeforesuite-and-reportaftersuite"
} }
return GinkgoError{ return GinkgoError{
@ -320,7 +320,7 @@ func (g ginkgoErrors) PushingCleanupNodeDuringTreeConstruction(cl CodeLocation)
func (g ginkgoErrors) PushingCleanupInReportingNode(cl CodeLocation, nodeType NodeType) error { func (g ginkgoErrors) PushingCleanupInReportingNode(cl CodeLocation, nodeType NodeType) error {
return GinkgoError{ return GinkgoError{
Heading: fmt.Sprintf("DeferCleanup cannot be called in %s", nodeType), Heading: fmt.Sprintf("DeferCleanup cannot be called in %s", nodeType),
Message: "Please inline your cleanup code - Ginkgo won't run cleanup code after a ReportAfterEach or ReportAfterSuite.", Message: "Please inline your cleanup code - Ginkgo won't run cleanup code after a Reporting node.",
CodeLocation: cl, CodeLocation: cl,
DocLink: "cleaning-up-our-cleanup-code-defercleanup", DocLink: "cleaning-up-our-cleanup-code-defercleanup",
} }

View File

@ -58,6 +58,7 @@ type Report struct {
SuiteConfig SuiteConfig SuiteConfig SuiteConfig
//SpecReports is a list of all SpecReports generated by this test run //SpecReports is a list of all SpecReports generated by this test run
//It is empty when the SuiteReport is provided to ReportBeforeSuite
SpecReports SpecReports SpecReports SpecReports
} }
@ -761,6 +762,7 @@ const (
NodeTypeReportBeforeEach NodeTypeReportBeforeEach
NodeTypeReportAfterEach NodeTypeReportAfterEach
NodeTypeReportBeforeSuite
NodeTypeReportAfterSuite NodeTypeReportAfterSuite
NodeTypeCleanupInvalid NodeTypeCleanupInvalid
@ -770,9 +772,9 @@ const (
) )
var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt
var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite
var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite
var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportAfterSuite var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite
var ntEnumSupport = NewEnumSupport(map[uint]string{ var ntEnumSupport = NewEnumSupport(map[uint]string{
uint(NodeTypeInvalid): "INVALID NODE TYPE", uint(NodeTypeInvalid): "INVALID NODE TYPE",
@ -790,6 +792,7 @@ var ntEnumSupport = NewEnumSupport(map[uint]string{
uint(NodeTypeSynchronizedAfterSuite): "SynchronizedAfterSuite", uint(NodeTypeSynchronizedAfterSuite): "SynchronizedAfterSuite",
uint(NodeTypeReportBeforeEach): "ReportBeforeEach", uint(NodeTypeReportBeforeEach): "ReportBeforeEach",
uint(NodeTypeReportAfterEach): "ReportAfterEach", uint(NodeTypeReportAfterEach): "ReportAfterEach",
uint(NodeTypeReportBeforeSuite): "ReportBeforeSuite",
uint(NodeTypeReportAfterSuite): "ReportAfterSuite", uint(NodeTypeReportAfterSuite): "ReportAfterSuite",
uint(NodeTypeCleanupInvalid): "DeferCleanup", uint(NodeTypeCleanupInvalid): "DeferCleanup",
uint(NodeTypeCleanupAfterEach): "DeferCleanup (Each)", uint(NodeTypeCleanupAfterEach): "DeferCleanup (Each)",

View File

@ -1,3 +1,3 @@
package types package types
const VERSION = "2.5.1" const VERSION = "2.6.0"

View File

@ -374,7 +374,7 @@ github.com/modern-go/concurrent
# github.com/modern-go/reflect2 v1.0.2 # github.com/modern-go/reflect2 v1.0.2
## explicit; go 1.12 ## explicit; go 1.12
github.com/modern-go/reflect2 github.com/modern-go/reflect2
# github.com/onsi/ginkgo/v2 v2.5.1 # github.com/onsi/ginkgo/v2 v2.6.0
## explicit; go 1.18 ## explicit; go 1.18
github.com/onsi/ginkgo/v2 github.com/onsi/ginkgo/v2
github.com/onsi/ginkgo/v2/config github.com/onsi/ginkgo/v2/config