1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package cli
import (
"context"
"errors"
"testing"
)
// TestWorkerPoolFailFastSwallowsCancellation locks in the §8.3 invariant that a
// fail-fast pool reports only GENUINE failures: the first real error cancels the
// pool, and the in-flight siblings that consequently return context.Canceled are
// swallowed (cancellation collateral is never an independent per-file failure).
func TestWorkerPoolFailFastSwallowsCancellation(t *testing.T) {
pool := newWorkerPool(context.Background(), 8, true)
// Occupy several slots with tasks that block until the pool is cancelled and
// then return context.Canceled (the collateral that must be swallowed).
const blockers = 5
for i := 0; i < blockers; i++ {
pool.submit("block", func(ctx context.Context) error {
<-ctx.Done()
return ctx.Err() // context.Canceled
})
}
// One genuine failure triggers fail-fast cancellation of the blockers above.
pool.submit("fail", func(ctx context.Context) error {
return fileError{path: "a.js", err: errors.New("boom")}
})
succeeded, errs := pool.wait()
if succeeded != 0 {
t.Fatalf("succeeded = %d, want 0", succeeded)
}
if len(errs) != 1 {
t.Fatalf("errs = %d (%v), want exactly 1 genuine failure", len(errs), errs)
}
if errors.Is(errs[0], context.Canceled) {
t.Fatalf("reported error is cancellation collateral, want the genuine failure: %v", errs[0])
}
var fe fileError
if !errors.As(errs[0], &fe) || fe.path != "a.js" {
t.Fatalf("reported error = %v, want fileError for a.js", errs[0])
}
}
// TestWorkerPoolDrainReportsAll confirms the non-fail-fast (drain-and-report)
// mode collects every failure and still runs all tasks.
func TestWorkerPoolDrainReportsAll(t *testing.T) {
pool := newWorkerPool(context.Background(), 4, false)
const tasks = 6
for i := 0; i < tasks; i++ {
odd := i%2 == 1
pool.submit("t", func(ctx context.Context) error {
if odd {
return errors.New("fail")
}
return nil
})
}
succeeded, errs := pool.wait()
if succeeded != 3 {
t.Fatalf("succeeded = %d, want 3", succeeded)
}
if len(errs) != 3 {
t.Fatalf("errs = %d, want 3", len(errs))
}
}
|