Skip to content

Commit

Permalink
Remove deprecation from flags, update example to use CLI flags (cucum…
Browse files Browse the repository at this point in the history
…ber#498)

* Remove deprecation from flags, update example to use CLI flags

* Add comment to ShowHelp option

* Fix test

* Update CHANGELOG.md
  • Loading branch information
vearutop authored and laurazard committed Sep 11, 2022
1 parent 2028828 commit 9da9f2d
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 83 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt

### Changed
- README example is updated with `context.Context` and `go test` usage. ([477](https://github.com/cucumber/godog/pull/477) - [vearutop](https://github.com/vearutop))
- Removed deprecation of `godog.BindFlags` ([498](https://github.com/cucumber/godog/pull/498) - [vearutop](https://github.com/vearutop))

### Fixed
- Fixed a bug which would ignore the context returned from a substep.([488](https://github.com/cucumber/godog/pull/488) - [wichert](https://github.com/wichert))
Expand Down
6 changes: 5 additions & 1 deletion _examples/godogs/features/godogs.feature
@@ -1,4 +1,3 @@
# file: $GOPATH/godogs/features/godogs.feature
Feature: eat godogs
In order to be happy
As a hungry gopher
Expand All @@ -8,3 +7,8 @@ Feature: eat godogs
Given there are 12 godogs
When I eat 5
Then there should be 7 remaining

Scenario: Eat 12 out of 12
Given there are 12 godogs
When I eat 12
Then there should be none remaining
14 changes: 14 additions & 0 deletions _examples/godogs/features/nodogs.feature
@@ -0,0 +1,14 @@
Feature: do not eat godogs
In order to be fit
As a well-fed gopher
I need to be able to avoid godogs

Scenario: Eat 0 out of 12
Given there are 12 godogs
When I eat 0
Then there should be 12 remaining

Scenario: Eat 0 out of 0
Given there are 0 godogs
When I eat 0
Then there should be none remaining
6 changes: 2 additions & 4 deletions _examples/godogs/godogs.go
@@ -1,6 +1,4 @@
package main
package godogs

// Godogs available to eat
// Godogs available to eat.
var Godogs int

func main() { /* usual main func */ }
38 changes: 30 additions & 8 deletions _examples/godogs/godogs_test.go
@@ -1,34 +1,51 @@
package main
package godogs

// This example shows how to set up test suite runner with Go subtests and godog command line parameters.
// Sample commands:
// * run all scenarios from default directory (features): go test -test.run "^TestFeatures/"
// * run all scenarios and list subtest names: go test -test.v -test.run "^TestFeatures/"
// * run all scenarios from one feature file: go test -test.v -godog.paths features/nodogs.feature -test.run "^TestFeatures/"
// * run all scenarios from multiple feature files: go test -test.v -godog.paths features/nodogs.feature,features/godogs.feature -test.run "^TestFeatures/"
// * run single scenario as a subtest: go test -test.v -test.run "^TestFeatures/Eat_5_out_of_12$"
// * show usage help: go test -godog.help
// * show usage help if there were other test files in directory: go test -godog.help godogs_test.go
// * run scenarios with multiple formatters: go test -test.v -godog.format cucumber:cuc.json,pretty -test.run "^TestFeatures/"

import (
"context"
"flag"
"fmt"
"os"
"testing"

"github.com/cucumber/godog"
"github.com/cucumber/godog/colors"
"github.com/spf13/pflag"
)

var opts = godog.Options{Output: colors.Colored(os.Stdout)}

func init() {
godog.BindCommandLineFlags("godog.", &opts)
godog.BindFlags("godog.", flag.CommandLine, &opts)
}

func TestMain(m *testing.M) {
pflag.Parse()
opts.Paths = pflag.Args()
func TestFeatures(t *testing.T) {
o := opts
o.TestingT = t

status := godog.TestSuite{
Name: "godogs",
Options: &o,
TestSuiteInitializer: InitializeTestSuite,
ScenarioInitializer: InitializeScenario,
Options: &opts,
}.Run()

os.Exit(status)
if status == 2 {
t.SkipNow()
}

if status != 0 {
t.Fatalf("zero status code expected, %d received", status)
}
}

func thereAreGodogs(available int) error {
Expand All @@ -51,6 +68,10 @@ func thereShouldBeRemaining(remaining int) error {
return nil
}

func thereShouldBeNoneRemaining() error {
return thereShouldBeRemaining(0)
}

func InitializeTestSuite(ctx *godog.TestSuiteContext) {
ctx.BeforeSuite(func() { Godogs = 0 })
}
Expand All @@ -64,4 +85,5 @@ func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step(`^there are (\d+) godogs$`, thereAreGodogs)
ctx.Step(`^I eat (\d+)$`, iEat)
ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
ctx.Step(`^there should be none remaining$`, thereShouldBeNoneRemaining)
}
47 changes: 32 additions & 15 deletions flags_deprecated.go → flags.go
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"io"
"sort"
"strconv"
"strings"

Expand All @@ -18,7 +19,8 @@ var descFeaturesArgument = "Optional feature(s) to run. Can be:\n" +
s(4) + "- dir " + colors.Yellow("(features/)") + "\n" +
s(4) + "- feature " + colors.Yellow("(*.feature)") + "\n" +
s(4) + "- scenario at specific line " + colors.Yellow("(*.feature:10)") + "\n" +
"If no feature paths are listed, suite tries " + colors.Yellow("features") + " path by default.\n"
"If no feature paths are listed, suite tries " + colors.Yellow("features") + " path by default.\n" +
"Multiple comma-separated values can be provided.\n"

var descConcurrencyOption = "Run the test suite with concurrency level:\n" +
s(4) + "- " + colors.Yellow(`= 1`) + ": supports all types of formats.\n" +
Expand Down Expand Up @@ -48,15 +50,30 @@ func FlagSet(opt *Options) *flag.FlagSet {

// BindFlags binds godog flags to given flag set prefixed
// by given prefix, without overriding usage
//
// Deprecated: Use BindCommandLineFlags(prefix, &opts)
// instead of BindFlags(prefix, flag.CommandLine, &opts)
func BindFlags(prefix string, set *flag.FlagSet, opt *Options) {
set.Usage = usage(set, set.Output())

descFormatOption := "How to format tests output. Built-in formats:\n"
// @TODO: sort by name

type fm struct {
name string
desc string
}
var fms []fm
for name, desc := range AvailableFormatters() {
descFormatOption += s(4) + "- " + colors.Yellow(name) + ": " + desc + "\n"
fms = append(fms, fm{
name: name,
desc: desc,
})
}
sort.Slice(fms, func(i, j int) bool {
return fms[i].name < fms[j].name
})

for _, fm := range fms {
descFormatOption += s(4) + "- " + colors.Yellow(fm.name) + ": " + fm.desc + "\n"
}

descFormatOption = strings.TrimSpace(descFormatOption)

// override flag defaults if any corresponding properties were supplied on the incoming `opt`
Expand Down Expand Up @@ -107,6 +124,14 @@ func BindFlags(prefix string, set *flag.FlagSet, opt *Options) {
set.BoolVar(&opt.Strict, prefix+"strict", defStrict, "Fail suite when there are pending or undefined steps.")
set.BoolVar(&opt.NoColors, prefix+"no-colors", defNoColors, "Disable ansi colors.")
set.Var(&randomSeed{&opt.Randomize}, prefix+"random", descRandomOption)
set.BoolVar(&opt.ShowHelp, "godog.help", false, "Show usage help.")
set.Func(prefix+"paths", descFeaturesArgument, func(paths string) error {
if paths != "" {
opt.Paths = strings.Split(paths, ",")
}

return nil
})
}

type flagged struct {
Expand Down Expand Up @@ -183,15 +208,7 @@ func usage(set *flag.FlagSet, w io.Writer) func() {

// --- GENERAL ---
fmt.Fprintln(w, colors.Yellow("Usage:"))
fmt.Fprintf(w, s(2)+"godog [options] [<features>]\n\n")
// description
fmt.Fprintln(w, "Builds a test package and runs given feature files.")
fmt.Fprintf(w, "Command should be run from the directory of tested package and contain buildable go source.\n\n")

// --- ARGUMENTS ---
fmt.Fprintln(w, colors.Yellow("Arguments:"))
// --> features
fmt.Fprintln(w, opt("features", descFeaturesArgument))
fmt.Fprintf(w, s(2)+"go test [options]\n\n")

// --- OPTIONS ---
fmt.Fprintln(w, colors.Yellow("Options:"))
Expand Down
4 changes: 3 additions & 1 deletion flags_deprecated_test.go → flags_test.go
Expand Up @@ -121,7 +121,9 @@ func TestBindFlagsShouldRespectOptDefaults(t *testing.T) {
Randomize: int64(7),
}

BindFlags("optDefaults.", flag.CommandLine, &opts)
flagSet := flag.FlagSet{}

BindFlags("optDefaults.", &flagSet, &opts)

if opts.Format != "progress" {
t.Fatalf("expected Format: progress, but it was: %s", opts.Format)
Expand Down
3 changes: 3 additions & 0 deletions internal/flags/options.go
Expand Up @@ -69,6 +69,9 @@ type Options struct {
// where the contents of each feature is stored as a byte slice
// in a map entry
FeatureContents []Feature

// ShowHelp enables suite to show CLI flags usage help and exit.
ShowHelp bool
}

type Feature struct {
Expand Down
85 changes: 50 additions & 35 deletions run.go
Expand Up @@ -2,6 +2,7 @@ package godog

import (
"context"
"flag"
"fmt"
"go/build"
"io"
Expand Down Expand Up @@ -102,37 +103,44 @@ func (r *runner) concurrent(rate int) (failed bool) {
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.Content)
}

runPickle := func(fail *bool, pickle *messages.Pickle) {
defer func() {
<-queue // free a space in queue
}()

if r.stopOnFailure && *fail {
return
}

// Copy base suite.
suite := *testSuiteContext.suite

if r.scenarioInitializer != nil {
sc := ScenarioContext{suite: &suite}
r.scenarioInitializer(&sc)
}

err := suite.runPickle(pickle)
if suite.shouldFail(err) {
copyLock.Lock()
*fail = true
copyLock.Unlock()
}
}

if rate == 1 {
// Running within the same goroutine for concurrency 1
// to preserve original stacks and simplify debugging.
runPickle(&failed, &pickle)
} else {
go runPickle(&failed, &pickle)
// scenario
if r.testingT != nil {
// Running scenario as a subtest.
r.testingT.Run(pickle.Name, func(t *testing.T) {

runPickle := func(fail *bool, pickle *messages.Pickle) {
defer func() {
<-queue // free a space in queue
}()

if r.stopOnFailure && *fail {
return
}

// Copy base suite.
suite := *testSuiteContext.suite

if r.scenarioInitializer != nil {
sc := ScenarioContext{suite: &suite, TestingT: t}
r.scenarioInitializer(&sc)
}

err := suite.runPickle(pickle)
if suite.shouldFail(err) {
copyLock.Lock()
*fail = true
copyLock.Unlock()
}
}

if rate == 1 {
// Running within the same goroutine for concurrency 1
// to preserve original stacks and simplify debugging.
runPickle(&failed, &pickle)
} else {
go runPickle(&failed, &pickle)
}
})
}
}
}
Expand Down Expand Up @@ -309,10 +317,11 @@ type TestSuite struct {
// all configuration options from flags.
//
// The exit codes may vary from:
// 0 - success
// 1 - failed
// 2 - command line usage error
// 128 - or higher, os signal related error exit codes
//
// 0 - success
// 1 - failed
// 2 - command line usage error
// 128 - or higher, os signal related error exit codes
//
// If there are flag related errors they will be directed to os.Stderr
func (ts TestSuite) Run() int {
Expand All @@ -323,6 +332,12 @@ func (ts TestSuite) Run() int {
return exitOptionError
}
}
if ts.Options.ShowHelp {
flag.CommandLine.Usage()

return 0
}

r := runner{testSuiteInitializer: ts.TestSuiteInitializer, scenarioInitializer: ts.ScenarioInitializer}
return runWithOptions(ts.Name, r, *ts.Options)
}
Expand Down
13 changes: 1 addition & 12 deletions suite.go
Expand Up @@ -442,18 +442,7 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {

s.fmt.Pickle(pickle)

// scenario
if s.testingT != nil {
// Running scenario as a subtest.
s.testingT.Run(pickle.Name, func(t *testing.T) {
ctx, err = s.runSteps(ctx, pickle, pickle.Steps)
if err != nil {
t.Error(err)
}
})
} else {
ctx, err = s.runSteps(ctx, pickle, pickle.Steps)
}
_, err = s.runSteps(ctx, pickle, pickle.Steps)

// After scenario handlers are called in context of last evaluated step
// so that error from handler can be added to step.
Expand Down
16 changes: 9 additions & 7 deletions test_context.go
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"regexp"
"testing"

"github.com/cucumber/godog/formatters"
"github.com/cucumber/godog/internal/builder"
Expand All @@ -26,12 +27,12 @@ type Step = messages.PickleStep
// instead of returning an error in step func
// it is possible to return combined steps:
//
// func multistep(name string) godog.Steps {
// return godog.Steps{
// fmt.Sprintf(`an user named "%s"`, name),
// fmt.Sprintf(`user "%s" is authenticated`, name),
// }
// }
// func multistep(name string) godog.Steps {
// return godog.Steps{
// fmt.Sprintf(`an user named "%s"`, name),
// fmt.Sprintf(`user "%s" is authenticated`, name),
// }
// }
//
// These steps will be matched and executed in
// sequential order. The first one which fails
Expand Down Expand Up @@ -104,7 +105,8 @@ func (ctx *TestSuiteContext) ScenarioContext() *ScenarioContext {
// executions are catching panic error since it may
// be a context specific error.
type ScenarioContext struct {
suite *suite
suite *suite
TestingT *testing.T
}

// StepContext allows registering step hooks.
Expand Down

0 comments on commit 9da9f2d

Please sign in to comment.