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  }