Skip to content

Commit 4124262

Browse files
authored
Merge pull request #34 from jaypipes/no-retry-fail
allow an Evaluable to override retry behaviour
2 parents a5e6bc6 + db376cc commit 4124262

File tree

9 files changed

+177
-31
lines changed

9 files changed

+177
-31
lines changed

context/context_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ func (s *fooSpec) Base() *gdttypes.Spec {
3939
return &s.Spec
4040
}
4141

42+
func (s *fooSpec) Retry() *gdttypes.Retry {
43+
return nil
44+
}
45+
46+
func (s *fooSpec) Timeout() *gdttypes.Timeout {
47+
return nil
48+
}
49+
4250
func (s *fooSpec) UnmarshalYAML(node *yaml.Node) error {
4351
return nil
4452
}

plugin/exec/spec.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ func (s *Spec) SetBase(b gdttypes.Spec) {
2626
func (s *Spec) Base() *gdttypes.Spec {
2727
return &s.Spec
2828
}
29+
30+
func (s *Spec) Retry() *gdttypes.Retry {
31+
return nil
32+
}
33+
34+
func (s *Spec) Timeout() *gdttypes.Timeout {
35+
return nil
36+
}

plugin/registry_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ func (s *fooSpec) Base() *gdttypes.Spec {
3636
return &s.Spec
3737
}
3838

39+
func (s *fooSpec) Retry() *gdttypes.Retry {
40+
return nil
41+
}
42+
43+
func (s *fooSpec) Timeout() *gdttypes.Timeout {
44+
return nil
45+
}
46+
3947
func (s *fooSpec) Eval(context.Context) (*result.Result, error) {
4048
return nil, nil
4149
}

scenario/run.go

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,8 @@ func (s *Scenario) Run(ctx context.Context, t *testing.T) error {
7878
time.Sleep(wait.BeforeDuration())
7979
}
8080
plugin := s.evalPlugins[idx]
81-
pinfo := plugin.Info()
82-
pretry := pinfo.Retry
83-
ptimeout := pinfo.Timeout
8481

85-
rt := getRetry(ctx, sb.Retry, scDefaults, pretry)
82+
rt := getRetry(ctx, scDefaults, plugin, spec)
8683

8784
// Create a brand new context that inherits the top-level context's
8885
// cancel func. We want to set deadlines for each test spec and if
@@ -91,7 +88,7 @@ func (s *Scenario) Run(ctx context.Context, t *testing.T) error {
9188
specCtx, specCancel := context.WithCancel(ctx)
9289
defer specCancel()
9390

94-
to := getTimeout(ctx, sb.Timeout, ptimeout, scDefaults)
91+
to := getTimeout(ctx, scDefaults, plugin, spec)
9592
if to != nil {
9693
var cancel context.CancelFunc
9794
specCtx, cancel = context.WithTimeout(specCtx, to.Duration())
@@ -113,7 +110,7 @@ func (s *Scenario) Run(ctx context.Context, t *testing.T) error {
113110
ctx = gdtcontext.StorePriorRun(ctx, res.Data())
114111
}
115112
for _, fail := range res.Failures() {
116-
t.Error(fail)
113+
t.Fatal(fail)
117114
}
118115
}
119116
})
@@ -137,7 +134,7 @@ func (s *Scenario) runSpec(
137134
defer func() {
138135
ctx = gdtcontext.PopTrace(ctx)
139136
}()
140-
if retry == nil {
137+
if retry == nil || retry == gdttypes.NoRetry {
141138
// Just evaluate the test spec once
142139
res, err := spec.Eval(ctx)
143140
if err != nil {
@@ -214,31 +211,49 @@ func (s *Scenario) runSpec(
214211
return res, nil
215212
}
216213

217-
// getTimeout returns the timeout value for the test spec. If the spec has a
218-
// timeout override, we use that. Otherwise, we inspect the scenario's defaults
219-
// and, if present, use that timeout. If the scenario's defaults for not
220-
// indicate a timeout configuration, we ask the plugin if it has timeout
221-
// defaults and use that.
214+
// getTimeout returns the timeout configuration for the test spec. We check for
215+
// overrides in timeout configuration using the following precedence:
216+
//
217+
// * Spec (Evaluable) override
218+
// * Spec's Base override
219+
// * Scenario's default
220+
// * Plugin's default
222221
func getTimeout(
223222
ctx context.Context,
224-
specTimeout *gdttypes.Timeout,
225-
pluginTimeout *gdttypes.Timeout,
226223
scenDefaults *Defaults,
224+
plugin gdttypes.Plugin,
225+
eval gdttypes.Evaluable,
227226
) *gdttypes.Timeout {
228-
if specTimeout != nil {
227+
evalTimeout := eval.Timeout()
228+
if evalTimeout != nil {
229+
debug.Println(
230+
ctx, "using timeout of %s",
231+
evalTimeout.After,
232+
)
233+
return evalTimeout
234+
}
235+
236+
sb := eval.Base()
237+
baseTimeout := sb.Timeout
238+
if baseTimeout != nil {
229239
debug.Println(
230240
ctx, "using timeout of %s",
231-
specTimeout.After,
241+
baseTimeout.After,
232242
)
233-
return specTimeout
243+
return baseTimeout
234244
}
245+
235246
if scenDefaults != nil && scenDefaults.Timeout != nil {
236247
debug.Println(
237248
ctx, "using timeout of %s [scenario default]",
238249
scenDefaults.Timeout.After,
239250
)
240251
return scenDefaults.Timeout
241252
}
253+
254+
pluginInfo := plugin.Info()
255+
pluginTimeout := pluginInfo.Timeout
256+
242257
if pluginTimeout != nil {
243258
debug.Println(
244259
ctx, "using timeout of %s [plugin default]",
@@ -249,31 +264,59 @@ func getTimeout(
249264
return nil
250265
}
251266

252-
// getRetry returns the retry configuration for the test spec. If the spec has a
253-
// retry override, we use that. Otherwise, we inspect the scenario's defaults
254-
// and, if present, use that timeout. If the scenario's defaults do not
255-
// indicate a retry configuration, we ask the plugin if it has retry defaults
256-
// and use that.
267+
// getRetry returns the retry configuration for the test spec. We check for
268+
// overrides in retry configuration using the following precedence:
269+
//
270+
// * Spec (Evaluable) override
271+
// * Spec's Base override
272+
// * Scenario's default
273+
// * Plugin's default
257274
func getRetry(
258275
ctx context.Context,
259-
specRetry *gdttypes.Retry,
260276
scenDefaults *Defaults,
261-
pluginRetry *gdttypes.Retry,
277+
plugin gdttypes.Plugin,
278+
eval gdttypes.Evaluable,
262279
) *gdttypes.Retry {
263-
if specRetry != nil {
280+
evalRetry := eval.Retry()
281+
if evalRetry != nil {
282+
if evalRetry == gdttypes.NoRetry {
283+
return evalRetry
284+
}
264285
msg := "using retry"
265-
if specRetry.Attempts != nil {
266-
msg += fmt.Sprintf(" (attempts: %d)", *specRetry.Attempts)
286+
if evalRetry.Attempts != nil {
287+
msg += fmt.Sprintf(" (attempts: %d)", *evalRetry.Attempts)
267288
}
268-
if specRetry.Interval != "" {
269-
msg += fmt.Sprintf(" (interval: %s)", specRetry.Interval)
289+
if evalRetry.Interval != "" {
290+
msg += fmt.Sprintf(" (interval: %s)", evalRetry.Interval)
270291
}
271-
msg += fmt.Sprintf(" (exponential: %t)", specRetry.Exponential)
292+
msg += fmt.Sprintf(" (exponential: %t)", evalRetry.Exponential)
272293
debug.Println(ctx, msg)
273-
return specRetry
294+
return evalRetry
274295
}
296+
297+
sb := eval.Base()
298+
baseRetry := sb.Retry
299+
if baseRetry != nil {
300+
if baseRetry == gdttypes.NoRetry {
301+
return baseRetry
302+
}
303+
msg := "using retry"
304+
if baseRetry.Attempts != nil {
305+
msg += fmt.Sprintf(" (attempts: %d)", *baseRetry.Attempts)
306+
}
307+
if baseRetry.Interval != "" {
308+
msg += fmt.Sprintf(" (interval: %s)", baseRetry.Interval)
309+
}
310+
msg += fmt.Sprintf(" (exponential: %t)", baseRetry.Exponential)
311+
debug.Println(ctx, msg)
312+
return baseRetry
313+
}
314+
275315
if scenDefaults != nil && scenDefaults.Retry != nil {
276316
scenRetry := scenDefaults.Retry
317+
if scenRetry == gdttypes.NoRetry {
318+
return scenRetry
319+
}
277320
msg := "using retry"
278321
if scenRetry.Attempts != nil {
279322
msg += fmt.Sprintf(" (attempts: %d)", *scenRetry.Attempts)
@@ -285,7 +328,14 @@ func getRetry(
285328
debug.Println(ctx, msg)
286329
return scenRetry
287330
}
331+
332+
pluginInfo := plugin.Info()
333+
pluginRetry := pluginInfo.Retry
334+
288335
if pluginRetry != nil {
336+
if pluginRetry == gdttypes.NoRetry {
337+
return pluginRetry
338+
}
289339
msg := "using retry"
290340
if pluginRetry.Attempts != nil {
291341
msg += fmt.Sprintf(" (attempts: %d)", *pluginRetry.Attempts)

scenario/run_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,30 @@ func TestNoRetry(t *testing.T) {
140140
require.Contains(debugout, "[gdt] [no-retry/0:bar] run: single-shot (no retries) ok: true")
141141
}
142142

143+
func TestNoRetryEvaluableOverride(t *testing.T) {
144+
require := require.New(t)
145+
146+
fp := filepath.Join("testdata", "no-retry-evaluable-override.yaml")
147+
f, err := os.Open(fp)
148+
require.Nil(err)
149+
150+
var b bytes.Buffer
151+
w := bufio.NewWriter(&b)
152+
ctx := gdtcontext.New(gdtcontext.WithDebug(w))
153+
154+
s, err := scenario.FromReader(f, scenario.WithPath(fp))
155+
require.Nil(err)
156+
require.NotNil(s)
157+
158+
err = s.Run(ctx, t)
159+
require.Nil(err)
160+
require.False(t.Failed())
161+
w.Flush()
162+
require.NotEqual(b.Len(), 0)
163+
debugout := b.String()
164+
require.Contains(debugout, "[gdt] [no-retry-evaluable-override/0:bar] run: single-shot (no retries) ok: true")
165+
}
166+
143167
func TestFailRetryTestOverride(t *testing.T) {
144168
if !*failFlag {
145169
t.Skip("skipping without -fail flag")

scenario/stub_plugins_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ func (s *failSpec) Base() *gdttypes.Spec {
8282
return &s.Spec
8383
}
8484

85+
func (s *failSpec) Retry() *gdttypes.Retry {
86+
return nil
87+
}
88+
89+
func (s *failSpec) Timeout() *gdttypes.Timeout {
90+
return nil
91+
}
92+
8593
func (s *failSpec) Eval(context.Context) (*result.Result, error) {
8694
return nil, fmt.Errorf("%w: Indy, bad dates!", gdterrors.RuntimeError)
8795
}
@@ -185,6 +193,14 @@ func (s *fooSpec) Base() *gdttypes.Spec {
185193
return &s.Spec
186194
}
187195

196+
func (s *fooSpec) Retry() *gdttypes.Retry {
197+
return nil
198+
}
199+
200+
func (s *fooSpec) Timeout() *gdttypes.Timeout {
201+
return nil
202+
}
203+
188204
func (s *fooSpec) UnmarshalYAML(node *yaml.Node) error {
189205
if node.Kind != yaml.MappingNode {
190206
return errors.ExpectedMapAt(node)
@@ -266,6 +282,14 @@ func (s *barSpec) Base() *gdttypes.Spec {
266282
return &s.Spec
267283
}
268284

285+
func (s *barSpec) Retry() *gdttypes.Retry {
286+
return gdttypes.NoRetry
287+
}
288+
289+
func (s *barSpec) Timeout() *gdttypes.Timeout {
290+
return nil
291+
}
292+
269293
func (s *barSpec) Eval(context.Context) (*result.Result, error) {
270294
return result.New(), nil
271295
}
@@ -341,6 +365,14 @@ func (s *priorRunSpec) Base() *gdttypes.Spec {
341365
return &s.Spec
342366
}
343367

368+
func (s *priorRunSpec) Retry() *gdttypes.Retry {
369+
return nil
370+
}
371+
372+
func (s *priorRunSpec) Timeout() *gdttypes.Timeout {
373+
return nil
374+
}
375+
344376
func (s *priorRunSpec) UnmarshalYAML(node *yaml.Node) error {
345377
if node.Kind != yaml.MappingNode {
346378
return errors.ExpectedMapAt(node)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: no-retry-evaluable-override
2+
description: a scenario using a plugin with an evaluable override for no retries
3+
tests:
4+
# The bar plugin's Evaluable has a NoRetry override
5+
- bar: 42
6+
name: bar

types/evaluable.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ type Evaluable interface {
2323
SetBase(Spec)
2424
// Base returns the Evaluable's base Spec
2525
Base() *Spec
26+
// Retry returns the Evaluable's Retry override, if any
27+
Retry() *Retry
28+
// Timeout returns the Evaluable's Timeout override, if any
29+
Timeout() *Timeout
2630
}

types/retry.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ const (
1919
DefaultRetryConstantInterval = 3 * time.Second
2020
)
2121

22+
var (
23+
// NoRetry indicates that there should not be any retry attempts. It is
24+
// passed from a plugin to indicate a Spec should not be retried.
25+
NoRetry = &Retry{}
26+
)
27+
2228
// Retry contains information about the number of attempts and interval
2329
// duration with which a Plugin should re-run a Spec's action if the Spec's
2430
// assertions fail.

0 commit comments

Comments
 (0)