1 package internal 2 3 import ( 4 "context" 5 6 "github.com/onsi/ginkgo/v2/types" 7 ) 8 9 type SpecContext interface { 10 context.Context 11 12 SpecReport() types.SpecReport 13 AttachProgressReporter(func() string) func() 14 } 15 16 type specContext struct { 17 context.Context 18 *ProgressReporterManager 19 20 cancel context.CancelCauseFunc 21 22 suite *Suite 23 } 24 25 /* 26 SpecContext includes a reference to `suite` and embeds itself in itself as a "GINKGO_SPEC_CONTEXT" value. This allows users to create child Contexts without having down-stream consumers (e.g. Gomega) lose access to the SpecContext and its methods. This allows us to build extensions on top of Ginkgo that simply take an all-encompassing context. 27 28 Note that while SpecContext is used to enforce deadlines by Ginkgo it is not configured as a context.WithDeadline. Instead, Ginkgo owns responsibility for cancelling the context when the deadline elapses. 29 30 This is because Ginkgo needs finer control over when the context is canceled. Specifically, Ginkgo needs to generate a ProgressReport before it cancels the context to ensure progress is captured where the spec is currently running. The only way to avoid a race here is to manually control the cancellation. 31 */ 32 func NewSpecContext(suite *Suite) *specContext { 33 ctx, cancel := context.WithCancelCause(context.Background()) 34 sc := &specContext{ 35 cancel: cancel, 36 suite: suite, 37 ProgressReporterManager: NewProgressReporterManager(), 38 } 39 ctx = context.WithValue(ctx, "GINKGO_SPEC_CONTEXT", sc) //yes, yes, the go docs say don't use a string for a key... but we'd rather avoid a circular dependency between Gomega and Ginkgo 40 sc.Context = ctx //thank goodness for garbage collectors that can handle circular dependencies 41 42 return sc 43 } 44 45 func (sc *specContext) SpecReport() types.SpecReport { 46 return sc.suite.CurrentSpecReport() 47 } 48