github.com/richardwilkes/toolbox@v1.121.0/errs/errors_test.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package errs_test 11 12 import ( 13 "errors" 14 "fmt" 15 "os" 16 "strings" 17 "testing" 18 19 "github.com/richardwilkes/toolbox/check" 20 "github.com/richardwilkes/toolbox/errs" 21 ) 22 23 func ExampleError() { 24 var bad *int 25 func() int { 26 defer errs.Recovery(func(err error) { fmt.Println(err) }) 27 return *bad // trigger a panic due to a nil pointer dereference 28 }() 29 // Output: recovered from panic 30 // [github.com/richardwilkes/toolbox/errs_test.ExampleError.func1] errors_test.go:27 31 // [github.com/richardwilkes/toolbox/errs_test.ExampleError] errors_test.go:28 32 // Caused by: runtime error: invalid memory address or nil pointer dereference 33 } 34 35 func TestAppendError(t *testing.T) { 36 original := errs.New("foo") 37 result := errs.Append(original, errs.New("bar")) 38 check.Equal(t, 2, result.Count()) 39 } 40 41 func TestAppendToEmptyError(t *testing.T) { 42 original := &errs.Error{} 43 result := errs.Append(original, errs.New("bar")) 44 check.Equal(t, 1, result.Count()) 45 } 46 47 func TestAppendFlattening(t *testing.T) { 48 original := errs.New("foo") 49 result := errs.Append(original, errs.Append(nil, errs.New("foo"), errs.New("bar"))) 50 check.Equal(t, 3, result.Count()) 51 } 52 53 func TestAppendTypedNil(t *testing.T) { 54 var e *errs.Error 55 result := errs.Append(e, errs.New("bar")) 56 check.Equal(t, 1, result.Count()) 57 } 58 59 func TestAppendNilError(t *testing.T) { 60 var err error 61 result := errs.Append(err, errs.New("bar")) 62 check.Equal(t, 1, result.Count()) 63 } 64 65 func TestAppendNilErrorArg(t *testing.T) { 66 var err error 67 var nilErr *errs.Error 68 result := errs.Append(err, nilErr) 69 check.Equal(t, 0, result.Count()) 70 } 71 72 func TestAppendNilErrorInterfaceArg(t *testing.T) { 73 var err error 74 var nilErr error 75 result := errs.Append(err, nilErr) 76 check.Equal(t, 0, result.Count()) 77 } 78 79 func TestAppendNonErrorError(t *testing.T) { 80 original := errors.New("foo") 81 result := errs.Append(original, errs.New("bar")) 82 check.Equal(t, 2, result.Count()) 83 } 84 85 func TestAppendNonErrorErrorWithAppend(t *testing.T) { 86 original := errors.New("foo") 87 result := errs.Append(original, errs.Append(nil, errors.New("bar"))) 88 check.Equal(t, 2, result.Count()) 89 } 90 91 func TestErrorOrNil(t *testing.T) { 92 var err errs.Error 93 check.Nil(t, err.ErrorOrNil()) 94 } 95 96 func TestErrorOrNilPointer(t *testing.T) { 97 var err *errs.Error 98 check.Nil(t, err.ErrorOrNil()) 99 } 100 101 func TestWrap(t *testing.T) { 102 notError := errors.New("foo") 103 result := errs.Wrap(notError) 104 check.NotNil(t, result) 105 check.Equal(t, 1, strings.Count(result.Error(), "\n")) 106 } 107 108 func TestDoubleWrap(t *testing.T) { 109 errError := error(errs.New("foo")) 110 111 // Verify *errs.Error doesn't get wrapped again 112 check.Equal(t, errError, errs.Wrap(errError)) 113 114 // Wrap the error using the standard library 115 wrappedErr := fmt.Errorf("bar: %w", errError) 116 check.Equal(t, errError, errors.Unwrap(wrappedErr)) 117 118 // Verify that an error with an embedded *errs.Error cause doesn't get wrapped again 119 check.Equal(t, wrappedErr, errs.Wrap(wrappedErr)) 120 } 121 122 func TestDoubleWrapTyped(t *testing.T) { 123 errError := errs.New("foo") 124 125 // Verify *errs.Error doesn't get wrapped again 126 check.Equal(t, errError, errs.WrapTyped(errError)) 127 128 // Wrap the error using the standard library 129 wrappedErr := fmt.Errorf("bar: %w", errError) 130 check.Equal(t, error(errError), errors.Unwrap(wrappedErr)) 131 132 // It seems the best thing to do here is to wrap again 133 rewrappedError := errs.WrapTyped(wrappedErr) 134 check.Equal(t, wrappedErr, errors.Unwrap(rewrappedError)) 135 } 136 137 func TestIs(t *testing.T) { 138 err := errs.Wrap(os.ErrNotExist) 139 check.NotNil(t, err) 140 check.True(t, errors.Is(err, os.ErrNotExist)) 141 check.False(t, errors.Is(err, os.ErrClosed)) 142 } 143 144 type customErr struct { 145 value string 146 } 147 148 func (e *customErr) Error() string { 149 return e.value 150 } 151 152 func TestAs(t *testing.T) { 153 original := &customErr{value: "err"} 154 wrapped := errs.Wrap(original) 155 check.NotNil(t, wrapped) 156 var target *customErr 157 check.True(t, errors.As(wrapped, &target)) 158 check.Equal(t, original, target) 159 } 160 161 func TestNew(t *testing.T) { 162 result := errs.New("foo") 163 check.Equal(t, 1, strings.Count(result.Error(), "\n")) 164 } 165 166 func TestNewWithCause(t *testing.T) { 167 cause := errs.New("bar") 168 result := errs.NewWithCause("foo", cause) 169 check.Equal(t, 3, strings.Count(result.Error(), "\n")) 170 } 171 172 func TestFormat(t *testing.T) { 173 err := errs.New("test") 174 check.Equal(t, "test", fmt.Sprintf("%s", err)) 175 check.Equal(t, `"test"`, fmt.Sprintf("%q", err)) 176 result := fmt.Sprintf("%v", err) 177 check.Contains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestFormat]") 178 check.NotContains(t, result, "[runtime.goexit]") 179 result = fmt.Sprintf("%+v", err) 180 check.Contains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestFormat]") 181 check.Contains(t, result, "[runtime.goexit]") 182 183 wrappedErrors := err.WrappedErrors() 184 check.Equal(t, 1, len(wrappedErrors)) 185 check.Equal(t, "test", fmt.Sprintf("%s", wrappedErrors[0])) //nolint:gocritic // Testing %s, so necessary 186 check.Equal(t, `"test"`, fmt.Sprintf("%q", wrappedErrors[0])) 187 result = fmt.Sprintf("%v", wrappedErrors[0]) //nolint:gocritic // Testing %v, so necessary 188 check.Contains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestFormat]") 189 check.NotContains(t, result, "[runtime.goexit]") 190 result = fmt.Sprintf("%+v", wrappedErrors[0]) 191 check.Contains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestFormat]") 192 check.Contains(t, result, "[runtime.goexit]") 193 } 194 195 func TestWrappedErrors(t *testing.T) { 196 foo := errs.New("foo") 197 bar := errs.Append(foo, errs.New("bar")) 198 foo2 := errs.New("foo2") 199 bar2 := errs.Append(foo2, errs.New("bar2")) 200 result := errs.Append(bar, bar2) 201 list := result.WrappedErrors() 202 check.Equal(t, 4, len(list)) 203 check.Equal(t, "foo", strings.SplitN(list[0].Error(), "\n", 2)[0]) 204 check.Equal(t, "bar", strings.SplitN(list[1].Error(), "\n", 2)[0]) 205 check.Equal(t, "foo2", strings.SplitN(list[2].Error(), "\n", 2)[0]) 206 check.Equal(t, "bar2", strings.SplitN(list[3].Error(), "\n", 2)[0]) 207 } 208 209 func TestAlteredFilter(t *testing.T) { 210 err := errs.New("test") 211 result := fmt.Sprintf("%v", err) 212 check.Contains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestAlteredFilter]") 213 saved := errs.RuntimePrefixesToFilter 214 errs.RuntimePrefixesToFilter = []string{"github.com/richardwilkes/toolbox/errs_test.TestAlteredFilter"} 215 result = fmt.Sprintf("%v", err) 216 check.NotContains(t, result, "[github.com/richardwilkes/toolbox/errs_test.TestAlteredFilter]") 217 errs.RuntimePrefixesToFilter = saved 218 }