-
-
Notifications
You must be signed in to change notification settings - Fork 684
Description
DefaultReporter.emitTimeline can panic with slice bounds out of range when timeline entries arrive out of order relative to CapturedGinkgoWriterOutput (gw). The code slices using gw[cursor:tl.Offset] after only checking tl.Offset < len(gw). If tl.Offset < cursor, the slice start > end triggers a panic.
Actual behavior
Occasional panic in reporter:
panic: runtime error: slice bounds out of range [start:cursor] with capacity len
at ginkgo/v2/reporters/default_reporter.go:emitTimeline
r.emit(r.fi(indent, "%s", gw[cursor:tl.Offset]))
Expected behavior
Reporter should never panic; it should tolerate out-of-order or beyond-end offsets and still emit the buffered writer output.
Root cause
emitTimeline assumes cursor ≤ tl.Offset ≤ len(gw). With concurrency, timeline entries can be recorded/emitted out of order, so tl.Offset can be less than cursor, violating the assumption.
Minimal fix:
func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, timeline types.Timeline) {
isVeryVerbose := r.conf.Verbosity().Is(types.VerbosityLevelVeryVerbose)
gw := report.CapturedGinkgoWriterOutput
cursor := 0
for _, entry := range timeline {
tl := entry.GetTimelineLocation()
end := tl.Offset
if end > len(gw) { end = len(gw) }
if end < cursor { end = cursor }
if cursor < end {
r.emit(r.fi(indent, "%s", gw[cursor:end]))
cursor = end
} else if cursor < len(gw) && end == len(gw) {
r.emit(r.fi(indent, "%s", gw[cursor:]))
cursor = len(gw)
}
switch x := entry.(type) {
case types.Failure:
if isVeryVerbose { r.emitFailure(indent, report.State, x, false) } else { r.emitShortFailure(indent, report.State, x) }
case types.AdditionalFailure:
if isVeryVerbose { r.emitFailure(indent, x.State, x.Failure, true) } else { r.emitShortFailure(indent, x.State, x.Failure) }
case types.ReportEntry:
r.emitReportEntry(indent, x)
case types.ProgressReport:
r.emitProgressReport(indent, false, x)
case types.SpecEvent:
if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents {
r.emitSpecEvent(indent, x, isVeryVerbose)
}
}
}
if cursor < len(gw) {
r.emit(r.fi(indent, "%s", gw[cursor:]))
}
}
Reproduceable steps:
Create multiple goroutines that (a) write to GinkgoWriter, (b) emit timeline entries, and (c) complete in an order different from creation—this can yield tl.Offset < cursor for a later-emitted entry.