continue with output formatter implementation

Этот коммит содержится в:
gedi 2015-06-15 11:15:06 +03:00
родитель 3ee0746a3c
коммит b69fa26b8b
3 изменённых файлов: 173 добавлений и 51 удалений

Просмотреть файл

@ -1,6 +1,7 @@
package godog
import (
"flag"
"fmt"
"os"
"path/filepath"
@ -9,14 +10,37 @@ import (
"github.com/DATA-DOG/godog/gherkin"
)
type registeredFormatter struct {
name string
fmt Formatter
}
var formatters []*registeredFormatter
// RegisterFormatter registers a feature suite output
// Formatter for its given name
func RegisterFormatter(name string, f Formatter) {
formatters = append(formatters, &registeredFormatter{
name: name,
fmt: f,
})
}
var cfg config
func init() {
// @TODO: colorize flag help output
flag.StringVar(&cfg.featuresPath, "features", "features", "Path to feature files")
flag.StringVar(&cfg.formatterName, "formatter", "pretty", "Formatter name")
}
type config struct {
featuresPath string
formatterName string
}
func (c config) validate() error {
// feature path
inf, err := os.Stat(c.featuresPath)
if err != nil {
return err
@ -24,11 +48,20 @@ func (c config) validate() error {
if !inf.IsDir() {
return fmt.Errorf("feature path \"%s\" is not a directory.", c.featuresPath)
}
switch c.formatterName {
case "pretty":
// ok
default:
return fmt.Errorf("Unsupported formatter name: %s", c.formatterName)
// formatter
var found bool
var names []string
for _, f := range formatters {
if f.name == c.formatterName {
found = true
break
}
names = append(names, f.name)
}
if !found {
return fmt.Errorf(`unregistered formatter name: "%s", use one of: %s`, c.formatterName, strings.Join(names, ", "))
}
return nil
}
@ -46,7 +79,7 @@ func (c config) features() (lst []*gherkin.Feature, err error) {
})
}
func (c config) formatter() formatter {
func (c config) formatter() Formatter {
return &pretty{}
}

Просмотреть файл

@ -2,26 +2,93 @@ package godog
import (
"fmt"
"strings"
"github.com/DATA-DOG/godog/gherkin"
)
type formatter interface {
node(interface{})
func init() {
RegisterFormatter("pretty", &pretty{})
}
type pretty struct{}
// Formatter is an interface for feature runner output
type Formatter interface {
Node(interface{})
Failed(*gherkin.Step, error)
Passed(*gherkin.Step)
Skipped(*gherkin.Step)
Pending(*gherkin.Step)
}
func (f *pretty) node(node interface{}) {
type pretty struct {
feature *gherkin.Feature
scenario *gherkin.Scenario
doneBackground bool
background *gherkin.Background
}
func (f *pretty) line(tok *gherkin.Token) string {
return cl(fmt.Sprintf("#%s:%d", f.feature.Path, tok.Line), magenta)
}
func (f *pretty) canPrintStep(step *gherkin.Step) bool {
if f.background == nil {
return true
}
var backgroundStep bool
for _, s := range f.background.Steps {
if s == step {
backgroundStep = true
break
}
}
if !backgroundStep {
f.doneBackground = true
return true
}
return !f.doneBackground
}
func (f *pretty) Node(node interface{}) {
switch t := node.(type) {
case *gherkin.Feature:
fmt.Println(bcl("Feature: ", white) + t.Title)
fmt.Println(t.Description + "\n")
f.feature = t
f.doneBackground = false
f.scenario = nil
f.background = nil
fmt.Println("\n"+bcl("Feature: ", white)+t.Title, f.line(t.Token))
fmt.Println(t.Description)
case *gherkin.Background:
fmt.Println(bcl("Background:", white))
f.background = t
fmt.Println("\n" + bcl("Background:", white))
case *gherkin.Scenario:
fmt.Println(bcl("Scenario: ", white) + t.Title)
case *gherkin.Step:
fmt.Println(bcl(t.Token.Text, green))
fmt.Println("\n"+strings.Repeat(" ", t.Token.Indent)+bcl("Scenario: ", white)+t.Title, f.line(t.Token))
}
}
func (f *pretty) Passed(step *gherkin.Step) {
if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, green))
}
}
func (f *pretty) Skipped(step *gherkin.Step) {
if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, cyan))
}
}
func (f *pretty) Pending(step *gherkin.Step) {
if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, yellow))
}
}
func (f *pretty) Failed(step *gherkin.Step, err error) {
if f.canPrintStep(step) {
fmt.Println(cl(step.Token.Text, red))
}
}

