-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy patherrors_test.go
More file actions
113 lines (100 loc) · 3.13 KB
/
Copy patherrors_test.go
File metadata and controls
113 lines (100 loc) · 3.13 KB
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package iago
import (
"context"
"errors"
"io/fs"
"testing"
)
// fakeHost is a no-op Host for exercising Group.Run without a real
// connection. Name is used to label task errors; cmd, if set, backs
// NewCommand for tests that exercise Shell/Output; fsys, if set, backs GetFS
// for tests that exercise Upload/UploadFile.
type fakeHost struct {
name string
cmd CmdRunner
fsys fs.FS
}
func (h fakeHost) Name() string { return h.name }
func (h fakeHost) Address() string { return h.name }
func (h fakeHost) GetEnv(string) string { return "" }
func (h fakeHost) GetFS() fs.FS { return h.fsys }
func (h fakeHost) NewCommand() (CmdRunner, error) { return h.cmd, nil }
func (h fakeHost) Close() error { return nil }
func (h fakeHost) SetVar(string, any) {}
func (h fakeHost) GetVar(string) (any, bool) { return nil, false }
func TestErrorsCollector(t *testing.T) {
var errs Errors
if err := errs.Err(); err != nil {
t.Fatalf("empty collector: got %v, want nil", err)
}
e1 := errors.New("boom 1")
e2 := errors.New("boom 2")
errs.Handle(e1)
errs.Handle(e2)
got := errs.Err()
if !errors.Is(got, e1) || !errors.Is(got, e2) {
t.Fatalf("joined error %v does not wrap both %v and %v", got, e1, e2)
}
}
func TestWithErrorHandlerOption(t *testing.T) {
var errs Errors
cfg := applyGroupOptions(WithErrorHandler(errs.Handle))
if cfg.errorHandler == nil {
t.Fatal("WithErrorHandler did not set errorHandler")
}
cfg.errorHandler(errors.New("boom"))
if errs.Err() == nil {
t.Fatal("handler set by the option did not reach the collector")
}
}
func TestGroupRunCollectsErrors(t *testing.T) {
var errs Errors
g := NewGroup([]Host{fakeHost{name: "a"}, fakeHost{name: "b"}, fakeHost{name: "c"}})
g.ErrorHandler = errs.Handle
g.Run("task", func(ctx context.Context, host Host) error {
if host.Name() == "b" {
return errors.New("task failed")
}
return nil
})
err := errs.Err()
if err == nil {
t.Fatal("expected an error from host b, got nil")
}
// Only host b fails, so exactly one error should be collected.
if joined, ok := err.(interface{ Unwrap() []error }); ok {
if n := len(joined.Unwrap()); n != 1 {
t.Fatalf("collected %d errors, want 1", n)
}
}
var te TaskError
if !errors.As(err, &te) {
t.Fatalf("error %v is not a TaskError", err)
}
if te.HostName != "b" {
t.Fatalf("TaskError host = %q, want b", te.HostName)
}
}
func TestCollect(t *testing.T) {
g := NewGroup([]Host{fakeHost{name: "a"}, fakeHost{name: "b"}, fakeHost{name: "c"}})
results, err := Collect(g, "task", func(ctx context.Context, host Host) (int, error) {
if host.Name() == "b" {
return 0, errors.New("task failed")
}
return len(host.Name()), nil
})
if err == nil {
t.Fatal("expected an error from host b, got nil")
}
if len(results) != 2 {
t.Fatalf("results = %v, want exactly hosts a and c", results)
}
if _, ok := results["b"]; ok {
t.Fatal("host b returned an error but still has an entry")
}
for _, host := range []string{"a", "c"} {
if got, want := results[host], 1; got != want {
t.Fatalf("results[%q] = %d, want %d", host, got, want)
}
}
}