summaryrefslogtreecommitdiff
path: root/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go')
-rw-r--r--vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go117
1 files changed, 117 insertions, 0 deletions
diff --git a/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go b/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go
new file mode 100644
index 000000000..16cb66c3e
--- /dev/null
+++ b/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go
@@ -0,0 +1,117 @@
+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
+}