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  }