Skip to content

Commit 92fbee7

Browse files
committed
tests suite execution function and related errors
1 parent 8e9c01d commit 92fbee7

File tree

4 files changed

+156
-10
lines changed

4 files changed

+156
-10
lines changed

examples/api/version.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ Feature: get version
2020
And the response should match json:
2121
"""
2222
{
23-
"version": "v0.7.1"
23+
"version": "v0.7.2"
2424
}
2525
"""

godog.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ Godog was inspired by Behat and Cucumber the above description is taken from it'
3939
package godog
4040

4141
// Version of package - based on Semantic Versioning 2.0.0 http://semver.org/
42-
const Version = "v0.7.1"
42+
const Version = "v0.7.2"

run.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import (
1010
"github.com/DATA-DOG/godog/colors"
1111
)
1212

13+
const (
14+
exitSuccess int = iota
15+
exitFailure
16+
exitOptionError
17+
)
18+
1319
type initializer func(*Suite)
1420

1521
type runner struct {
@@ -78,6 +84,15 @@ func (r *runner) run() bool {
7884
// This method is useful in case if you run
7985
// godog in for example TestMain function together
8086
// with go tests
87+
//
88+
// The exit codes may vary from:
89+
// 0 - success
90+
// 1 - failed
91+
// 2 - command line usage error
92+
// 128 - or higher, os signal related error exit codes
93+
//
94+
// If there are flag related errors they will
95+
// be directed to os.Stderr
8196
func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Options) int {
8297
var output io.Writer = os.Stdout
8398
if nil != opt.Output {
@@ -94,7 +109,7 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt
94109
s := &Suite{}
95110
contextInitializer(s)
96111
s.printStepDefinitions(output)
97-
return 2 // showing help or printing definitions, results exit code - 2
112+
return exitOptionError
98113
}
99114

100115
if len(opt.Paths) == 0 {
@@ -106,7 +121,7 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt
106121

107122
if opt.Concurrency > 1 && !supportsConcurrency(opt.Format) {
108123
fmt.Fprintln(os.Stderr, fmt.Errorf("format \"%s\" does not support concurrent execution", opt.Format))
109-
return 1
124+
return exitOptionError
110125
}
111126
formatter := findFmt(opt.Format)
112127
if nil == formatter {
@@ -119,13 +134,13 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt
119134
opt.Format,
120135
strings.Join(names, ", "),
121136
))
122-
return 1
137+
return exitOptionError
123138
}
124139

125140
features, err := parseFeatures(opt.Tags, opt.Paths)
126141
if err != nil {
127142
fmt.Fprintln(os.Stderr, err)
128-
return 1
143+
return exitOptionError
129144
}
130145

131146
r := runner{
@@ -147,9 +162,9 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt
147162
failed = r.run()
148163
}
149164
if failed && opt.Format != "events" {
150-
return 1
165+
return exitFailure
151166
}
152-
return 0
167+
return exitSuccess
153168
}
154169

155170
// Run creates and runs the feature suite.
@@ -164,13 +179,22 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt
164179
//
165180
// contextInitializer must be able to register
166181
// the step definitions and event handlers.
182+
//
183+
// The exit codes may vary from:
184+
// 0 - success
185+
// 1 - failed
186+
// 2 - command line usage error
187+
// 128 - or higher, os signal related error exit codes
188+
//
189+
// If there are flag related errors they will
190+
// be directed to os.Stderr
167191
func Run(suite string, contextInitializer func(suite *Suite)) int {
168192
var opt Options
169193
opt.Output = colors.Colored(os.Stdout)
170194
flagSet := FlagSet(&opt)
171195
if err := flagSet.Parse(os.Args[1:]); err != nil {
172196
fmt.Fprintln(os.Stderr, err)
173-
return 1
197+
return exitOptionError
174198
}
175199

176200
opt.Paths = flagSet.Args()
@@ -188,5 +212,5 @@ func supportsConcurrency(format string) bool {
188212
return true // supports concurrency
189213
}
190214

191-
return true // all custom formatters are treated as supporting concurrency
215+
return false // does not support concurrency
192216
}

