Skip to content

Commit ca7ef5b

Browse files
committed
feat: add --retry and --timeout flags
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
1 parent 017f140 commit ca7ef5b

File tree

6 files changed

+83
-44
lines changed

6 files changed

+83
-44
lines changed

.github/workflows/go.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ jobs:
7373
strategy:
7474
matrix:
7575
golang:
76-
#- 1.11.13
77-
#- 1.12.17
78-
#- 1.13.15
7976
#- 1.14.7
8077
- 1.15.1
8178
steps:
@@ -133,9 +130,6 @@ jobs:
133130
strategy:
134131
matrix:
135132
golang:
136-
- 1.11.13
137-
- 1.12.17
138-
- 1.13.15
139133
- 1.14.7
140134
- 1.15.1
141135
env:

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
GOPKG ?= moul.io/testman
22
DOCKER_IMAGE ?= moul/testman
3+
GOMOD_DIRS ?= .
34
GOBINS ?= .
45
NPM_PACKAGES ?= .
56

examples/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
integration:
22
# test with testman
3-
testman test -run ^TestStable ./...
4-
@# FIXME: test unstable tests
3+
testman test -timeout=10s -run ^TestStable ./... # should always work
4+
testman test -timeout=60s -run ^TestUnstable -retry=50 ./... # should work at least 1/50
55
@# FIXME: test broken tests
66

77
# test with default tools

go.mod

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.sum

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

main.go

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
"time"
1515

1616
"github.com/peterbourgon/ff/v3/ffcli"
17-
"moul.io/godev"
1817
"moul.io/motd"
18+
"moul.io/u"
1919
)
2020

2121
var opts Opts
@@ -34,8 +34,8 @@ func run(args []string) error {
3434
testFlags := flag.NewFlagSet("testman test", flag.ExitOnError)
3535
testFlags.BoolVar(&opts.Verbose, "v", false, "verbose")
3636
testFlags.StringVar(&opts.Run, "run", "^(Test|Example)", "regex to filter out tests and examples")
37-
//testFlags.IntVar(&opts.Retry, "retry", 0, "fail after N retries")
38-
//testFlags.DurationVar(&opts.Timeout, "timeout", opts.Timeout, "max duration allowed to run the whole suite")
37+
testFlags.IntVar(&opts.Retry, "retry", 0, "fail after N retries")
38+
testFlags.DurationVar(&opts.Timeout, "timeout", opts.Timeout, "max duration allowed to run the whole suite")
3939
listFlags := flag.NewFlagSet("testman list", flag.ExitOnError)
4040
listFlags.BoolVar(&opts.Verbose, "v", false, "verbose")
4141
listFlags.StringVar(&opts.Run, "run", "^(Test|Example)", "regex to filter out tests and examples")
@@ -53,14 +53,14 @@ func run(args []string) error {
5353
FlagSet: testFlags,
5454
ShortHelp: "advanced go test workflows",
5555
ShortUsage: "testman test [flags] [packages]",
56-
LongHelp: "EXAMPLES\n testman test -v ./...",
56+
LongHelp: testLongHelp,
5757
Exec: runTest,
5858
}, {
5959
Name: "list",
6060
FlagSet: listFlags,
6161
ShortHelp: "list available tests",
6262
ShortUsage: "testman list [packages]",
63-
LongHelp: "EXAMPLE\n testman list ./...",
63+
LongHelp: listLongHelp,
6464
Exec: runList,
6565
},
6666
},
@@ -69,11 +69,26 @@ func run(args []string) error {
6969
return root.ParseAndRun(context.Background(), args[1:])
7070
}
7171

