arnaucube
arnaucube/mirror-to-giteaPublic Self-hosted repository
mirror-to-gitea/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go
vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.goRaw
  1package leafnodes
  2
  3import (
  4	"fmt"
  5	"reflect"
  6	"time"
  7
  8	"github.com/onsi/ginkgo/internal/codelocation"
  9	"github.com/onsi/ginkgo/internal/failer"
 10	"github.com/onsi/ginkgo/types"
 11)
 12
 13type runner struct {
 14	isAsync          bool
 15	asyncFunc        func(chan<- interface{})
 16	syncFunc         func()
 17	codeLocation     types.CodeLocation
 18	timeoutThreshold time.Duration
 19	nodeType         types.SpecComponentType
 20	componentIndex   int
 21	failer           *failer.Failer
 22}
 23
 24func newRunner(body interface{}, codeLocation types.CodeLocation, timeout time.Duration, failer *failer.Failer, nodeType types.SpecComponentType, componentIndex int) *runner {
 25	bodyType := reflect.TypeOf(body)
 26	if bodyType.Kind() != reflect.Func {
 27		panic(fmt.Sprintf("Expected a function but got something else at %v", codeLocation))
 28	}
 29
 30	runner := &runner{
 31		codeLocation:     codeLocation,
 32		timeoutThreshold: timeout,
 33		failer:           failer,
 34		nodeType:         nodeType,
 35		componentIndex:   componentIndex,
 36	}
 37
 38	switch bodyType.NumIn() {
 39	case 0:
 40		runner.syncFunc = body.(func())
 41		return runner
 42	case 1:
 43		if !(bodyType.In(0).Kind() == reflect.Chan && bodyType.In(0).Elem().Kind() == reflect.Interface) {
 44			panic(fmt.Sprintf("Must pass a Done channel to function at %v", codeLocation))
 45		}
 46
 47		wrappedBody := func(done chan<- interface{}) {
 48			bodyValue := reflect.ValueOf(body)
 49			bodyValue.Call([]reflect.Value{reflect.ValueOf(done)})
 50		}
 51
 52		runner.isAsync = true
 53		runner.asyncFunc = wrappedBody
 54		return runner
 55	}
 56
 57	panic(fmt.Sprintf("Too many arguments to function at %v", codeLocation))
 58}
 59
 60func (r *runner) run() (outcome types.SpecState, failure types.SpecFailure) {
 61	if r.isAsync {
 62		return r.runAsync()
 63	} else {
 64		return r.runSync()
 65	}
 66}
 67
 68func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) {
 69	done := make(chan interface{}, 1)
 70
 71	go func() {
 72		finished := false
 73
 74		defer func() {
 75			if e := recover(); e != nil || !finished {
 76				r.failer.Panic(codelocation.New(2), e)
 77				select {
 78				case <-done:
 79					break
 80				default:
 81					close(done)
 82				}
 83			}
 84		}()
 85
 86		r.asyncFunc(done)
 87		finished = true
 88	}()
 89
 90	// If this goroutine gets no CPU time before the select block,
 91	// the <-done case may complete even if the test took longer than the timeoutThreshold.
 92	// This can cause flaky behaviour, but we haven't seen it in the wild.
 93	select {
 94	case <-done:
 95	case <-time.After(r.timeoutThreshold):
 96		r.failer.Timeout(r.codeLocation)
 97	}
 98
 99	failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation)
100	return
101}
102func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) {
103	finished := false
104
105	defer func() {
106		if e := recover(); e != nil || !finished {
107			r.failer.Panic(codelocation.New(2), e)
108		}
109
110		failure, outcome = r.failer.Drain(r.nodeType, r.componentIndex, r.codeLocation)
111	}()
112
113	r.syncFunc()
114	finished = true
115
116	return
117}