Просмотреть файл

@ -64,6 +64,8 @@ func (f StepHandlerFunc) HandleStep(args ...Arg) error {
return f(args...)
}
var errPending = fmt.Errorf("pending step")
// Suite is an interface which allows various contexts
// to register step definitions and event handlers
type Suite interface {
@ -73,19 +75,13 @@ type Suite interface {
type suite struct {
steps map[*regexp.Regexp]StepHandler
features []*gherkin.Feature
fmt formatter
fmt Formatter
}
// New initializes a suite which supports the Suite
// interface. The instance is passed around to all
// context initialization functions from *_test.go files
func New() *suite {
// @TODO: colorize flag help output
flag.StringVar(&cfg.featuresPath, "features", "features", "Path to feature files")
flag.StringVar(&cfg.formatterName, "formatter", "pretty", "Formatter name")
if !flag.Parsed() {
flag.Parse()
}
return &suite{
steps: make(map[*regexp.Regexp]StepHandler),
}
@ -108,6 +104,11 @@ func (s *suite) Step(exp *regexp.Regexp, h StepHandler) {
// Run - runs a godog feature suite
func (s *suite) Run() {
var err error
if !flag.Parsed() {
flag.Parse()
}
fatal(cfg.validate())
s.fmt = cfg.formatter()
s.features, err = cfg.features()
fatal(err)
@ -120,7 +121,14 @@ func (s *suite) Run() {
}
}
func (s *suite) runStep(step *gherkin.Step) {
func (s *suite) runStep(step *gherkin.Step) (err error) {
defer func() {
if e := recover(); e != nil {
err = e.(error)
s.fmt.Failed(step, err)
}
}()
var handler StepHandler
var args []Arg
for r, h := range s.steps {
@ -133,41 +141,55 @@ func (s *suite) runStep(step *gherkin.Step) {
}
}
if handler == nil {
fmt.Println("PENDING")
return
s.fmt.Pending(step)
return errPending
}
defer func() {
if e := recover(); e != nil {
fmt.Println("PANIC")
}
}()
if err := handler.HandleStep(args...); err != nil {
fmt.Println("ERR")
if err = handler.HandleStep(args...); err != nil {
s.fmt.Failed(step, err)
} else {
fmt.Println("OK")
s.fmt.Passed(step)
}
return
}
func (s *suite) runSteps(steps []*gherkin.Step) bool {
var failed bool
for _, step := range steps {
if failed {
s.fmt.Skipped(step)
continue
}
if err := s.runStep(step); err != nil {
failed = true
}
}
return failed
}
func (s *suite) skipSteps(steps []*gherkin.Step) {
for _, step := range steps {
s.fmt.Skipped(step)
}
}
func (s *suite) runFeature(f *gherkin.Feature) {
s.fmt.node(f)
var background bool
s.fmt.Node(f)
var failed bool
for _, scenario := range f.Scenarios {
if f.Background != nil {
if !background {
s.fmt.node(f.Background)
}
for _, step := range f.Background.Steps {
s.runStep(step)
if !background {
s.fmt.node(step)
}
}
background = true
// background
// @TODO: do not print more than once
if f.Background != nil && !failed {
s.fmt.Node(f.Background)
failed = s.runSteps(f.Background.Steps)
}
s.fmt.node(scenario)
for _, step := range scenario.Steps {
s.runStep(step)
s.fmt.node(step)
// scenario
s.fmt.Node(scenario)
if failed {
s.skipSteps(scenario.Steps)
} else {
s.runSteps(scenario.Steps)
}
}
}