-
Notifications
You must be signed in to change notification settings - Fork 9
/
step_def.go
109 lines (88 loc) · 2.43 KB
/
step_def.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package gocuke
import (
"fmt"
"reflect"
"regexp"
"runtime"
"testing"
"gotest.tools/v3/assert"
)
type stepDef struct {
regex *regexp.Regexp
theFunc reflect.Value
specialArgs []*specialArg
funcLoc string
}
type specialArg struct {
typ reflect.Type
getValue specialArgGetter
}
type specialArgGetter func(*scenarioRunner) interface{}
// Step can be used to manually register a step with the runner. step should
// be a string or *regexp.Regexp instance. definition should be a function
// which takes special step arguments first and then regular step arguments
// next (with string, int64, *big.Int, and *apd.Decimal
// as valid types) and gocuke.DocString or gocuke.DataTable
// as the last argument if this step uses a doc string or data table respectively.
// Custom step definitions will always take priority of auto-discovered step
// definitions.
func (r *Runner) Step(step interface{}, definition interface{}) *Runner {
r.topLevelT.Helper()
exp, ok := step.(*regexp.Regexp)
if !ok {
str, ok := step.(string)
if !ok {
r.topLevelT.Fatalf("expected step %v to be a string or regex", step)
}
var err error
exp, err = regexp.Compile(str)
assert.NilError(r.topLevelT, err)
}
_ = r.addStepDef(r.topLevelT, exp, reflect.ValueOf(definition))
return r
}
func (r *Runner) addStepDef(t *testing.T, exp *regexp.Regexp, definition reflect.Value) *stepDef {
t.Helper()
def := r.newStepDefOrHook(t, exp, definition)
r.stepDefs = append(r.stepDefs, def)
return def
}
func (r *Runner) newStepDefOrHook(t *testing.T, exp *regexp.Regexp, f reflect.Value) *stepDef {
t.Helper()
typ := f.Type()
if typ.Kind() != reflect.Func {
t.Fatalf("expected step method, got %s", f)
}
funcPtr := f.Pointer()
rfunc := runtime.FuncForPC(funcPtr)
file, line := rfunc.FileLine(funcPtr)
def := &stepDef{
regex: exp,
theFunc: f,
funcLoc: fmt.Sprintf("%s (%s:%d)", rfunc.Name(), file, line),
}
for i := 0; i < typ.NumIn(); i++ {
stepTyp := typ.In(i)
getter, ok := r.supportedSpecialArgs[stepTyp]
if !ok {
// expect remaining args to be step arguments
break
}
def.specialArgs = append(def.specialArgs, &specialArg{
typ: stepTyp,
getValue: getter,
})
}
if typ.NumOut() != 0 {
t.Fatalf("expected 0 out parameters for method %+v", f.String())
}
return def
}
func (s stepDef) usesRapid() bool {
for _, arg := range s.specialArgs {
if arg.typ == rapidTType {
return true
}
}
return false
}