72+
const (
73+
testLongHelp = `EXAMPLES
74+
testman test ./...
75+
testman test -v ./...
76+
testman test -run ^TestUnstable -timeout=300s -retry=50 ./...`
77+
listLongHelp = `EXAMPLES
78+
testman list ./...
79+
testman list -v ./...
80+
testman list -run ^TestStable ./...`
81+
)
82+
7283
func runList(ctx context.Context, args []string) error {
7384
if len(args) == 0 {
7485
return flag.ErrHelp
7586
}
76-
preRun()
87+
cleanup, err := preRun()
88+
if err != nil {
89+
return err
90+
}
91+
defer cleanup()
7792

7893
// list packages
7994
pkgs, err := listPackagesWithTests(args)
@@ -103,22 +118,28 @@ func runTest(ctx context.Context, args []string) error {
103118
if len(args) == 0 {
104119
return flag.ErrHelp
105120
}
106-
preRun()
107-
log.Printf("runTest opts=%s args=%s", godev.JSON(opts), godev.JSON(args))
108-
start := time.Now()
109-
110-
// list packages
111-
pkgs, err := listPackagesWithTests(args)
121+
cleanup, err := preRun()
112122
if err != nil {
113123
return err
114124
}
125+
defer cleanup()
115126

116-
// create temp dir
117-
tmpdir, err := ioutil.TempDir("", "testman")
127+
log.Printf("runTest opts=%s args=%s", u.JSON(opts), u.JSON(args))
128+
start := time.Now()
129+
130+
if opts.Timeout > 0 {
131+
go func() {
132+
<-time.After(opts.Timeout)
133+
fmt.Printf("FAIL: timed out after %s\n", time.Since(start))
134+
os.Exit(1)
135+
}()
136+
}
137+
138+
// list packages
139+
pkgs, err := listPackagesWithTests(args)
118140
if err != nil {
119141
return err
120142
}
121-
defer os.RemoveAll(tmpdir)
122143

123144
atLeastOneFailure := false
124145
// list tests
@@ -133,7 +154,7 @@ func runTest(ctx context.Context, args []string) error {
133154

134155
pkgStart := time.Now()
135156
// compile test binary
136-
bin, err := compileTestBin(pkg, tmpdir)
157+
bin, err := compileTestBin(pkg, opts.TmpDir)
137158
if err != nil {
138159
fmt.Printf("FAIL\t%s\t[compile error: %v]\n", pkg.ImportPath, err)
139160
return err
@@ -144,40 +165,61 @@ func runTest(ctx context.Context, args []string) error {
144165
// FIXME: check if matches run regex
145166
args := []string{
146167
"-test.count=1",
147-
"-test.timeout=300s",
168+
fmt.Sprintf("-test.timeout=%s", opts.Timeout),
148169
}
149170
if opts.Verbose {
150171
args = append(args, "-test.v")
151172
}
152173
args = append(args, "-test.run", fmt.Sprintf("^%s$", test))
153-
cmd := exec.Command(bin, args...)
154-
log.Println(cmd.String())
155-
out, err := cmd.CombinedOutput()
156-
if err != nil {
157-
fmt.Printf("FAIL\t%s.%s\t[compile error: %v]\n", pkg.ImportPath, test, err)
158-
if opts.Verbose {
159-
fmt.Println(string(out))
174+
for i := opts.Retry; i >= 0; i-- {
175+
cmd := exec.Command(bin, args...)
176+
log.Println(cmd.String())
177+
out, err := cmd.CombinedOutput()
178+
if err != nil {
179+
if i == 0 {
180+
fmt.Printf("FAIL\t%s.%s\t[test error: %v]\n", pkg.ImportPath, test, err)
181+
isPackageOK = false
182+
atLeastOneFailure = true
183+
} else if opts.Verbose {
184+
fmt.Printf("RETRY\t%s.%s\t[test error: %v]\n", pkg.ImportPath, test, err)
185+
}
186+
if opts.Verbose {
187+
fmt.Println(string(out))
188+
}
189+
} else {
190+
fmt.Printf("ok\t%s.%s\n", pkg.ImportPath, test)
191+
break
160192
}
161-
isPackageOK = false
162-
atLeastOneFailure = true
163193
}
164194
}
165195
if isPackageOK {
166196
fmt.Printf("ok\t%s\t%s\n", pkg.ImportPath, time.Since(pkgStart))
167197
}
168198
}
169199

170-
fmt.Printf("total: %s\n", time.Since(start))
200+
log.Printf("total: %s\n", time.Since(start))
171201
if atLeastOneFailure {
172202
os.Exit(1)
173203
}
174204
return nil
175205
}
176206

177-
func preRun() {
207+
func preRun() (func(), error) {
178208
if !opts.Verbose {
179209
log.SetOutput(ioutil.Discard)
180210
}
211+
212+
// create temp dir
213+
var err error
214+
opts.TmpDir, err = ioutil.TempDir("", "testman")
215+
if err != nil {
216+
return nil, err
217+
}
218+
219+
cleanup := func() {
220+
os.RemoveAll(opts.TmpDir)
221+
}
222+
return cleanup, nil
181223
}
182224

183225
func compileTestBin(pkg Package, tempdir string) (string, error) {
@@ -258,8 +300,9 @@ type Package struct {
258300
type Opts struct {
259301
Verbose bool
260302
Run string
261-
// Timeout time.Duration
262-
// Retry int
303+
Timeout time.Duration
304+
Retry int
305+
TmpDir string
263306
// c
264307
// debug
265308
// continueOnFailure vs failFast

0 commit comments

Comments
 (0)