feat(process): skip pending runner specs on cancellation
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
82e85a99fd
commit
16e5c57fd4
2 changed files with 89 additions and 5 deletions
44
runner.go
44
runner.go
|
|
@ -123,6 +123,13 @@ func (r *Runner) RunAll(ctx context.Context, specs []RunSpec) (*RunAllResult, er
|
|||
}
|
||||
|
||||
for len(remaining) > 0 {
|
||||
if err := ctx.Err(); err != nil {
|
||||
for name := range remaining {
|
||||
results[indexMap[name]] = cancelledRunResult("Runner.RunAll", remaining[name], err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Find specs ready to run (all dependencies satisfied)
|
||||
ready := make([]RunSpec, 0)
|
||||
for _, spec := range remaining {
|
||||
|
|
@ -276,17 +283,18 @@ func (r *Runner) RunSequential(ctx context.Context, specs []RunSpec) (*RunAllRes
|
|||
results := make([]RunResult, 0, len(specs))
|
||||
|
||||
for _, spec := range specs {
|
||||
if err := ctx.Err(); err != nil {
|
||||
results = append(results, cancelledRunResult("Runner.RunSequential", spec, err))
|
||||
continue
|
||||
}
|
||||
|
||||
result := r.runSpec(ctx, spec)
|
||||
results = append(results, result)
|
||||
|
||||
if !result.Passed() && !spec.AllowFailure {
|
||||
// Mark remaining as skipped
|
||||
for i := len(results); i < len(specs); i++ {
|
||||
results = append(results, RunResult{
|
||||
Name: specs[i].Name,
|
||||
Spec: specs[i],
|
||||
Skipped: true,
|
||||
})
|
||||
results = append(results, skippedRunResult("Runner.RunSequential", specs[i], nil))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
@ -330,6 +338,10 @@ func (r *Runner) RunParallel(ctx context.Context, specs []RunSpec) (*RunAllResul
|
|||
wg.Add(1)
|
||||
go func(i int, spec RunSpec) {
|
||||
defer wg.Done()
|
||||
if err := ctx.Err(); err != nil {
|
||||
results[i] = cancelledRunResult("Runner.RunParallel", spec, err)
|
||||
return
|
||||
}
|
||||
results[i] = r.runSpec(ctx, spec)
|
||||
}(i, spec)
|
||||
}
|
||||
|
|
@ -366,3 +378,25 @@ func validateSpecs(specs []RunSpec) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func skippedRunResult(op string, spec RunSpec, err error) RunResult {
|
||||
result := RunResult{
|
||||
Name: spec.Name,
|
||||
Spec: spec,
|
||||
Skipped: true,
|
||||
}
|
||||
if err != nil {
|
||||
result.ExitCode = 1
|
||||
result.Error = coreerr.E(op, "skipped", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func cancelledRunResult(op string, spec RunSpec, err error) RunResult {
|
||||
result := skippedRunResult(op, spec, err)
|
||||
if result.Error == nil {
|
||||
result.ExitCode = 1
|
||||
result.Error = coreerr.E(op, "context cancelled", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,56 @@ func TestRunner_RunAll_CircularDeps(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestRunner_ContextCancellation(t *testing.T) {
|
||||
t.Run("run sequential skips pending specs", func(t *testing.T) {
|
||||
runner := newTestRunner(t)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
result, err := runner.RunSequential(ctx, []RunSpec{
|
||||
{Name: "first", Command: "echo", Args: []string{"1"}},
|
||||
{Name: "second", Command: "echo", Args: []string{"2"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 0, result.Passed)
|
||||
assert.Equal(t, 0, result.Failed)
|
||||
assert.Equal(t, 2, result.Skipped)
|
||||
require.Len(t, result.Results, 2)
|
||||
for _, res := range result.Results {
|
||||
assert.True(t, res.Skipped)
|
||||
assert.Equal(t, 1, res.ExitCode)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Contains(t, res.Error.Error(), "context canceled")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("run all skips pending specs", func(t *testing.T) {
|
||||
runner := newTestRunner(t)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
result, err := runner.RunAll(ctx, []RunSpec{
|
||||
{Name: "first", Command: "echo", Args: []string{"1"}},
|
||||
{Name: "second", Command: "echo", Args: []string{"2"}, After: []string{"first"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 0, result.Passed)
|
||||
assert.Equal(t, 0, result.Failed)
|
||||
assert.Equal(t, 2, result.Skipped)
|
||||
require.Len(t, result.Results, 2)
|
||||
for _, res := range result.Results {
|
||||
assert.True(t, res.Skipped)
|
||||
assert.Equal(t, 1, res.ExitCode)
|
||||
assert.Error(t, res.Error)
|
||||
assert.Contains(t, res.Error.Error(), "context canceled")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunResult_Passed(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
r := RunResult{ExitCode: 0}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue