Skip to content

Commit c4e219f

Browse files
eugenenosenkoonsi
authored andcommitted
add SpecContext to other reporting nodes and update tests
1 parent 06de431 commit c4e219f

File tree

6 files changed

+113
-37
lines changed

6 files changed

+113
-37
lines changed

internal/internal_integration/report_each_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ var _ = Describe("Sending reports to ReportBeforeEach and ReportAfterEach nodes"
9898
reports["interrupt"] = append(reports["interrupt"], report)
9999
})
100100
})
101+
Context("when a after each reporter times out", func() {
102+
It("passes", rt.T("passes"))
103+
ReportAfterEach(func(ctx SpecContext, report types.SpecReport) {
104+
select {
105+
case <-ctx.Done():
106+
rt.Run("timeout-reporter")
107+
reports["timeout"] = append(reports["timeout"], report)
108+
case <-time.After(100 * time.Millisecond):
109+
}
110+
}, NodeTimeout(10*time.Millisecond))
111+
})
101112
})
102113
ReportBeforeEach(func(report types.SpecReport) {
103114
rt.Run("outer-RBE")
@@ -114,15 +125,16 @@ var _ = Describe("Sending reports to ReportBeforeEach and ReportAfterEach nodes"
114125
"outer-RBE", "inner-RBE", "passes", "inner-RAE", "outer-RAE",
115126
"outer-RBE", "inner-RBE", "fails", "inner-RAE", "outer-RAE",
116127
"outer-RBE", "inner-RBE", "panics", "inner-RAE", "outer-RAE",
117-
"outer-RBE", "inner-RBE", "inner-RAE", "outer-RAE", //pending test
128+
"outer-RBE", "inner-RBE", "inner-RAE", "outer-RAE", // pending test
118129
"outer-RBE", "inner-RBE", "skipped", "inner-RAE", "outer-RAE",
119-
"outer-RBE", "inner-RBE", "inner-RAE", "outer-RAE", //flag-skipped test
130+
"outer-RBE", "inner-RBE", "inner-RAE", "outer-RAE", // flag-skipped test
120131
"outer-RBE", "inner-RBE", "also-passes", "failing-RAE", "inner-RAE", "outer-RAE",
121-
"outer-RBE", "inner-RBE", "failing-in-skip-RAE", "inner-RAE", "outer-RAE", //is also flag-skipped
132+
"outer-RBE", "inner-RBE", "failing-in-skip-RAE", "inner-RAE", "outer-RAE", // is also flag-skipped
122133
"outer-RBE", "inner-RBE", "writer", "writing-reporter", "inner-RAE", "outer-RAE",
123134
"outer-RBE", "inner-RBE", "failing-RBE", "not-failing-RBE", "inner-RAE", "outer-RAE",
124135
"outer-RBE", "inner-RBE", "passes-yet-again", "inner-RAE", "outer-RAE",
125-
"outer-RBE", "inner-RBE", "interrupt-reporter", "inner-RAE", "outer-RAE", //skipped by interrupt
136+
"outer-RBE", "inner-RBE", "interrupt-reporter", "inner-RAE", "outer-RAE", // skipped by interrupt
137+
"outer-RBE", "inner-RBE", "timeout-reporter", "inner-RAE", "outer-RAE", // skipped by timeout
126138
"after-suite",
127139
))
128140
})
@@ -191,7 +203,7 @@ var _ = Describe("Sending reports to ReportBeforeEach and ReportAfterEach nodes"
191203
Ω(reports["inner-RAE"].Find("writes stuff").CapturedGinkgoWriterOutput).Should(Equal("GinkgoWriter from It\nGinkgoWriter from ReportAfterEach\n"))
192204
Ω(reports["inner-RAE"].Find("writes stuff").CapturedStdOutErr).Should(Equal("Output from It\nOutput from ReportAfterEach\n"))
193205

194-
//but a report containing the additional output will be send to Ginkgo's reporter...
206+
// but a report containing the additional output will be send to Ginkgo's reporter...
195207
Ω(reporter.Did.Find("writes stuff").CapturedGinkgoWriterOutput).Should((Equal("GinkgoWriter from It\nGinkgoWriter from ReportAfterEach\n")))
196208
Ω(reporter.Did.Find("writes stuff").CapturedStdOutErr).Should((Equal("Output from It\nOutput from ReportAfterEach\n")))
197209
})

