Skip to content

Commit 4cff4c5

Browse files
committed
catch innermost spec context timeout
In order to prevent the panic() that occurs if a context timeout cancel() function fires in the Go testing tool, we execute each test spec in its own goroutine and catch the individual cancel() function firing in order to properly output a failed assertion instead of a panic. Issue #37 Signed-off-by: Jay Pipes <[email protected]>
1 parent f051721 commit 4cff4c5

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

scenario/run.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,19 @@ func (s *Scenario) Run(ctx context.Context, t *testing.T) error {
9393
defer cancel()
9494
}
9595

96-
res, rterr := s.runSpec(specCtx, rt, to, idx, spec)
96+
var res *api.Result
97+
ch := make(chan runSpecRes, 1)
98+
99+
go s.runSpec(specCtx, ch, rt, to, idx, spec)
100+
101+
select {
102+
case <-specCtx.Done():
103+
t.Fatalf("assertion failed: timeout exceeded (%s)", to.After)
104+
break
105+
case runres := <-ch:
106+
res = runres.r
107+
rterr = runres.err
108+
}
97109
if rterr != nil {
98110
break
99111
}
@@ -115,14 +127,20 @@ func (s *Scenario) Run(ctx context.Context, t *testing.T) error {
115127
return rterr
116128
}
117129

130+
type runSpecRes struct {
131+
r *api.Result
132+
err error
133+
}
134+
118135
// runSpec executes an individual test spec
119136
func (s *Scenario) runSpec(
120137
ctx context.Context,
138+
ch chan runSpecRes,
121139
retry *api.Retry,
122140
timeout *api.Timeout,
123141
idx int,
124142
spec api.Evaluable,
125-
) (*api.Result, error) {
143+
) {
126144
sb := spec.Base()
127145
specTraceMsg := strconv.Itoa(idx)
128146
if sb.Name != "" {
@@ -136,13 +154,15 @@ func (s *Scenario) runSpec(
136154
// Just evaluate the test spec once
137155
res, err := spec.Eval(ctx)
138156
if err != nil {
139-
return nil, err
157+
ch <- runSpecRes{nil, err}
158+
return
140159
}
141160
debug.Println(
142161
ctx, "run: single-shot (no retries) ok: %v",
143162
!res.Failed(),
144163
)
145-
return res, nil
164+
ch <- runSpecRes{res, nil}
165+
return
146166
}
147167

148168
// retry the action and test the assertions until they succeed,
@@ -187,7 +207,8 @@ func (s *Scenario) runSpec(
187207

188208
res, err = spec.Eval(ctx)
189209
if err != nil {
190-
return nil, err
210+
ch <- runSpecRes{nil, err}
211+
return
191212
}
192213
success = !res.Failed()
193214
debug.Println(
@@ -200,13 +221,13 @@ func (s *Scenario) runSpec(
200221
}
201222
for _, f := range res.Failures() {
202223
debug.Println(
203-
ctx, "run: attempt %d after %s failure: %s",
204-
attempts, after, f,
224+
ctx, "run: attempt %d failure: %s",
225+
attempts, f,
205226
)
206227
}
207228
attempts++
208229
}
209-
return res, nil
230+
ch <- runSpecRes{res, nil}
210231
}
211232

212233
// getTimeout returns the timeout configuration for the test spec. We check for

0 commit comments

Comments
 (0)