Files
weave-scope/vendor/github.com/lsegal/gucumber/stepdef.go
2016-03-23 15:41:37 +00:00

213 lines
4.5 KiB
Go

package gucumber
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/lsegal/gucumber/gherkin"
)
var (
GlobalContext = Context{
Steps: []StepDefinition{},
World: map[string]interface{}{},
BeforeFilters: map[string]func(){},
AfterFilters: map[string]func(){},
Filters: []string{},
}
T Tester
World = GlobalContext.World
errNoMatchingStepFns = fmt.Errorf("no functions matched step.")
)
func Given(match string, fn interface{}) {
GlobalContext.Given(match, fn)
}
func Then(match string, fn interface{}) {
GlobalContext.Then(match, fn)
}
func When(match string, fn interface{}) {
GlobalContext.When(match, fn)
}
func And(match string, fn interface{}) {
GlobalContext.And(match, fn)
}
func Before(filter string, fn func()) {
GlobalContext.Before(filter, fn)
}
func After(filter string, fn func()) {
GlobalContext.After(filter, fn)
}
func BeforeMulti(filters []string, fn func()) {
GlobalContext.BeforeMulti(filters, fn)
}
func AfterMulti(filters []string, fn func()) {
GlobalContext.AfterMulti(filters, fn)
}
type Context struct {
Filters []string
World map[string]interface{}
BeforeFilters map[string]func()
AfterFilters map[string]func()
BeforeAllFilter func()
AfterAllFilter func()
Steps []StepDefinition
T Tester
}
func (c *Context) addStep(match string, fn interface{}) {
c.Steps = append(c.Steps, StepDefinition{
Matcher: regexp.MustCompile(match),
Function: reflect.ValueOf(fn),
})
}
func (c *Context) Given(match string, fn interface{}) {
c.addStep(match, fn)
}
func (c *Context) Then(match string, fn interface{}) {
c.addStep(match, fn)
}
func (c *Context) When(match string, fn interface{}) {
c.addStep(match, fn)
}
func (c *Context) And(match string, fn interface{}) {
c.addStep(match, fn)
}
func (c *Context) Before(filter string, fn func()) {
c.BeforeFilters[filter] = fn
}
func (c *Context) After(filter string, fn func()) {
c.AfterFilters[filter] = fn
}
func (c *Context) BeforeMulti(filters []string, fn func()) {
c.BeforeFilters[strings.Join(filters, "|")] = fn
}
func (c *Context) AfterMulti(filters []string, fn func()) {
c.AfterFilters[strings.Join(filters, "|")] = fn
}
func (c *Context) BeforeAll(fn func()) {
c.BeforeAllFilter = fn
}
func (c *Context) AfterAll(fn func()) {
c.AfterAllFilter = fn
}
func (c *Context) Execute(t Tester, line string, arg string) (bool, error) {
T = t
c.T = t
found := false
for _, step := range c.Steps {
f, err := step.CallIfMatch(c, t, line, arg)
if err != nil {
return f, err
}
if f {
found = true
}
}
return found, nil
}
type StepDefinition struct {
Matcher *regexp.Regexp
Function reflect.Value
}
func (s *StepDefinition) CallIfMatch(c *Context, test Tester, line string, arg string) (bool, error) {
if match := s.Matcher.FindStringSubmatch(line); match != nil {
match = match[1:] // discard full line match
// adjust arity if there is step arg data
numArgs := len(match)
if arg != "" {
numArgs++
}
t := s.Function.Type()
if t.NumIn() > 0 && t.In(0).Kind() == reflect.Ptr {
e := t.In(0).Elem()
if e.String() == "testing.T" {
numArgs++ // first param is *testing.T
}
}
if numArgs != t.NumIn() { // function has different arity
return true, fmt.Errorf("matcher function has different arity %d != %d",
numArgs, t.NumIn())
}
values := make([]reflect.Value, numArgs)
for m, i := 0, 0; i < t.NumIn(); i++ {
param := t.In(i)
var v interface{}
switch param.Kind() {
case reflect.Slice:
param = param.Elem()
if param.String() == "gherkin.TabularData" {
v = gherkin.StringData(arg).ToTable()
} else if param.Kind() == reflect.Slice && param.Elem().Kind() == reflect.String {
// just a raw [][]string slice
v = gherkin.StringData(arg).ToTable()
}
case reflect.Ptr:
if param.Elem().String() == "testing.T" {
v = test
}
case reflect.Int:
i, _ := strconv.ParseInt(match[m], 10, 32)
v = int(i)
m++
case reflect.Int64:
v, _ = strconv.ParseInt(match[m], 10, 64)
m++
case reflect.String:
// this could be from `arg`, check match index
if m >= len(match) {
v = arg
} else {
v = match[m]
m++
}
case reflect.Float64:
v, _ = strconv.ParseFloat(match[m], 64)
m++
}
if v == nil {
panic("type " + t.String() + "is not supported.")
}
values[i] = reflect.ValueOf(v)
}
s.Function.Call(values)
return true, nil
}
return false, nil
}