github.com/mitranim/gg@v0.1.17/err_test.go (about) 1 package gg_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "strings" 8 "testing" 9 10 "github.com/mitranim/gg" 11 "github.com/mitranim/gg/gtest" 12 ) 13 14 // Limited sanity check, TODO full test. 15 func TestErr(t *testing.T) { 16 defer gtest.Catch(t) 17 18 err := gg.Err{}.Msgf(`unable to perform %v`, `some operation`).Caused(io.EOF).TracedAt(0) 19 20 gtest.Eq(err.Error(), `unable to perform some operation: EOF`) 21 22 gtest.TextHas(err.Stack(), strings.TrimSpace(` 23 unable to perform some operation: EOF 24 trace: 25 TestErr err_test.go 26 `)) 27 } 28 29 func TestErrTrace(t *testing.T) { 30 defer gtest.Catch(t) 31 32 inner := gg.Errf(`inner`) 33 outer := fmt.Errorf(`outer: %w`, inner) 34 35 gtest.Eq(outer.Error(), `outer: inner`) 36 gtest.True(errors.Is(outer, inner)) 37 gtest.Equal(gg.ErrTrace(inner), gg.PtrGet(inner.Trace)) 38 gtest.Equal(gg.ErrTrace(outer), gg.PtrGet(inner.Trace)) 39 } 40 41 func TestAnyErr(t *testing.T) { 42 defer gtest.Catch(t) 43 44 gtest.Zero(gg.AnyErr(nil)) 45 gtest.AnyEq(gg.AnyErr(`str`), error(gg.ErrStr(`str`))) 46 gtest.AnyEq(gg.AnyErr(gg.ErrStr(`str`)), error(gg.ErrStr(`str`))) 47 gtest.AnyEq(gg.AnyErr(gg.ErrAny{`str`}), error(gg.ErrAny{`str`})) 48 } 49 50 func TestErrs_Error(t *testing.T) { 51 defer gtest.Catch(t) 52 53 gtest.Zero(gg.Errs(nil).Error()) 54 gtest.Zero(gg.Errs{}.Error()) 55 56 gtest.Eq( 57 gg.Errs{nil, testErr0, nil}.Error(), 58 `test err 0`, 59 ) 60 61 gtest.Eq( 62 gg.Errs{nil, testErr0, nil, testErr1, nil}.Error(), 63 `multiple errors; test err 0; test err 1`, 64 ) 65 } 66 67 func TestErrs_Err(t *testing.T) { 68 defer gtest.Catch(t) 69 70 testEmpty := func(src gg.Errs) { 71 gtest.Zero(src.Err()) 72 } 73 74 testEmpty(gg.Errs(nil)) 75 testEmpty(gg.Errs{}) 76 testEmpty(gg.Errs{nil, nil, nil}) 77 78 testOne := func(exp error) { 79 gtest.Equal(gg.Errs{nil, exp, nil}.Err(), exp) 80 gtest.Equal(gg.Errs{exp, nil}.Err(), exp) 81 gtest.Equal(gg.Errs{nil, exp}.Err(), exp) 82 } 83 84 testOne(testErr0) 85 testOne(testErr1) 86 87 errs := gg.Errs{nil, testErr0, nil, testErr1, nil} 88 gtest.Equal(errs.Err(), error(errs)) 89 } 90 91 func TestErrs_Unwrap(t *testing.T) { 92 defer gtest.Catch(t) 93 94 gtest.Zero(gg.Errs(nil).Unwrap()) 95 gtest.Zero(gg.Errs{}.Unwrap()) 96 gtest.Equal(gg.Errs{nil, testErr0}.Unwrap(), testErr0) 97 gtest.Equal(gg.Errs{nil, testErr0, testErr1}.Unwrap(), testErr0) 98 } 99 100 func TestErrs_Is(t *testing.T) { 101 defer gtest.Catch(t) 102 103 gtest.False(errors.Is(gg.Errs(nil), io.EOF)) 104 gtest.False(errors.Is(gg.Errs(nil), testErr0)) 105 106 gtest.False(errors.Is(gg.Errs{nil, testErr0, nil, testErr1, nil}, io.EOF)) 107 gtest.False(errors.Is(gg.Errs{nil, testErr0, nil, testErr1, nil}, testErr2)) 108 109 gtest.True(errors.Is(gg.Errs{nil, testErr0, nil, testErr1, nil}, testErr0)) 110 gtest.True(errors.Is(gg.Errs{nil, testErr0, nil, testErr1, nil}, testErr1)) 111 112 gtest.True(errors.Is(gg.Errs{nil, gg.Wrapf(testErr0, ``), nil, testErr1, nil}, testErr0)) 113 gtest.True(errors.Is(gg.Errs{nil, gg.Wrapf(io.EOF, ``), nil, testErr1, nil}, io.EOF)) 114 115 gtest.True(errors.Is(gg.Errs{nil, testErr0, nil, gg.Wrapf(testErr1, ``), nil}, testErr1)) 116 gtest.True(errors.Is(gg.Errs{nil, testErr0, nil, gg.Wrapf(io.EOF, ``), nil}, io.EOF)) 117 } 118 119 func TestErrs_As(t *testing.T) { 120 defer gtest.Catch(t) 121 122 test := func(src gg.Errs, ok bool, exp gg.ErrStr) { 123 var tar gg.ErrStr 124 gtest.Eq(errors.As(src, &tar), ok) 125 gtest.Eq(tar, exp) 126 } 127 128 test(gg.Errs(nil), false, ``) 129 test(gg.Errs{}, false, ``) 130 test(gg.Errs{testErr0}, false, ``) 131 test(gg.Errs{testErr0, testErr1}, false, ``) 132 test(gg.Errs{testErrA, testErr0, testErr1}, true, testErrA) 133 test(gg.Errs{testErr0, testErrA, testErr1}, true, testErrA) 134 test(gg.Errs{testErr0, testErr1, testErrA}, true, testErrA) 135 test(gg.Errs{nil, testErr0, nil, testErr1, nil, testErrA, nil}, true, testErrA) 136 test(gg.Errs{nil, testErrA, nil, testErrB, nil}, true, testErrA) 137 } 138 139 func TestErrs_Find(t *testing.T) { 140 defer gtest.Catch(t) 141 142 for _, src := range []gg.Errs{nil, {}, {nil}, {nil, nil}} { 143 for _, fun := range []func(error) bool{nil, True1[error], False1[error]} { 144 gtest.Zero(src.Find(fun)) 145 } 146 } 147 148 match := func(err error) bool { return err == testErr0 } 149 150 for _, src := range []gg.Errs{ 151 {testErr0, testErr1, testErrA, testErrB}, 152 {testErr1, testErr0, testErrA, testErrB}, 153 {testErr1, testErrA, testErr0, testErrB}, 154 {testErr1, testErrA, testErrB, testErr0}, 155 {testErr1, nil, nil, testErr0}, 156 {testErr1, gg.Wrapf(fmt.Errorf(`one: %w`, testErr0), `two`)}, 157 } { 158 gtest.Equal(src.Find(match), testErr0) 159 } 160 } 161 162 func TestErrStr(t *testing.T) { 163 defer gtest.Catch(t) 164 165 const err0 gg.ErrStr = `err0` 166 const err1 gg.ErrStr = `err1` 167 168 wrap0 := gg.Wrapf(err0, `wrap0`) 169 wrap1 := gg.Wrapf(err1, `wrap1`) 170 171 gtest.False(errors.Is(err0, err1)) 172 gtest.False(errors.Is(err1, err0)) 173 gtest.False(errors.Is(wrap0, err1)) 174 gtest.False(errors.Is(wrap1, err0)) 175 176 gtest.True(errors.Is(err0, err0)) 177 gtest.True(errors.Is(err1, err1)) 178 gtest.True(errors.Is(wrap0, err0)) 179 gtest.True(errors.Is(wrap1, err1)) 180 } 181 182 func TestWrapf(t *testing.T) { 183 defer gtest.Catch(t) 184 185 t.Run(`cause_without_stack`, func(t *testing.T) { 186 defer gtest.Catch(t) 187 188 err := gg.Wrapf(gg.ErrAny{`some cause`}, `unable to %v`, `do stuff`).(gg.Err) 189 190 gtest.Equal(err.Msg, `unable to do stuff`) 191 gtest.Equal(err.Cause, error(gg.ErrAny{`some cause`})) 192 gtest.True(gg.PtrGet(err.Trace).IsNotEmpty()) 193 }) 194 195 t.Run(`cause_with_stack`, func(t *testing.T) { 196 defer gtest.Catch(t) 197 198 err := gg.Wrapf(gg.Errf(`some cause`), `unable to %v`, `do stuff`).(gg.Err) 199 200 gtest.Equal(err.Msg, `unable to do stuff`) 201 gtest.False(gg.PtrGet(err.Trace).IsNotEmpty()) 202 203 cause := err.Cause.(gg.Err) 204 gtest.Equal(cause.Msg, `some cause`) 205 gtest.True(gg.PtrGet(cause.Trace).IsNotEmpty()) 206 }) 207 } 208 209 /* 210 Internally `Wrap` is very similar to `Wrapf` which has a fuller test. 211 Here, we only need to test message generation. 212 */ 213 func TestWrap(t *testing.T) { 214 defer gtest.Catch(t) 215 216 err := gg.Wrap(gg.ErrAny{`some cause`}, `unable to %v `, `do stuff`).(gg.Err) 217 gtest.Equal(err.Msg, `unable to %v do stuff`) 218 gtest.Equal(err.Cause, error(gg.ErrAny{`some cause`})) 219 } 220 221 func BenchmarkIsErrTraced_error_without_trace(b *testing.B) { 222 const err = gg.ErrStr(`some error`) 223 gtest.False(gg.IsErrTraced(err)) 224 225 for ind := 0; ind < b.N; ind++ { 226 gg.Nop1(gg.IsErrTraced(err)) 227 } 228 } 229 230 func BenchmarkIsErrTraced_error_with_trace(b *testing.B) { 231 err := error(gg.Errf(`some error`)) 232 gtest.True(gg.IsErrTraced(err)) 233 234 for ind := 0; ind < b.N; ind++ { 235 gg.Nop1(gg.IsErrTraced(err)) 236 } 237 } 238 239 func BenchmarkErrTraced_error_without_trace(b *testing.B) { 240 const err = gg.ErrStr(`some error`) 241 gtest.False(gg.IsErrTraced(err)) 242 243 for ind := 0; ind < b.N; ind++ { 244 gg.Nop1(gg.ErrTracedAt(err, 1)) 245 } 246 } 247 248 func BenchmarkErrTraced_error_with_trace(b *testing.B) { 249 err := error(gg.Errf(`some error`)) 250 gtest.True(gg.IsErrTraced(err)) 251 252 for ind := 0; ind < b.N; ind++ { 253 gg.Nop1(gg.ErrTracedAt(err, 1)) 254 } 255 } 256 257 func TestErrAs(t *testing.T) { 258 defer gtest.Catch(t) 259 260 gtest.Zero(gg.ErrAs[gg.Err](nil)) 261 gtest.Zero(gg.ErrAs[gg.ErrStr](nil)) 262 gtest.Zero(gg.ErrAs[PtrErrStr](nil)) 263 264 gtest.Zero(gg.ErrAs[gg.Err](fmt.Errorf(``))) 265 gtest.Zero(gg.ErrAs[gg.ErrStr](fmt.Errorf(``))) 266 gtest.Zero(gg.ErrAs[PtrErrStr](fmt.Errorf(``))) 267 268 gtest.Zero(gg.ErrAs[gg.Err](fmt.Errorf(``))) 269 gtest.Zero(gg.ErrAs[gg.ErrStr](fmt.Errorf(``))) 270 gtest.Zero(gg.ErrAs[PtrErrStr](fmt.Errorf(``))) 271 272 gtest.Zero(gg.ErrAs[gg.Err](fmt.Errorf(`%w`, fmt.Errorf(``)))) 273 gtest.Zero(gg.ErrAs[gg.ErrStr](fmt.Errorf(`%w`, fmt.Errorf(``)))) 274 gtest.Zero(gg.ErrAs[PtrErrStr](fmt.Errorf(`%w`, fmt.Errorf(``)))) 275 276 gtest.Eq( 277 gg.ErrAs[gg.ErrStr](gg.ErrStr(`one`)), 278 gg.ErrStr(`one`), 279 ) 280 281 gtest.Eq( 282 gg.ErrAs[gg.ErrStr](fmt.Errorf(`%w`, gg.ErrStr(`one`))), 283 gg.ErrStr(`one`), 284 ) 285 286 gtest.Eq( 287 gg.ErrAs[PtrErrStr](gg.Ptr(PtrErrStr(`one`))), 288 PtrErrStr(`one`), 289 ) 290 291 gtest.Eq( 292 gg.ErrAs[PtrErrStr](fmt.Errorf(`%w`, gg.Ptr(PtrErrStr(`one`)))), 293 PtrErrStr(`one`), 294 ) 295 } 296 297 func TestErrFind(t *testing.T) { 298 defer gtest.Catch(t) 299 300 inner := error(gg.ErrStr(`one`)) 301 outer := gg.Wrapf(inner, `two`) 302 303 gtest.Zero(gg.ErrFind(nil, nil)) 304 gtest.Zero(gg.ErrFind(nil, True1[error])) 305 gtest.Zero(gg.ErrFind(nil, False1[error])) 306 gtest.Zero(gg.ErrFind(inner, False1[error])) 307 gtest.Zero(gg.ErrFind(outer, False1[error])) 308 309 gtest.Equal(gg.ErrFind(inner, True1[error]), inner) 310 gtest.Equal(gg.ErrFind(outer, True1[error]), outer) 311 312 test := func(err error) bool { return err == inner } 313 gtest.Equal(gg.ErrFind(outer, test), inner) 314 } 315 316 func TestErrStack(t *testing.T) { 317 defer gtest.Catch(t) 318 319 gtest.Zero(gg.ErrStack(nil)) 320 321 t.Run(`without_trace`, func(t *testing.T) { 322 defer gtest.Catch(t) 323 324 gtest.Eq(gg.ErrStack(gg.ErrStr(`str`)), `str`) 325 gtest.Eq(gg.ErrStack(gg.Err{Msg: `str`}), `str`) 326 gtest.Eq(gg.ErrStack(fmt.Errorf(`str`)), `str`) 327 }) 328 329 t.Run(`traced_without_message`, func(t *testing.T) { 330 defer gtest.Catch(t) 331 332 err := gg.Err{}.TracedAt(0) 333 334 gtest.Eq(gg.ErrStack(err), gg.Newline+`func2 err_test.go:332`) 335 }) 336 337 t.Run(`wrapped_without_message`, func(t *testing.T) { 338 defer gtest.Catch(t) 339 340 inner := gg.Err{Msg: `inner`}.TracedAt(0) 341 outer := gg.Err{Cause: inner} 342 343 gtest.Eq(gg.ErrStack(outer), inner.Stack()) 344 345 gtest.Eq(gg.ErrStack(outer), strings.TrimSpace(` 346 inner 347 trace: 348 func3 err_test.go:340 349 `)) 350 }) 351 352 t.Run(`wrapped_with_message`, func(t *testing.T) { 353 defer gtest.Catch(t) 354 355 inner := gg.Err{Msg: `inner`}.TracedAt(0) 356 outer := gg.Err{Msg: `outer`, Cause: inner} 357 358 gtest.Eq(gg.ErrStack(outer), strings.TrimSpace(` 359 outer: inner 360 trace: 361 func4 err_test.go:355 362 `)) 363 }) 364 }