github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/chain_test.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package throw
     7  
     8  import (
     9  	"fmt"
    10  	"io"
    11  	"runtime"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  type errType1 struct {
    19  	m string
    20  }
    21  
    22  func (errType1) Error() string {
    23  	return ""
    24  }
    25  
    26  type errType2 struct {
    27  	m func() // incomparable
    28  }
    29  
    30  func (errType2) Error() string {
    31  	return ""
    32  }
    33  
    34  func TestIsEqual(t *testing.T) {
    35  	require.Panics(t, func() {
    36  		var e1, e2 error = errType2{}, errType2{}
    37  		runtime.KeepAlive(e1 == e2)
    38  	})
    39  
    40  	require.False(t, IsEqual(nil, errType1{}))
    41  	require.False(t, IsEqual(errType1{}, nil))
    42  
    43  	require.True(t, IsEqual(errType1{}, errType1{}))
    44  	require.False(t, IsEqual(errType1{"A"}, errType1{"B"}))
    45  
    46  	require.False(t, IsEqual(errType2{}, errType2{}))
    47  
    48  	require.False(t, IsEqual(errType1{}, errType2{}))
    49  	require.False(t, IsEqual(errType2{}, errType1{}))
    50  }
    51  
    52  type errBuilder struct {
    53  	bottomErr error
    54  }
    55  
    56  func (v errBuilder) _err0() error {
    57  	err := WithDetails(v.bottomErr, Unsupported())
    58  	return err
    59  }
    60  
    61  func (v errBuilder) _err1() error {
    62  	err := WithStackAndDetails(v._err0(), struct {
    63  		msg string
    64  		v0  int
    65  	}{"err1Txt", 1})
    66  	return err
    67  }
    68  
    69  func (v errBuilder) _err2() error {
    70  	err := WithStack(v._err1())
    71  	return err
    72  }
    73  
    74  func (v errBuilder) _err3() error {
    75  	panic(v._err2())
    76  }
    77  
    78  func (v errBuilder) _err4() (err error) {
    79  	defer func() {
    80  		err = RW(recover(), err, "panicCatch", struct{ position int }{7})
    81  	}()
    82  	return v._err3()
    83  }
    84  
    85  func newChain(bottom error) error {
    86  	return errBuilder{bottom}._err4()
    87  }
    88  
    89  func TestWrapPanicExt(t *testing.T) {
    90  	err := WrapPanicExt("test", 0)
    91  	st := OutermostStack(err).ShallowStackTrace()
    92  	s := st.StackTraceAsText()
    93  	methodName := "github.com/insolar/vanilla/throw.TestWrapPanicExt"
    94  	require.True(t, strings.HasPrefix(st.StackTraceAsText(), methodName), "missing method: %s", s)
    95  }
    96  
    97  func TestStackOf(t *testing.T) {
    98  	errChain := newChain(io.EOF)
    99  	st := DeepestStackTraceOf(errChain)
   100  	require.NotNil(t, st)
   101  	s := ErrorWithStack(errChain)
   102  	methodName := "github.com/insolar/vanilla/throw.TestStackOf"
   103  	require.Contains(t, s, methodName)
   104  }
   105  
   106  func TestWalk(t *testing.T) {
   107  	t.Run("bad input", func(t *testing.T) {
   108  		// Cannot use panics with value, because throw.stackTrace is not comparable
   109  		require.Panics(t, func() {
   110  			Walk(nil, nil)
   111  		})
   112  	})
   113  
   114  	t.Run("empty error chain", func(t *testing.T) {
   115  		require.False(t, Walk(nil, func(err error, holder StackTraceHolder) bool {
   116  			return true
   117  		}))
   118  	})
   119  
   120  	t.Run("simple error", func(t *testing.T) {
   121  		require.True(t, Walk(fmt.Errorf("some error"), func(err error, holder StackTraceHolder) bool {
   122  			return true
   123  		}))
   124  	})
   125  
   126  	t.Run("single error chain", func(t *testing.T) {
   127  		require.True(t, Walk(IllegalValue(), func(err error, holder StackTraceHolder) bool {
   128  			return true
   129  		}))
   130  	})
   131  
   132  	t.Run("double error chain", func(t *testing.T) {
   133  		require.True(t, Walk(W(IllegalValue(), "one layer more", ""), func(err error, holder StackTraceHolder) bool {
   134  			return true
   135  		}))
   136  	})
   137  }
   138  
   139  func TestPrintTo(t *testing.T) {
   140  	t.Run("nil err with stack", func(t *testing.T) {
   141  		b := strings.Builder{}
   142  		PrintTo(nil, true, &b)
   143  		require.Equal(t, "", b.String())
   144  	})
   145  
   146  	t.Run("nil err without stack", func(t *testing.T) {
   147  		b := strings.Builder{}
   148  		PrintTo(nil, false, &b)
   149  		require.Equal(t, "", b.String())
   150  	})
   151  
   152  	t.Run("simple error without stack", func(t *testing.T) {
   153  		b := strings.Builder{}
   154  		PrintTo(fmt.Errorf("simple error"), false, &b)
   155  		require.Equal(t, "simple error\n", b.String())
   156  	})
   157  
   158  	t.Run("simple error with stack", func(t *testing.T) {
   159  		b := strings.Builder{}
   160  		PrintTo(fmt.Errorf("simple error"), false, &b)
   161  		require.Equal(t, "simple error\n", b.String())
   162  	})
   163  
   164  	t.Run("stacked error without stack", func(t *testing.T) {
   165  		b := strings.Builder{}
   166  		PrintTo(IllegalValue(), false, &b)
   167  		require.Equal(t, "illegal value\n", b.String())
   168  	})
   169  
   170  	t.Run("stacked error with stack", func(t *testing.T) {
   171  		b := strings.Builder{}
   172  		PrintTo(IllegalValue(), true, &b)
   173  		_, fileName, fileLine, ok := runtime.Caller(0)
   174  		require.True(t, ok, "failed to get caller")
   175  		require.Equal(t, fmt.Sprintf("illegal value\nStack trace:\ngithub.com/insolar/vanilla/throw.TestPrintTo.func6\n\t%s:%d\n", fileName, fileLine-1), b.String())
   176  	})
   177  }