Skip to content

Commit 606c1cb

Browse files
Stacey Birleyonsi
authored andcommitted
Fix Ginkgo Reporter slice-bounds panic
1 parent a6463b3 commit 606c1cb

File tree

3 files changed

+69
-38
lines changed

3 files changed

+69
-38
lines changed

integration/flags_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package integration_test
22

33
import (
44
"strings"
5+
"time"
56

67
. "github.com/onsi/ginkgo/v2"
78
. "github.com/onsi/gomega"
@@ -46,7 +47,7 @@ var _ = Describe("Flags Specs", func() {
4647
Ω(output).Should(ContainSubstring("Detected pending specs and --fail-on-pending is set"))
4748
})
4849

49-
It("should run the race detector when told to", Label("slow"), func() {
50+
It("should run the race detector when told to", Label("slow"), NodeTimeout(2*time.Minute), func(ctx SpecContext) {
5051
if !raceDetectorSupported() {
5152
Skip("race detection is not supported")
5253
}

reporters/default_reporter.go

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -377,43 +377,40 @@ func (r *DefaultReporter) humanReadableState(state types.SpecState) string {
377377

378378
func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, timeline types.Timeline) {
379379
isVeryVerbose := r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose)
380-
gw := report.CapturedGinkgoWriterOutput
381-
cursor := 0
382-
for _, entry := range timeline {
383-
tl := entry.GetTimelineLocation()
384-
if tl.Offset < len(gw) {
385-
r.emit(r.fi(indent, "%s", gw[cursor:tl.Offset]))
386-
cursor = tl.Offset
387-
} else if cursor < len(gw) {
388-
r.emit(r.fi(indent, "%s", gw[cursor:]))
389-
cursor = len(gw)
390-
}
391-
switch x := entry.(type) {
392-
case types.Failure:
393-
if isVeryVerbose {
394-
r.emitFailure(indent, report.State, x, false)
395-
} else {
396-
r.emitShortFailure(indent, report.State, x)
397-
}
398-
case types.AdditionalFailure:
399-
if isVeryVerbose {
400-
r.emitFailure(indent, x.State, x.Failure, true)
401-
} else {
402-
r.emitShortFailure(indent, x.State, x.Failure)
403-
}
404-
case types.ReportEntry:
405-
r.emitReportEntry(indent, x)
406-
case types.ProgressReport:
407-
r.emitProgressReport(indent, false, false, x)
408-
case types.SpecEvent:
409-
if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents {
410-
r.emitSpecEvent(indent, x, isVeryVerbose)
411-
}
412-
}
413-
}
414-
if cursor < len(gw) {
415-
r.emit(r.fi(indent, "%s", gw[cursor:]))
416-
}
380+
gw := report.CapturedGinkgoWriterOutput
381+
cursor := 0
382+
for _, entry := range timeline {
383+
tl := entry.GetTimelineLocation()
384+
385+
end := tl.Offset
386+
if end > len(gw) { end = len(gw) }
387+
if end < cursor { end = cursor }
388+
if cursor < end {
389+
r.emit(r.fi(indent, "%s", gw[cursor:end]))
390+
cursor = end
391+
} else if cursor < len(gw) && end == len(gw) {
392+
r.emit(r.fi(indent, "%s", gw[cursor:]))
393+
cursor = len(gw)
394+
}
395+
396+
switch x := entry.(type) {
397+
case types.Failure:
398+
if isVeryVerbose { r.emitFailure(indent, report.State, x, false) } else { r.emitShortFailure(indent, report.State, x) }
399+
case types.AdditionalFailure:
400+
if isVeryVerbose { r.emitFailure(indent, x.State, x.Failure, true) } else { r.emitShortFailure(indent, x.State, x.Failure) }
401+
case types.ReportEntry:
402+
r.emitReportEntry(indent, x)
403+
case types.ProgressReport:
404+
r.emitProgressReport(indent, false, isVeryVerbose, x)
405+
case types.SpecEvent:
406+
if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents {
407+
r.emitSpecEvent(indent, x, isVeryVerbose)
408+
}
409+
}
410+
}
411+
if cursor < len(gw) {
412+
r.emit(r.fi(indent, "%s", gw[cursor:]))
413+
}
417414
}
418415

419416
func (r *DefaultReporter) EmitFailure(state types.SpecState, failure types.Failure) {

reporters/default_reporter_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,39 @@ var _ = Describe("DefaultReporter", func() {
15531553
DELIMITER,
15541554
""),
15551555
),
1556+
Entry("a passing test with out-of-order timeline offsets (clamped safely)",
1557+
// Build a report whose timeline offsets go forward, then backward, then forward again.
1558+
// GW text is 3 lines: L1\nL2\nL3\n
1559+
S(
1560+
types.NodeTypeIt, "A", cl0, types.SpecStatePassed,
1561+
GW("L1\nL2\nL3\n"),
1562+
// tl.Offset = len("L1\n") -> forward
1563+
SE(types.SpecEventNodeStart, types.NodeTypeIt, "A", cl0, TL("L1\n")),
1564+
// tl.Offset = len("L1\nL2\n") -> further forward
1565+
SE(types.SpecEventByStart, "Step", cl1, TL("L1\nL2\n")),
1566+
// tl.Offset = len("L1\n") -> BACKWARD (this used to panic without clamping)
1567+
SE(types.SpecEventByEnd, "Step", cl1, time.Millisecond*100, TL("L1\n")),
1568+
// tl.Offset = len("L1\nL2\nL3\n") -> final
1569+
SE(types.SpecEventNodeEnd, types.NodeTypeIt, "A", cl0, time.Millisecond*100, TL("L1\nL2\nL3\n")),
1570+
),
1571+
// We assert using a config that shows the Timeline (Verbose|Parallel),
1572+
// matching the existing style of expectations in this table.
1573+
Case(Verbose|Parallel,
1574+
DELIMITER,
1575+
spr("{{green}}%s [1.000 seconds]{{/}}", DENOTER),
1576+
"{{green}}{{bold}}A{{/}}",
1577+
"{{gray}}cl0.go:12{{/}}",
1578+
"",
1579+
" {{gray}}Timeline >>{{/}}",
1580+
" L1",
1581+
" L2",
1582+
spr(" {{bold}}STEP:{{/}} Step {{gray}}@ %s{{/}}", FORMATTED_TIME),
1583+
" L3",
1584+
" {{gray}}<< Timeline{{/}}",
1585+
DELIMITER,
1586+
"",
1587+
),
1588+
),
15561589
Entry("a failed test with timeline entries",
15571590
S(types.NodeTypeIt, CTS("A", "B"), CLS(cl0, cl1), "C", cl2, types.SpecStateInterrupted, SE(types.SpecEventByStart, "a by step", cl0),
15581591
F("failure\nmessage", cl3, types.FailureNodeIsLeafNode, FailureNodeLocation(cl2), types.NodeTypeIt, TL(0),

0 commit comments

Comments
 (0)