...
1
16
17 package ktesting
18
19 import (
20 "errors"
21 "fmt"
22 "strings"
23 "sync"
24
25 "github.com/onsi/gomega"
26 "k8s.io/klog/v2"
27 )
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 func WithError(tCtx TContext, err *error) (TContext, func()) {
45 eCtx := &errorContext{
46 TContext: tCtx,
47 }
48
49 return eCtx, func() {
50
51
52
53 if e := recover(); e != nil {
54 if _, ok := e.(fatalWithError); !ok {
55
56 panic(e)
57 }
58 }
59
60 eCtx.finalize(err)
61 }
62 }
63
64 type errorContext struct {
65 TContext
66
67 mutex sync.Mutex
68 errors []error
69 failed bool
70 }
71
72 func (eCtx *errorContext) finalize(err *error) {
73 eCtx.mutex.Lock()
74 defer eCtx.mutex.Unlock()
75
76 if !eCtx.failed {
77 return
78 }
79
80 errs := eCtx.errors
81 if len(errs) == 0 {
82 errs = []error{errFailedWithNoExplanation}
83 }
84 *err = errors.Join(errs...)
85 }
86
87 func (eCtx *errorContext) Error(args ...any) {
88 eCtx.mutex.Lock()
89 defer eCtx.mutex.Unlock()
90
91
92
93
94
95 eCtx.errors = append(eCtx.errors, errors.New(strings.TrimSpace(fmt.Sprintln(args...))))
96 eCtx.failed = true
97 }
98
99 func (eCtx *errorContext) Errorf(format string, args ...any) {
100 eCtx.mutex.Lock()
101 defer eCtx.mutex.Unlock()
102
103 eCtx.errors = append(eCtx.errors, errors.New(strings.TrimSpace(fmt.Sprintf(format, args...))))
104 eCtx.failed = true
105 }
106
107 func (eCtx *errorContext) Fail() {
108 eCtx.mutex.Lock()
109 defer eCtx.mutex.Unlock()
110
111 eCtx.failed = true
112 }
113
114 func (eCtx *errorContext) FailNow() {
115 eCtx.Helper()
116 eCtx.Fail()
117 panic(failed)
118 }
119
120 func (eCtx *errorContext) Failed() bool {
121 eCtx.mutex.Lock()
122 defer eCtx.mutex.Unlock()
123
124 return eCtx.failed
125 }
126
127 func (eCtx *errorContext) Fatal(args ...any) {
128 eCtx.Error(args...)
129 eCtx.FailNow()
130 }
131
132 func (eCtx *errorContext) Fatalf(format string, args ...any) {
133 eCtx.Errorf(format, args...)
134 eCtx.FailNow()
135 }
136
137 func (eCtx *errorContext) CleanupCtx(cb func(TContext)) {
138 eCtx.Helper()
139 cleanupCtx(eCtx, cb)
140 }
141
142 func (eCtx *errorContext) Expect(actual interface{}, extra ...interface{}) gomega.Assertion {
143 eCtx.Helper()
144 return expect(eCtx, actual, extra...)
145 }
146
147 func (eCtx *errorContext) ExpectNoError(err error, explain ...interface{}) {
148 eCtx.Helper()
149 expectNoError(eCtx, err, explain...)
150 }
151
152 func (eCtx *errorContext) Logger() klog.Logger {
153 return klog.FromContext(eCtx)
154 }
155
156
157
158
159
160 type fatalWithError string
161
162 const failed = fatalWithError("WithError TContext encountered a fatal error, but the finalize function was not called via defer as it should have been.")
163
164 var errFailedWithNoExplanation = errors.New("WithError context was marked as failed without recording an error")
165
View as plain text