minor optimizations, add []byte support for arguments
Этот коммит содержится в:
родитель
b15ff233e7
коммит
b3b02bc417
5 изменённых файлов: 187 добавлений и 166 удалений
|
@ -11,7 +11,6 @@ script:
|
|||
- go get github.com/golang/lint/golint
|
||||
|
||||
# pull all external dependencies
|
||||
# remove them at all if possible
|
||||
- go get github.com/cucumber/gherkin-go
|
||||
- go get golang.org/x/tools/imports
|
||||
- go get github.com/shiena/ansicolor
|
||||
|
@ -20,10 +19,10 @@ script:
|
|||
- go test -v ./...
|
||||
- go test -race ./...
|
||||
|
||||
# test me with myself
|
||||
# run features
|
||||
- go run cmd/godog/main.go -f progress
|
||||
|
||||
# code correctness
|
||||
- sh -c 'if [ ! -z "$(go fmt ./...)" ]; then exit 1; fi'
|
||||
- golint ./...
|
||||
- sh -c 'RES="$(go fmt ./...)"; if [ ! -z "$RES" ]; then echo $RES; exit 1; fi'
|
||||
- sh -c 'RES="$(golint ./...)"; if [ ! -z "$RES" ]; then echo $RES; exit 1; fi'
|
||||
- go vet ./...
|
||||
|
|
|
@ -121,7 +121,7 @@ The public API is stable enough, but it may break until **1.0.0** version, see `
|
|||
### FAQ
|
||||
|
||||
**Q:** Where can I configure common options globally?
|
||||
**A:** You can't. Alias your common or project based commands: `alias mygodog="godog --format=progress"`
|
||||
**A:** You can't. Alias your common or project based commands: `alias mygodog="godog --format=progress --tags=@wip"`
|
||||
|
||||
### Contributions
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ package godog
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -230,9 +228,7 @@ func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) {
|
|||
} else {
|
||||
text = cl(ostep.Text, cyan)
|
||||
}
|
||||
// use reflect to get step handler function name
|
||||
name := runtime.FuncForPC(reflect.ValueOf(def.Handler).Pointer()).Name()
|
||||
text += s(f.commentPos-f.length(ostep)+1) + cl(fmt.Sprintf("# %s", name), black)
|
||||
text += s(f.commentPos-f.length(ostep)+1) + cl(fmt.Sprintf("# %s", def.funcName()), black)
|
||||
} else {
|
||||
text = cl(ostep.Text, cyan)
|
||||
}
|
||||
|
@ -285,9 +281,7 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
|
|||
} else {
|
||||
text += cl(step.Text, c)
|
||||
}
|
||||
// use reflect to get step handler function name
|
||||
name := runtime.FuncForPC(reflect.ValueOf(def.Handler).Pointer()).Name()
|
||||
text += s(f.commentPos-f.length(step)+1) + cl(fmt.Sprintf("# %s", name), black)
|
||||
text += s(f.commentPos-f.length(step)+1) + cl(fmt.Sprintf("# %s", def.funcName()), black)
|
||||
default:
|
||||
text += cl(step.Text, c)
|
||||
}
|
||||
|
@ -297,7 +291,11 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c color) {
|
|||
case *gherkin.DataTable:
|
||||
f.printTable(t, c)
|
||||
case *gherkin.DocString:
|
||||
fmt.Println(s(f.indent*3) + cl(t.Delimitter, c)) // @TODO: content type
|
||||
var ct string
|
||||
if len(t.ContentType) > 0 {
|
||||
ct = " " + cl(t.ContentType, c)
|
||||
}
|
||||
fmt.Println(s(f.indent*3) + cl(t.Delimitter, c) + ct)
|
||||
for _, ln := range strings.Split(t.Content, "\n") {
|
||||
fmt.Println(s(f.indent*3) + cl(ln, c))
|
||||
}
|
||||
|
|
166
stepdef.go
Обычный файл
166
stepdef.go
Обычный файл
|
@ -0,0 +1,166 @@
|
|||
package godog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/cucumber/gherkin-go"
|
||||
)
|
||||
|
||||
// StepDef is a registered step definition
|
||||
// contains a StepHandler and regexp which
|
||||
// is used to match a step. Args which
|
||||
// were matched by last executed step
|
||||
//
|
||||
// This structure is passed to the formatter
|
||||
// when step is matched and is either failed
|
||||
// or successful
|
||||
type StepDef struct {
|
||||
args []interface{}
|
||||
hv reflect.Value
|
||||
Expr *regexp.Regexp
|
||||
Handler interface{}
|
||||
}
|
||||
|
||||
func (sd *StepDef) funcName() string {
|
||||
return runtime.FuncForPC(sd.hv.Pointer()).Name()
|
||||
}
|
||||
|
||||
// run a step with the matched arguments using
|
||||
// reflect
|
||||
func (sd *StepDef) run() error {
|
||||
typ := sd.hv.Type()
|
||||
if len(sd.args) < typ.NumIn() {
|
||||
return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args))
|
||||
}
|
||||
var values []reflect.Value
|
||||
for i := 0; i < typ.NumIn(); i++ {
|
||||
param := typ.In(i)
|
||||
switch param.Kind() {
|
||||
case reflect.Int:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int(v)))
|
||||
case reflect.Int64:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int64: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int64(v)))
|
||||
case reflect.Int32:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int32: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int32(v)))
|
||||
case reflect.Int16:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int16: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int16(v)))
|
||||
case reflect.Int8:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 8)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int8: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int8(v)))
|
||||
case reflect.String:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values = append(values, reflect.ValueOf(s))
|
||||
case reflect.Float64:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float64: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
case reflect.Float32:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float32: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(float32(v)))
|
||||
case reflect.Ptr:
|
||||
arg := sd.args[i]
|
||||
switch param.Elem().String() {
|
||||
case "gherkin.DocString":
|
||||
v, ok := arg.(*gherkin.DocString)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
case "gherkin.DataTable":
|
||||
v, ok := arg.(*gherkin.DataTable)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
default:
|
||||
return fmt.Errorf("the argument %d type %T is not supported", i, arg)
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch param {
|
||||
case typeOfBytes:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values = append(values, reflect.ValueOf([]byte(s)))
|
||||
default:
|
||||
return fmt.Errorf("the slice argument %d type %s is not supported", i, param.Kind())
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind())
|
||||
}
|
||||
}
|
||||
ret := sd.hv.Call(values)[0].Interface()
|
||||
if nil == ret {
|
||||
return nil
|
||||
}
|
||||
return ret.(error)
|
||||
}
|
||||
|
||||
func (sd *StepDef) shouldBeString(idx int) (string, error) {
|
||||
arg := sd.args[idx]
|
||||
s, ok := arg.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)
|
||||
}
|
||||
return s, nil
|
||||
}
|
162
suite.go
162
suite.go
|
@ -14,6 +14,7 @@ import (
|
|||
)
|
||||
|
||||
var errorInterface = reflect.TypeOf((*error)(nil)).Elem()
|
||||
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
||||
|
||||
type feature struct {
|
||||
*gherkin.Feature
|
||||
|
@ -23,144 +24,6 @@ type feature struct {
|
|||
// ErrUndefined is returned in case if step definition was not found
|
||||
var ErrUndefined = fmt.Errorf("step is undefined")
|
||||
|
||||
// StepDef is a registered step definition
|
||||
// contains a StepHandler and regexp which
|
||||
// is used to match a step. Args which
|
||||
// were matched by last executed step
|
||||
//
|
||||
// This structure is passed to the formatter
|
||||
// when step is matched and is either failed
|
||||
// or successful
|
||||
type StepDef struct {
|
||||
args []interface{}
|
||||
hv reflect.Value
|
||||
Expr *regexp.Regexp
|
||||
Handler interface{}
|
||||
}
|
||||
|
||||
func (sd *StepDef) run() error {
|
||||
typ := sd.hv.Type()
|
||||
if len(sd.args) < typ.NumIn() {
|
||||
return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args))
|
||||
}
|
||||
var values []reflect.Value
|
||||
for i := 0; i < typ.NumIn(); i++ {
|
||||
param := typ.In(i)
|
||||
switch param.Kind() {
|
||||
case reflect.Int:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int(v)))
|
||||
case reflect.Int64:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int64: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int64(v)))
|
||||
case reflect.Int32:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int32: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int32(v)))
|
||||
case reflect.Int16:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int16: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int16(v)))
|
||||
case reflect.Int8:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 8)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int8: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(int8(v)))
|
||||
case reflect.String:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values = append(values, reflect.ValueOf(s))
|
||||
case reflect.Float64:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float64: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
case reflect.Float32:
|
||||
s, err := sd.shouldBeString(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float32: %s`, i, s, err)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(float32(v)))
|
||||
case reflect.Ptr:
|
||||
arg := sd.args[i]
|
||||
switch param.Elem().String() {
|
||||
case "gherkin.DocString":
|
||||
v, ok := arg.(*gherkin.DocString)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
case "gherkin.DataTable":
|
||||
v, ok := arg.(*gherkin.DataTable)
|
||||
if !ok {
|
||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg)
|
||||
}
|
||||
values = append(values, reflect.ValueOf(v))
|
||||
default:
|
||||
return fmt.Errorf("the argument %d type %T is not supported", i, arg)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind())
|
||||
}
|
||||
}
|
||||
ret := sd.hv.Call(values)[0].Interface()
|
||||
if nil == ret {
|
||||
return nil
|
||||
}
|
||||
return ret.(error)
|
||||
}
|
||||
|
||||
func (sd *StepDef) shouldBeString(idx int) (string, error) {
|
||||
arg := sd.args[idx]
|
||||
s, ok := arg.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Suite is an interface which allows various contexts
|
||||
// to register steps and event handlers.
|
||||
//
|
||||
|
@ -207,9 +70,9 @@ type Suite interface {
|
|||
}
|
||||
|
||||
type suite struct {
|
||||
stepHandlers []*StepDef
|
||||
features []*feature
|
||||
fmt Formatter
|
||||
steps []*StepDef
|
||||
features []*feature
|
||||
fmt Formatter
|
||||
|
||||
failed bool
|
||||
|
||||
|
@ -273,7 +136,7 @@ func (s *suite) Step(expr interface{}, stepFunc interface{}) {
|
|||
if typ.Out(0).Kind() != reflect.Interface || !typ.Out(0).Implements(errorInterface) {
|
||||
panic(fmt.Sprintf("expected handler to return an error interface, but we have: %s", typ.Out(0).Kind()))
|
||||
}
|
||||
s.stepHandlers = append(s.stepHandlers, &StepDef{
|
||||
s.steps = append(s.steps, &StepDef{
|
||||
Handler: stepFunc,
|
||||
Expr: regex,
|
||||
hv: v,
|
||||
|
@ -403,7 +266,7 @@ func (s *suite) run() {
|
|||
}
|
||||
|
||||
func (s *suite) matchStep(step *gherkin.Step) *StepDef {
|
||||
for _, h := range s.stepHandlers {
|
||||
for _, h := range s.steps {
|
||||
if m := h.Expr.FindStringSubmatch(step.Text); len(m) > 0 {
|
||||
var args []interface{}
|
||||
for _, m := range m[1:] {
|
||||
|
@ -473,11 +336,6 @@ func (s *suite) skipSteps(steps []*gherkin.Step) {
|
|||
}
|
||||
|
||||
func (s *suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (err error) {
|
||||
// run before scenario handlers
|
||||
defer func() {
|
||||
// run after scenario handlers
|
||||
}()
|
||||
|
||||
s.fmt.Node(outline)
|
||||
|
||||
for _, example := range outline.Examples {
|
||||
|
@ -584,17 +442,17 @@ func (s *suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (
|
|||
|
||||
func (s *suite) printStepDefinitions() {
|
||||
var longest int
|
||||
for _, def := range s.stepHandlers {
|
||||
for _, def := range s.steps {
|
||||
if longest < len(def.Expr.String()) {
|
||||
longest = len(def.Expr.String())
|
||||
}
|
||||
}
|
||||
for _, def := range s.stepHandlers {
|
||||
location := runtime.FuncForPC(reflect.ValueOf(def.Handler).Pointer()).Name()
|
||||
for _, def := range s.steps {
|
||||
location := runtime.FuncForPC(def.hv.Pointer()).Name()
|
||||
spaces := strings.Repeat(" ", longest-len(def.Expr.String()))
|
||||
fmt.Println(cl(def.Expr.String(), yellow)+spaces, cl("# "+location, black))
|
||||
}
|
||||
if len(s.stepHandlers) == 0 {
|
||||
if len(s.steps) == 0 {
|
||||
fmt.Println("there were no contexts registered, could not find any step definition..")
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче