minor optimizations, add []byte support for arguments

Этот коммит содержится в:
gedi 2015-06-28 11:37:05 +03:00
родитель 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 Обычный файл
Просмотреть файл

@ -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
Просмотреть файл

@ -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..")
}
}