Skip to content

Commit bf885ad

Browse files
authored
Merge pull request #71 from SimonBaeumer/add-retry-interval
Add retry interval
2 parents 6e5d39b + 854766b commit bf885ad

File tree

8 files changed

+95
-28
lines changed

8 files changed

+95
-28
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# v1.2.0
2+
3+
- Add `interval` option for `retries` which allows to execute a retry after a given period of time. I.e. `interval: 50ms`
4+
15
# v1.1.0
26

37
- Add `not-contains` assertion on `stdout` and `stderr`

commander_unix.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,7 @@ tests:
5252
stdout:
5353
contains:
5454
- ✗ echo hello, retries 3
55-
- ✓ ./integration/unix/_fixtures/retries.sh, retries 2
55+
- ✓ it should retry failed commands, retries 2
56+
- ✗ it should retry failed commands with an interval, retries 2
57+
- "Duration: 0.1" # Assertion that the interval is working
5658
exit-code: 1

docs/manual.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,6 @@ tests:
6868
ANOTHER: yeah # Add another env variable
6969
timeout: 1000 # Overwrite timeout
7070
retries: 5
71+
interval: 30ms
7172
exit-code: 0
7273
```

integration/unix/retries.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ tests:
44
config:
55
retries: 3
66

7-
./integration/unix/_fixtures/retries.sh:
7+
it should retry failed commands:
8+
command: ./integration/unix/_fixtures/retries.sh
89
exit-code: 0
910
config:
11+
retries: 2
12+
13+
it should retry failed commands with an interval:
14+
command: echo hello
15+
stdout: fail
16+
exit-code: 0
17+
config:
18+
interval: 50ms
1019
retries: 2

pkg/runtime/runtime.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package runtime
22

33
import (
4+
"fmt"
45
"github.com/SimonBaeumer/commander/pkg/cmd"
56
"log"
67
"runtime"
78
"strings"
89
"sync"
10+
"time"
911
)
1012

1113
// Constants for defining the various tested properties
@@ -36,10 +38,11 @@ type TestCase struct {
3638

3739
//TestConfig represents the configuration for a test
3840
type TestConfig struct {
39-
Env map[string]string
40-
Dir string
41-
Timeout string
42-
Retries int
41+
Env map[string]string
42+
Dir string
43+
Timeout string
44+
Retries int
45+
Interval string
4346
}
4447

4548
// ResultStatus represents the status code of a test result
@@ -74,11 +77,12 @@ type ExpectedOut struct {
7477

7578
// CommandUnderTest represents the command under test
7679
type CommandUnderTest struct {
77-
Cmd string
78-
Env map[string]string
79-
Dir string
80-
Timeout string
81-
Retries int
80+
Cmd string
81+
Env map[string]string
82+
Dir string
83+
Timeout string
84+
Retries int
85+
Interval string
8286
}
8387

8488
// TestResult represents the TestCase and the ValidationResult
@@ -120,6 +124,8 @@ func Start(tests []TestCase, maxConcurrent int) <-chan TestResult {
120124
if result.ValidationResult.Success {
121125
break
122126
}
127+
128+
executeRetryInterval(t)
123129
}
124130

125131
out <- result
@@ -135,6 +141,16 @@ func Start(tests []TestCase, maxConcurrent int) <-chan TestResult {
135141
return out
136142
}
137143

144+
func executeRetryInterval(t TestCase) {
145+
if t.Command.GetRetries() > 1 && t.Command.Interval != "" {
146+
interval, err := time.ParseDuration(t.Command.Interval)
147+
if err != nil {
148+
panic(fmt.Sprintf("'%s' interval error: %s", t.Command.Cmd, err))
149+
}
150+
time.Sleep(interval)
151+
}
152+
}
153+
138154
// runTest executes the current test case
139155
func runTest(test TestCase) TestResult {
140156
// cut = command under test

pkg/runtime/runtime_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/stretchr/testify/assert"
66
"runtime"
77
"testing"
8+
"time"
89
)
910

1011
const SingleConcurrent = 1
@@ -37,7 +38,29 @@ func TestRuntime_WithRetries(t *testing.T) {
3738
assert.False(t, r.ValidationResult.Success)
3839
assert.Equal(t, 3, r.Tries)
3940
}
41+
42+
assert.Equal(t, 1, counter)
43+
}
44+
45+
func TestRuntime_WithRetriesAndInterval(t *testing.T) {
46+
s := getExampleTestSuite()
47+
s[0].Command.Retries = 3
48+
s[0].Command.Cmd = "echo fail"
49+
s[0].Command.Interval = "50ms"
50+
51+
start := time.Now()
52+
got := Start(s, 1)
53+
54+
var counter = 0
55+
for r := range got {
56+
counter++
57+
assert.False(t, r.ValidationResult.Success)
58+
assert.Equal(t, 3, r.Tries)
59+
}
60+
duration := time.Since(start)
61+
4062
assert.Equal(t, 1, counter)
63+
assert.True(t, duration.Seconds() > 0.15, "Retry interval did not work")
4164
}
4265

4366
func TestRuntime_WithEnvVariables(t *testing.T) {

pkg/suite/yaml_suite.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ type YAMLConfig struct {
1515

1616
// YAMLTestConfig is a struct to represent the test config
1717
type YAMLTestConfig struct {
18-
Env map[string]string `yaml:"env,omitempty"`
19-
Dir string `yaml:"dir,omitempty"`
20-
Timeout string `yaml:"timeout,omitempty"`
21-
Retries int `yaml:"retries,omitempty"`
18+
Env map[string]string `yaml:"env,omitempty"`
19+
Dir string `yaml:"dir,omitempty"`
20+
Timeout string `yaml:"timeout,omitempty"`
21+
Retries int `yaml:"retries,omitempty"`
22+
Interval string `yaml:"interval,omitempty"`
2223
}
2324

2425
// YAMLTest represents a test in the yaml test suite
@@ -69,10 +70,11 @@ func ParseYAML(content []byte) Suite {
6970
return YAMLSuite{
7071
TestCases: convertYAMLConfToTestCases(yamlConfig),
7172
Config: runtime.TestConfig{
72-
Env: yamlConfig.Config.Env,
73-
Dir: yamlConfig.Config.Dir,
74-
Timeout: yamlConfig.Config.Timeout,
75-
Retries: yamlConfig.Config.Retries,
73+
Env: yamlConfig.Config.Env,
74+
Dir: yamlConfig.Config.Dir,
75+
Timeout: yamlConfig.Config.Timeout,
76+
Retries: yamlConfig.Config.Retries,
77+
Interval: yamlConfig.Config.Interval,
7678
},
7779
}
7880
}
@@ -84,11 +86,12 @@ func convertYAMLConfToTestCases(conf YAMLConfig) []runtime.TestCase {
8486
tests = append(tests, runtime.TestCase{
8587
Title: t.Title,
8688
Command: runtime.CommandUnderTest{
87-
Cmd: t.Command,
88-
Env: t.Config.Env,
89-
Dir: t.Config.Dir,
90-
Timeout: t.Config.Timeout,
91-
Retries: t.Config.Retries,
89+
Cmd: t.Command,
90+
Env: t.Config.Env,
91+
Dir: t.Config.Dir,
92+
Timeout: t.Config.Timeout,
93+
Retries: t.Config.Retries,
94+
Interval: t.Config.Interval,
9295
},
9396
Expected: runtime.Expected{
9497
ExitCode: t.ExitCode,
@@ -140,10 +143,11 @@ func (y *YAMLConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
140143

141144
//Parse global configuration
142145
y.Config = YAMLTestConfig{
143-
Env: params.Config.Env,
144-
Dir: params.Config.Dir,
145-
Timeout: params.Config.Timeout,
146-
Retries: params.Config.Retries,
146+
Env: params.Config.Env,
147+
Dir: params.Config.Dir,
148+
Timeout: params.Config.Timeout,
149+
Retries: params.Config.Retries,
150+
Interval: params.Config.Interval,
147151
}
148152

149153
return nil
@@ -239,6 +243,10 @@ func (y *YAMLConfig) mergeConfigs(local YAMLTestConfig, global YAMLTestConfig) Y
239243
conf.Retries = local.Retries
240244
}
241245

246+
if local.Interval != "" {
247+
conf.Interval = local.Interval
248+
}
249+
242250
return conf
243251
}
244252

pkg/suite/yaml_suite_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ config:
184184
dir: /home/commander/
185185
timeout: 10ms
186186
retries: 2
187+
interval: 500ms
187188
188189
tests:
189190
echo hello:
@@ -194,18 +195,21 @@ tests:
194195
dir: /home/test
195196
timeout: 1s
196197
retries: 10
198+
interval: 5s
197199
`)
198200

199201
got := ParseYAML(yaml)
200202
assert.Equal(t, map[string]string{"KEY": "global", "ANOTHER_KEY": "another_global"}, got.GetGlobalConfig().Env)
201203
assert.Equal(t, "/home/commander/", got.GetGlobalConfig().Dir)
202204
assert.Equal(t, "10ms", got.GetGlobalConfig().Timeout)
203205
assert.Equal(t, 2, got.GetGlobalConfig().Retries)
206+
assert.Equal(t, "500ms", got.GetGlobalConfig().Interval)
204207

205208
assert.Equal(t, map[string]string{"KEY": "local", "ANOTHER_KEY": "another_global"}, got.GetTests()[0].Command.Env)
206209
assert.Equal(t, "/home/test", got.GetTests()[0].Command.Dir)
207210
assert.Equal(t, "1s", got.GetTests()[0].Command.Timeout)
208211
assert.Equal(t, 10, got.GetTests()[0].Command.Retries)
212+
assert.Equal(t, "5s", got.GetTests()[0].Command.Interval)
209213
}
210214

211215
func TestYAMLSuite_ShouldThrowAnErrorIfFieldIsNotRegistered(t *testing.T) {

0 commit comments

Comments
 (0)