func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) { done := make(chan interface{}, 1) go func() { finished := false defer func() { if e := recover(); e != nil || !finished { r.failer.Panic(codelocation.New(2), e) select { case <-done: break default: close(done) } } }() r.asyncFunc(done) finished = true }() select { case <-done: case <-time.After(r.timeoutThreshold): r.failer.Timeout(r.codeLocation) } failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation) return }
//SynchronizedAfterSuite blocks complement the SynchronizedBeforeSuite blocks in solving the problem of setting up //external singleton resources shared across nodes when running tests in parallel. // //SynchronizedAfterSuite accomplishes this by taking *two* function arguments. The first runs on all nodes. The second runs only on parallel node #1 //and *only* after all other nodes have finished and exited. This ensures that node 1, and any resources it is running, remain alive until //all other nodes are finished. // //Both functions have the same signature: either func() or func(done Done) to run asynchronously. // //Here's a pseudo-code example that complements that given in SynchronizedBeforeSuite. Here, SynchronizedAfterSuite is used to tear down the shared database //only after all nodes have finished: // // var _ = SynchronizedAfterSuite(func() { // dbClient.Cleanup() // }, func() { // dbRunner.Stop() // }) func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool { globalSuite.SetSynchronizedAfterSuiteNode( allNodesBody, node1Body, codelocation.New(1), parseTimeout(timeout...), ) return true }
//Fail notifies Ginkgo that the current spec has failed. (Gomega will call Fail for you automatically when an assertion fails.) func Fail(message string, callerSkip ...int) { skip := 0 if len(callerSkip) > 0 { skip = callerSkip[0] } globalFailer.Fail(message, codelocation.New(skip+1)) panic(GINKGO_PANIC) }
func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) { finished := false defer func() { if e := recover(); e != nil || !finished { r.failer.Panic(codelocation.New(2), e) } failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation) }() r.syncFunc() finished = true return }
//AfterEach blocks are run after It blocks. When multiple AfterEach blocks are defined in nested //Describe and Context blocks the innermost AfterEach blocks are run first. // //Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func AfterEach(body interface{}, timeout ...float64) bool { globalSuite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true }
//BeforeSuite blocks are run just once before any specs are run. When running in parallel, each //parallel node process will call BeforeSuite. // //BeforeSuite blocks can be made asynchronous by providing a body function that accepts a Done channel // //You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. func BeforeSuite(body interface{}, timeout ...float64) bool { globalSuite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) return true }
//You can mark Maeasurements as pending using XMeasure func XMeasure(text string, _ ...interface{}) bool { globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) return true }
//You can focus individual Measures using FMeasure func FMeasure(text string, body interface{}, samples int) bool { globalSuite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) return true }
//You can mark Its as pending using XIt func XIt(text string, _ ...interface{}) bool { globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true }
//You can focus individual Its using FIt func FIt(text string, body interface{}, timeout ...float64) bool { globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) return true }
//You can mark the tests within a describe block as pending using XContext func XContext(text string, body func()) bool { globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true }
//You can focus the tests within a describe block using FDescribe func FDescribe(text string, body func()) bool { globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) return true }
//GinkgoRecover should be deferred at the top of any spawned goroutine that (may) call `Fail` //Since Gomega assertions call fail, you should throw a `defer GinkgoRecover()` at the top of any goroutine that //calls out to Gomega // //Here's why: Ginkgo's `Fail` method records the failure and then panics to prevent //further assertions from running. This panic must be recovered. Ginkgo does this for you //if the panic originates in a Ginkgo node (an It, BeforeEach, etc...) // //Unfortunately, if a panic originates on a goroutine *launched* from one of these nodes there's no //way for Ginkgo to rescue the panic. To do this, you must remember to `defer GinkgoRecover()` at the top of such a goroutine. func GinkgoRecover() { e := recover() if e != nil { globalFailer.Panic(codelocation.New(1), e) } }