123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- package leafnodes
- import (
- "fmt"
- "reflect"
- "time"
- "github.com/onsi/ginkgo/internal/codelocation"
- "github.com/onsi/ginkgo/internal/failer"
- "github.com/onsi/ginkgo/types"
- )
- type runner struct {
- isAsync bool
- asyncFunc func(chan<- interface{})
- syncFunc func()
- codeLocation types.CodeLocation
- timeoutThreshold time.Duration
- nodeType types.SpecComponentType
- componentIndex int
- failer *failer.Failer
- }
- func newRunner(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, nodeType types.SpecComponentType, componentIndex int) *runner {
- bodyType := reflect.TypeOf(body)
- if bodyType.Kind() != reflect.Func {
- panic(fmt.Sprintf("Expected a function but got something else at %v", codeLocation))
- }
- runner := &runner{
- codeLocation: codeLocation,
- timeoutThreshold: timeout,
- failer: failer,
- nodeType: nodeType,
- componentIndex: componentIndex,
- }
- switch bodyType.NumIn() {
- case 0:
- runner.syncFunc = body.(func())
- return runner
- case 1:
- if !(bodyType.In(0).Kind() == reflect.Chan && bodyType.In(0).Elem().Kind() == reflect.Interface) {
- panic(fmt.Sprintf("Must pass a Done channel to function at %v", codeLocation))
- }
- wrappedBody := func(done chan<- interface{}) {
- bodyValue := reflect.ValueOf(body)
- bodyValue.Call([]reflect.Value{reflect.ValueOf(done)})
- }
- runner.isAsync = true
- runner.asyncFunc = wrappedBody
- return runner
- }
- panic(fmt.Sprintf("Too many arguments to function at %v", codeLocation))
- }
- func (r *runner) run() (outcome types.SpecState, failure types.SpecFailure) {
- if r.isAsync {
- return r.runAsync()
- } else {
- return r.runSync()
- }
- }
- 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
- }()
- // If this goroutine gets no CPU time before the select block,
- // the <-done case may complete even if the test took longer than the timeoutThreshold.
- // This can cause flaky behaviour, but we haven't seen it in the wild.
- 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
- }
- 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
- }
|