-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy pathcontext_test.go
148 lines (132 loc) · 4.54 KB
/
context_test.go
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2017 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package contextutil
import (
"context"
"fmt"
"net"
"testing"
"time"
"github.com/cockroachdb/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRunWithTimeout(t *testing.T) {
ctx := context.Background()
err := RunWithTimeout(ctx, "foo", 1, func(ctx context.Context) error {
time.Sleep(10 * time.Millisecond)
return nil
})
if err != nil {
t.Fatal("RunWithTimeout shouldn't return a timeout error if nobody touched the context.")
}
err = RunWithTimeout(ctx, "foo", 1, func(ctx context.Context) error {
time.Sleep(15 * time.Millisecond)
return ctx.Err()
})
require.Error(t, err)
baseExpectedMsg := "operation \"foo\" timed out after ..ms \\(given timeout 1ns\\)"
expectedMsg := baseExpectedMsg + ": context deadline exceeded"
require.Regexp(t, expectedMsg, err.Error())
var netError net.Error
if !errors.As(err, &netError) {
t.Fatal("RunWithTimeout should return a net.Error")
}
//lint:ignore SA1019 grandfathered test code
if !netError.Timeout() || !netError.Temporary() {
t.Fatal("RunWithTimeout should return a timeout and temporary error")
}
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("RunWithTimeout should return an error with a DeadlineExceeded cause")
}
err = RunWithTimeout(ctx, "foo", 1, func(ctx context.Context) error {
time.Sleep(15 * time.Millisecond)
return errors.Wrap(ctx.Err(), "custom error")
})
require.Error(t, err)
expExtended := baseExpectedMsg + ": custom error: context deadline exceeded"
require.Regexp(t, expExtended, err.Error())
if !errors.As(err, &netError) {
t.Fatal("RunWithTimeout should return a net.Error")
}
//lint:ignore SA1019 grandfathered test code
if !netError.Timeout() || !netError.Temporary() {
t.Fatal("RunWithTimeout should return a timeout and temporary error")
}
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("RunWithTimeout should return an error with a DeadlineExceeded cause")
}
}
func testFuncA(ctx context.Context) error {
return testFuncB(ctx)
}
func testFuncB(ctx context.Context) error {
<-ctx.Done()
return ctx.Err()
}
func TestRunWithTimeoutCtxWithStacktrace(t *testing.T) {
ctx := context.Background()
err := RunWithTimeout(ctx, "foo", 1, testFuncA)
require.Error(t, err)
stacktrace := fmt.Sprintf("%+v", err)
require.Contains(t, stacktrace, "testFuncB")
require.Contains(t, stacktrace, "testFuncA")
}
// TestRunWithTimeoutWithoutDeadlineExceeded ensures that when a timeout on the
// context occurs but the underlying error does not have
// context.DeadlineExceeded as its Cause (perhaps due to serialization) the
// returned error is still a TimeoutError. In this case however the underlying
// cause should be the returned error and not context.DeadlineExceeded.
func TestRunWithTimeoutWithoutDeadlineExceeded(t *testing.T) {
ctx := context.Background()
notContextDeadlineExceeded := errors.Handled(context.DeadlineExceeded)
err := RunWithTimeout(ctx, "foo", 1, func(ctx context.Context) error {
<-ctx.Done()
return notContextDeadlineExceeded
})
var netError net.Error
if !errors.As(err, &netError) {
t.Fatal("RunWithTimeout should return a net.Error")
}
//lint:ignore SA1019 grandfathered test code
if !netError.Timeout() || !netError.Temporary() {
t.Fatal("RunWithTimeout should return a timeout and temporary error")
}
if !errors.Is(err, notContextDeadlineExceeded) {
t.Fatalf("RunWithTimeout should return an error caused by the underlying " +
"returned error")
}
}
func TestRunWithTimeoutAfterDeadline(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
defer cancel()
err := RunWithTimeout(ctx, "test", time.Second, func(ctx context.Context) error {
<-ctx.Done()
return ctx.Err()
})
require.Error(t, err)
require.Regexp(t,
`operation "test" timed out after \d+m?s \(given timeout 1s\): context deadline exceeded`,
err.Error())
}
func TestCancelWithReason(t *testing.T) {
ctx := context.Background()
var cancel CancelWithReasonFunc
ctx, cancel = WithCancelReason(ctx)
e := errors.New("hodor")
go func() {
cancel(e)
}()
<-ctx.Done()
expected := "context canceled"
found := ctx.Err().Error()
assert.Equal(t, expected, found)
assert.Equal(t, e, GetCancelReason(ctx))
}