run_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package godog
33
import (
44
"bytes"
55
"fmt"
6+
"io"
67
"io/ioutil"
8+
"os"
79
"strings"
810
"testing"
911

@@ -123,3 +125,123 @@ func TestShouldFailOnError(t *testing.T) {
123125
t.Fatal("the suite should have failed")
124126
}
125127
}
128+
129+
func TestFailsWithConcurrencyOptionError(t *testing.T) {
130+
stderr, closer := bufErrorPipe(t)
131+
defer closer()
132+
defer stderr.Close()
133+
134+
opt := Options{
135+
Format: "pretty",
136+
Paths: []string{"features/load:6"},
137+
Concurrency: 2,
138+
Output: ioutil.Discard,
139+
}
140+
141+
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
142+
if status != exitOptionError {
143+
t.Fatalf("expected exit status to be 2, but was: %d", status)
144+
}
145+
closer()
146+
147+
b, err := ioutil.ReadAll(stderr)
148+
if err != nil {
149+
t.Fatal(err)
150+
}
151+
152+
out := strings.TrimSpace(string(b))
153+
if out != `format "pretty" does not support concurrent execution` {
154+
t.Fatalf("unexpected error output: \"%s\"", out)
155+
}
156+
}
157+
158+
func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
159+
stderr, closer := bufErrorPipe(t)
160+
defer closer()
161+
defer stderr.Close()
162+
163+
opt := Options{
164+
Format: "unknown",
165+
Paths: []string{"features/load:6"},
166+
Output: ioutil.Discard,
167+
}
168+
169+
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
170+
if status != exitOptionError {
171+
t.Fatalf("expected exit status to be 2, but was: %d", status)
172+
}
173+
closer()
174+
175+
b, err := ioutil.ReadAll(stderr)
176+
if err != nil {
177+
t.Fatal(err)
178+
}
179+
180+
out := strings.TrimSpace(string(b))
181+
if strings.Index(out, `unregistered formatter name: "unknown", use one of`) == -1 {
182+
t.Fatalf("unexpected error output: \"%s\"", out)
183+
}
184+
}
185+
186+
func TestFailsWithOptionErrorWhenLookingForFeaturesInUnavailablePath(t *testing.T) {
187+
stderr, closer := bufErrorPipe(t)
188+
defer closer()
189+
defer stderr.Close()
190+
191+
opt := Options{
192+
Format: "progress",
193+
Paths: []string{"unavailable"},
194+
Output: ioutil.Discard,
195+
}
196+
197+
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
198+
if status != exitOptionError {
199+
t.Fatalf("expected exit status to be 2, but was: %d", status)
200+
}
201+
closer()
202+
203+
b, err := ioutil.ReadAll(stderr)
204+
if err != nil {
205+
t.Fatal(err)
206+
}
207+
208+
out := strings.TrimSpace(string(b))
209+
if out != `feature path "unavailable" is not available` {
210+
t.Fatalf("unexpected error output: \"%s\"", out)
211+
}
212+
}
213+
214+
func TestByDefaultRunsFeaturesPath(t *testing.T) {
215+
opt := Options{
216+
Format: "progress",
217+
Output: ioutil.Discard,
218+
Strict: true,
219+
}
220+
221+
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
222+
// should fail in strict mode due to undefined steps
223+
if status != exitFailure {
224+
t.Fatalf("expected exit status to be 1, but was: %d", status)
225+
}
226+
227+
opt.Strict = false
228+
status = RunWithOptions("succeeds", func(_ *Suite) {}, opt)
229+
// should succeed in non strict mode due to undefined steps
230+
if status != exitSuccess {
231+
t.Fatalf("expected exit status to be 0, but was: %d", status)
232+
}
233+
}
234+
235+
func bufErrorPipe(t *testing.T) (io.ReadCloser, func()) {
236+
stderr := os.Stderr
237+
r, w, err := os.Pipe()
238+
if err != nil {
239+
t.Fatal(err)
240+
}
241+
242+
os.Stderr = w
243+
return r, func() {
244+
w.Close()
245+
os.Stderr = stderr
246+
}
247+
}

0 commit comments

Comments
 (0)