internal/internal_integration/report_suite_test.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import (
1111
)
1212

1313
var _ = Describe("Sending reports to ReportBeforeSuite and ReportAfterSuite nodes", func() {
14-
var failInReportBeforeSuiteA, failInReportAfterSuiteA, timeoutInReportAfterSuiteC, interruptSuiteB bool
14+
var failInReportBeforeSuiteA, timeoutInReportBeforeSuiteB, failInReportAfterSuiteA, timeoutInReportAfterSuiteC, interruptSuiteB bool
1515
var fixture func()
1616

1717
BeforeEach(func() {
1818
failInReportBeforeSuiteA = false
19+
timeoutInReportBeforeSuiteB = false
1920
failInReportAfterSuiteA = false
2021
timeoutInReportAfterSuiteC = false
2122
interruptSuiteB = false
@@ -32,11 +33,20 @@ var _ = Describe("Sending reports to ReportBeforeSuite and ReportAfterSuite node
3233
F("fail in report-before-suite-A")
3334
}
3435
})
35-
ReportBeforeSuite(func(report Report) {
36+
ReportBeforeSuite(func(ctx SpecContext, report Report) {
37+
timeout := 200 * time.Millisecond
38+
if timeoutInReportBeforeSuiteB {
39+
timeout = timeout + 1*time.Second
40+
}
3641
rt.RunWithData("report-before-suite-B", "report", report)
3742
writer.Print("gw-report-before-suite-B")
38-
outputInterceptor.AppendInterceptedOutput("out-report-before-suite-B")
39-
})
43+
select {
44+
case <-ctx.Done():
45+
outputInterceptor.AppendInterceptedOutput("timeout-report-before-suite-B")
46+
case <-time.After(timeout):
47+
outputInterceptor.AppendInterceptedOutput("out-report-before-suite-B")
48+
}
49+
}, NodeTimeout(500*time.Millisecond))
4050
Context("container", func() {
4151
It("A", rt.T("A"))
4252
It("B", rt.T("B", func() {
@@ -190,6 +200,24 @@ var _ = Describe("Sending reports to ReportBeforeSuite and ReportAfterSuite node
190200
})
191201
})
192202

203+
Context("when a ReportBeforeSuite times out", func() {
204+
BeforeEach(func() {
205+
timeoutInReportBeforeSuiteB = true
206+
success, _ := RunFixture("report-before-suite-B-timed-out", fixture)
207+
Ω(success).Should(BeFalse())
208+
})
209+
210+
It("reports on the failure, to Ginkgo's reporter and any subsequent reporters", func() {
211+
Ω(reporter.Did.WithLeafNodeType(types.NodeTypeReportBeforeSuite).WithState(types.SpecStateTimedout)).
212+
Should(ContainElement(HaveTimedOut(
213+
types.NodeTypeReportBeforeSuite,
214+
"A node timeout occurred",
215+
CapturedGinkgoWriterOutput("gw-report-before-suite-B"),
216+
CapturedStdOutput("timeout-report-before-suite-B"),
217+
)))
218+
})
219+
})
220+
193221
Context("when a ReportAfterSuite times out", func() {
194222
BeforeEach(func() {
195223
timeoutInReportAfterSuiteC = true

internal/node.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ var _global_node_id_counter = uint(0)
1515
var _global_id_mutex = &sync.Mutex{}
1616

1717
func UniqueNodeID() uint {
18-
//There's a reace in the internal integration tests if we don't make
19-
//accessing _global_node_id_counter safe across goroutines.
18+
// There's a reace in the internal integration tests if we don't make
19+
// accessing _global_node_id_counter safe across goroutines.
2020
_global_id_mutex.Lock()
2121
defer _global_id_mutex.Unlock()
2222
_global_node_id_counter += 1
@@ -43,7 +43,7 @@ type Node struct {
4343
SynchronizedAfterSuiteProc1Body func(SpecContext)
4444
SynchronizedAfterSuiteProc1BodyHasContext bool
4545

46-
ReportEachBody func(types.SpecReport)
46+
ReportEachBody func(SpecContext, types.SpecReport)
4747
ReportSuiteBody func(SpecContext, types.Report)
4848

4949
MarkedFocus bool
@@ -208,7 +208,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
208208
args = unrollInterfaceSlice(args)
209209

210210
remainingArgs := []interface{}{}
211-
//First get the CodeLocation up-to-date
211+
// First get the CodeLocation up-to-date
212212
for _, arg := range args {
213213
switch v := arg.(type) {
214214
case Offset:
@@ -224,11 +224,11 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
224224
trackedFunctionError := false
225225
args = remainingArgs
226226
remainingArgs = []interface{}{}
227-
//now process the rest of the args
227+
// now process the rest of the args
228228
for _, arg := range args {
229229
switch t := reflect.TypeOf(arg); {
230230
case t == reflect.TypeOf(float64(0)):
231-
break //ignore deprecated timeouts
231+
break // ignore deprecated timeouts
232232
case t == reflect.TypeOf(Focus):
233233
node.MarkedFocus = bool(arg.(focusType))
234234
if !nodeType.Is(types.NodeTypesForContainerAndIt) {
@@ -324,7 +324,12 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
324324
node.Body = func(SpecContext) { body() }
325325
} else if nodeType.Is(types.NodeTypeReportBeforeEach | types.NodeTypeReportAfterEach) {
326326
if node.ReportEachBody == nil {
327-
node.ReportEachBody = arg.(func(types.SpecReport))
327+
if fn, ok := arg.(func(types.SpecReport)); ok {
328+
node.ReportEachBody = func(_ SpecContext, r types.SpecReport) { fn(r) }
329+
} else {
330+
node.ReportEachBody = arg.(func(SpecContext, types.SpecReport))
331+
node.HasContext = true
332+
}
328333
} else {
329334
appendError(types.GinkgoErrors.MultipleBodyFunctions(node.CodeLocation, nodeType))
330335
trackedFunctionError = true
@@ -399,7 +404,7 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
399404
}
400405
}
401406

402-
//validations
407+
// validations
403408
if node.MarkedPending && node.MarkedFocus {
404409
appendError(types.GinkgoErrors.InvalidDeclarationOfFocusedAndPending(node.CodeLocation, nodeType))
405410
}

internal/node_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ var _ = Describe("Constructing nodes", func() {
142142
Ω(node.ID).Should(BeNumerically(">", 0))
143143
Ω(node.NodeType).Should(Equal(types.NodeTypeReportBeforeEach))
144144

145-
node.ReportEachBody(types.SpecReport{})
145+
node.ReportEachBody(internal.NewSpecContext(nil), types.SpecReport{})
146146
Ω(didRun).Should(BeTrue())
147147

148148
Ω(node.Body).Should(BeNil())
@@ -162,7 +162,7 @@ var _ = Describe("Constructing nodes", func() {
162162
Ω(node.ID).Should(BeNumerically(">", 0))
163163
Ω(node.NodeType).Should(Equal(types.NodeTypeReportAfterEach))
164164

165-
node.ReportEachBody(types.SpecReport{})
165+
node.ReportEachBody(internal.NewSpecContext(nil), types.SpecReport{})
166166
Ω(didRun).Should(BeTrue())
167167

168168
Ω(node.Body).Should(BeNil())
@@ -196,7 +196,7 @@ var _ = Describe("Constructing nodes", func() {
196196
cl := types.NewCodeLocation(2)
197197
cl2 := types.NewCustomCodeLocation("hi")
198198
node, errors := internal.NewNode(dt, ntIt, "text", body, cl2, Offset(1))
199-
//note that Offset overrides cl2
199+
// note that Offset overrides cl2
200200
Ω(node.CodeLocation.FileName).Should(Equal(cl.FileName))
201201
ExpectAllWell(errors)
202202
})
@@ -655,7 +655,7 @@ var _ = Describe("Constructing nodes", func() {
655655
})
656656

657657
var _ = Describe("Node", func() {
658-
//HERE - and all the fun edge cases
658+
// HERE - and all the fun edge cases
659659
Describe("The nodes that take more specific functions", func() {
660660
var dt *types.DeprecationTracker
661661
BeforeEach(func() {

internal/suite.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,8 @@ func (suite *Suite) reportEach(spec Spec, nodeType types.NodeType) {
594594
suite.writer.Truncate()
595595
suite.outputInterceptor.StartInterceptingOutput()
596596
report := suite.currentSpecReport
597-
nodes[i].Body = func(SpecContext) {
598-
nodes[i].ReportEachBody(report)
597+
nodes[i].Body = func(ctx SpecContext) {
598+
nodes[i].ReportEachBody(ctx, report)
599599
}
600600
state, failure := suite.runNode(nodes[i], time.Time{}, spec.Nodes.BestTextFor(nodes[i]))
601601

@@ -840,7 +840,7 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
840840
timeoutInPlay = "node"
841841
}
842842
if (!deadline.IsZero() && deadline.Before(now)) || interruptStatus.Interrupted() {
843-
//we're out of time already. let's wait for a NodeTimeout if we have it, or GracePeriod if we don't
843+
// we're out of time already. let's wait for a NodeTimeout if we have it, or GracePeriod if we don't
844844
if node.NodeTimeout > 0 {
845845
deadline = now.Add(node.NodeTimeout)
846846
timeoutInPlay = "node"
@@ -918,9 +918,9 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
918918
if outcomeFromRun != types.SpecStatePassed {
919919
additionalFailure := types.AdditionalFailure{
920920
State: outcomeFromRun,
921-
Failure: failure, //we make a copy - this will include all the configuration set up above...
921+
Failure: failure, // we make a copy - this will include all the configuration set up above...
922922
}
923-
//...and then we update the failure with the details from failureFromRun
923+
// ...and then we update the failure with the details from failureFromRun
924924
additionalFailure.Failure.Location, additionalFailure.Failure.ForwardedPanic, additionalFailure.Failure.TimelineLocation = failureFromRun.Location, failureFromRun.ForwardedPanic, failureFromRun.TimelineLocation
925925
additionalFailure.Failure.ProgressReport = types.ProgressReport{}
926926
if outcome == types.SpecStateTimedout {
@@ -959,7 +959,7 @@ func (suite *Suite) runNode(node Node, specDeadline time.Time, text string) (typ
959959
// tell the spec to stop. it's important we generate the progress report first to make sure we capture where
960960
// the spec is actually stuck
961961
sc.cancel(fmt.Errorf("%s timeout occurred", timeoutInPlay))
962-
//and now we wait for the grace period
962+
// and now we wait for the grace period
963963
gracePeriodChannel = time.After(gracePeriod)
964964
case <-interruptStatus.Channel:
965965
interruptStatus = suite.interruptHandler.Status()

reporting_dsl.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,34 +74,61 @@ func AddReportEntry(name string, args ...interface{}) {
7474

7575
/*
7676
ReportBeforeEach nodes are run for each spec, even if the spec is skipped or pending. ReportBeforeEach nodes take a function that
77-
receives a SpecReport. They are called before the spec starts.
77+
receives a SpecReport or both SpecContext and Report for interruptible behavior. They are called before the spec starts.
78+
79+
Example:
80+
81+
ReportBeforeEach(func(report SpecReport) { // process report })
82+
ReportBeforeEach(func(ctx SpecContext, report SpecReport) {
83+
// process report
84+
}), NodeTimeout(1 * time.Minute))
7885
7986
You cannot nest any other Ginkgo nodes within a ReportBeforeEach node's closure.
8087
You can learn more about ReportBeforeEach here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
88+
89+
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
8190
*/
82-
func ReportBeforeEach(body func(SpecReport), args ...interface{}) bool {
91+
func ReportBeforeEach(body any, args ...any) bool {
8392
combinedArgs := []interface{}{body}
8493
combinedArgs = append(combinedArgs, args...)
8594

8695
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeEach, "", combinedArgs...))
8796
}
8897

8998
/*
90-
ReportAfterEach nodes are run for each spec, even if the spec is skipped or pending. ReportAfterEach nodes take a function that
91-
receives a SpecReport. They are called after the spec has completed and receive the final report for the spec.
99+
ReportAfterEach nodes are run for each spec, even if the spec is skipped or pending.
100+
ReportAfterEach nodes take a function that receives a SpecReport or both SpecContext and Report for interruptible behavior.
101+
They are called after the spec has completed and receive the final report for the spec.
102+
103+
Example:
104+
105+
ReportAfterEach(func(report SpecReport) { // process report })
106+
ReportAfterEach(func(ctx SpecContext, report SpecReport) {
107+
// process report
108+
}), NodeTimeout(1 * time.Minute))
92109
93110
You cannot nest any other Ginkgo nodes within a ReportAfterEach node's closure.
94111
You can learn more about ReportAfterEach here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
112+
113+
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
95114
*/
96-
func ReportAfterEach(body func(SpecReport), args ...interface{}) bool {
115+
func ReportAfterEach(body any, args ...any) bool {
97116
combinedArgs := []interface{}{body}
98117
combinedArgs = append(combinedArgs, args...)
99118

100119
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportAfterEach, "", combinedArgs...))
101120
}
102121

103122
/*
104-
ReportBeforeSuite nodes are run at the beginning of the suite. ReportBeforeSuite nodes take a function that receives a suite Report.
123+
ReportBeforeSuite nodes are run at the beginning of the suite. ReportBeforeSuite nodes take a function
124+
that can either receive Report or both SpecContext and Report for interruptible behavior.
125+
126+
Example Usage:
127+
128+
ReportBeforeSuite(func(r Report) { // process report })
129+
ReportBeforeSuite(func(ctx SpecContext, r Report) {
130+
// process report
131+
}, NodeTimeout(1 * time.Minute))
105132
106133
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.
107134
ReportBeforeSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node)
@@ -112,21 +139,25 @@ You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
112139
You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
113140
114141
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
142+
143+
You can learn about interruptible nodes here: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes
115144
*/
116-
func ReportBeforeSuite(body func(Report), args ...interface{}) bool {
145+
func ReportBeforeSuite(body any, args ...any) bool {
117146
combinedArgs := []interface{}{body}
118147
combinedArgs = append(combinedArgs, args...)
119148
return pushNode(internal.NewNode(deprecationTracker, types.NodeTypeReportBeforeSuite, "", combinedArgs...))
120149
}
121150

122151
/*
123152
ReportAfterSuite nodes are run at the end of the suite. ReportAfterSuite nodes execute at the suite's conclusion,
124-
accepting a function that can either receives Report or both SpecContext and Report for interruptible behavior.
153+
and accept a function that can either receive Report or both SpecContext and Report for interruptible behavior.
125154
126155
Example Usage:
127156
128-
ReportAfterSuite("Non-interruptible ReportAfterSuite", func(r Report) { })
129-
ReportAfterSuite("Interruptible ReportAfterSuite", func(ctx SpecContext, r Report) { })
157+
ReportAfterSuite("Non-interruptible ReportAfterSuite", func(r Report) { // process report })
158+
ReportAfterSuite("Interruptible ReportAfterSuite", func(ctx SpecContext, r Report) {
159+
// process report
160+
}, NodeTimeout(1 * time.Minute))
130161
131162
They are called at the end of the suite, after all specs have run and any AfterSuite or SynchronizedAfterSuite nodes, and are passed in the final report for the suite.
132163
ReportAfterSuite nodes must be created at the top-level (i.e. not nested in a Context/Describe/When node)

0 commit comments

Comments
 (0)