Skip to content

Commit 20367d4

Browse files
authored
feat: ignore context.Canceled by default in slogtest (#207)
* feat: ignore context.Canceled by default in slogtest Signed-off-by: Spike Curtis <spike@coder.com> * code review suggestions Signed-off-by: Spike Curtis <spike@coder.com> --------- Signed-off-by: Spike Curtis <spike@coder.com>
1 parent f0c466f commit 20367d4

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed

sloggers/slogtest/t.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"sync"
1515
"testing"
1616

17+
"golang.org/x/xerrors"
18+
1719
"cdr.dev/slog"
1820
"cdr.dev/slog/internal/entryhuman"
1921
"cdr.dev/slog/sloggers/sloghuman"
@@ -36,13 +38,26 @@ type Options struct {
3638
// conditions exist when t.Log is called concurrently of a test exiting. Set
3739
// to true if you don't need this behavior.
3840
SkipCleanup bool
41+
// IgnoredErrorIs causes the test logger not to error the test on Error
42+
// if the SinkEntry contains one of the listed errors in its "error" Field.
43+
// Errors are matched using xerrors.Is().
44+
//
45+
// By default, context.Canceled and context.DeadlineExceeded are included,
46+
// as these are nearly always benign in testing. Override to []error{} (zero
47+
// length error slice) to disable the whitelist entirely.
48+
IgnoredErrorIs []error
3949
}
4050

41-
// Make creates a Logger that writes logs to tb in a human readable format.
51+
var DefaultIgnoredErrorIs = []error{context.Canceled, context.DeadlineExceeded}
52+
53+
// Make creates a Logger that writes logs to tb in a human-readable format.
4254
func Make(tb testing.TB, opts *Options) slog.Logger {
4355
if opts == nil {
4456
opts = &Options{}
4557
}
58+
if opts.IgnoredErrorIs == nil {
59+
opts.IgnoredErrorIs = DefaultIgnoredErrorIs
60+
}
4661

4762
sink := &testSink{
4863
tb: tb,
@@ -66,7 +81,7 @@ type testSink struct {
6681
testDone bool
6782
}
6883

69-
func (ts *testSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
84+
func (ts *testSink) LogEntry(_ context.Context, ent slog.SinkEntry) {
7085
ts.mu.RLock()
7186
defer ts.mu.RUnlock()
7287

@@ -83,7 +98,7 @@ func (ts *testSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
8398
case slog.LevelDebug, slog.LevelInfo, slog.LevelWarn:
8499
ts.tb.Log(sb.String())
85100
case slog.LevelError, slog.LevelCritical:
86-
if ts.opts.IgnoreErrors {
101+
if ts.shouldIgnoreError(ent) {
87102
ts.tb.Log(sb.String())
88103
} else {
89104
sb.WriteString(fmt.Sprintf(
@@ -98,6 +113,24 @@ func (ts *testSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
98113
}
99114
}
100115

116+
func (ts *testSink) shouldIgnoreError(ent slog.SinkEntry) bool {
117+
if ts.opts.IgnoreErrors {
118+
return true
119+
}
120+
for _, f := range ent.Fields {
121+
if f.Name == "error" {
122+
if err, ok := f.Value.(error); ok {
123+
for _, ig := range ts.opts.IgnoredErrorIs {
124+
if xerrors.Is(err, ig) {
125+
return true
126+
}
127+
}
128+
}
129+
}
130+
}
131+
return false
132+
}
133+
101134
func (ts *testSink) Sync() {}
102135

103136
var ctx = context.Background()

sloggers/slogtest/t_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"context"
55
"testing"
66

7+
"golang.org/x/xerrors"
8+
9+
"cdr.dev/slog"
710
"cdr.dev/slog/internal/assert"
811
"cdr.dev/slog/sloggers/slogtest"
912
)
@@ -15,6 +18,12 @@ func TestStateless(t *testing.T) {
1518
slogtest.Debug(tb, "hello")
1619
slogtest.Info(tb, "hello")
1720

21+
slogtest.Error(tb, "canceled", slog.Error(xerrors.Errorf("test %w:", context.Canceled)))
22+
assert.Equal(t, "errors", 0, tb.errors)
23+
24+
slogtest.Error(tb, "deadline", slog.Error(xerrors.Errorf("test %w:", context.DeadlineExceeded)))
25+
assert.Equal(t, "errors", 0, tb.errors)
26+
1827
slogtest.Error(tb, "hello")
1928
assert.Equal(t, "errors", 1, tb.errors)
2029

@@ -45,6 +54,60 @@ func TestIgnoreErrors(t *testing.T) {
4554
l.Fatal(bg, "hello")
4655
}
4756

57+
func TestIgnoreErrorIs_Default(t *testing.T) {
58+
t.Parallel()
59+
60+
tb := &fakeTB{}
61+
l := slogtest.Make(tb, nil)
62+
63+
l.Error(bg, "canceled", slog.Error(xerrors.Errorf("test %w:", context.Canceled)))
64+
assert.Equal(t, "errors", 0, tb.errors)
65+
66+
l.Error(bg, "deadline", slog.Error(xerrors.Errorf("test %w:", context.DeadlineExceeded)))
67+
assert.Equal(t, "errors", 0, tb.errors)
68+
69+
l.Error(bg, "new", slog.Error(xerrors.New("test")))
70+
assert.Equal(t, "errors", 1, tb.errors)
71+
72+
defer func() {
73+
recover()
74+
assert.Equal(t, "fatals", 1, tb.fatals)
75+
}()
76+
77+
l.Fatal(bg, "hello", slog.Error(xerrors.Errorf("fatal %w:", context.Canceled)))
78+
}
79+
80+
func TestIgnoreErrorIs_Explicit(t *testing.T) {
81+
t.Parallel()
82+
83+
tb := &fakeTB{}
84+
ignored := xerrors.New("ignored")
85+
notIgnored := xerrors.New("not ignored")
86+
l := slogtest.Make(tb, &slogtest.Options{IgnoredErrorIs: []error{ignored}})
87+
88+
l.Error(bg, "ignored", slog.Error(xerrors.Errorf("test %w:", ignored)))
89+
assert.Equal(t, "errors", 0, tb.errors)
90+
91+
l.Error(bg, "not ignored", slog.Error(xerrors.Errorf("test %w:", notIgnored)))
92+
assert.Equal(t, "errors", 1, tb.errors)
93+
94+
l.Error(bg, "canceled", slog.Error(xerrors.Errorf("test %w:", context.Canceled)))
95+
assert.Equal(t, "errors", 2, tb.errors)
96+
97+
l.Error(bg, "deadline", slog.Error(xerrors.Errorf("test %w:", context.DeadlineExceeded)))
98+
assert.Equal(t, "errors", 3, tb.errors)
99+
100+
l.Error(bg, "new", slog.Error(xerrors.New("test")))
101+
assert.Equal(t, "errors", 4, tb.errors)
102+
103+
defer func() {
104+
recover()
105+
assert.Equal(t, "fatals", 1, tb.fatals)
106+
}()
107+
108+
l.Fatal(bg, "hello", slog.Error(xerrors.Errorf("test %w:", ignored)))
109+
}
110+
48111
func TestCleanup(t *testing.T) {
49112
t.Parallel()
50113

0 commit comments

Comments
 (0)