github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/infra/err_stack_test.go (about)

     1  package infra
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  	"runtime"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/zap"
    13  )
    14  
    15  var initPC = caller()
    16  
    17  func caller() frame {
    18  	var PCs [3]uintptr
    19  	n := runtime.Callers(2, PCs[:])
    20  	frames := runtime.CallersFrames(PCs[:n])
    21  	fr, _ := frames.Next()
    22  	return frame(fr.PC)
    23  }
    24  
    25  func TestFrameFormat(t *testing.T) {
    26  	testcases := []struct {
    27  		frame
    28  		format       string
    29  		expectedExpr string
    30  	}{
    31  		{
    32  			initPC,
    33  			"%s",
    34  			"^err_stack_test.go$",
    35  		},
    36  		{
    37  			initPC,
    38  			"%+s",
    39  			"^github.com/benz9527/xboot/lib/infra.init\\n\\t.*xboot/lib/infra/err_stack_test.go$",
    40  		},
    41  		{
    42  			initPC,
    43  			"%n",
    44  			"^init$",
    45  		},
    46  		{
    47  			initPC,
    48  			"%d",
    49  			"\\d+",
    50  		},
    51  		{
    52  			initPC,
    53  			"%v",
    54  			"^err_stack_test.go:\\d+$",
    55  		},
    56  		{
    57  			initPC,
    58  			"%+v",
    59  			"^github.com/benz9527/xboot/lib/infra.init\\n\\t.*xboot/lib/infra/err_stack_test.go:\\d+$",
    60  		},
    61  		{
    62  			frame(0),
    63  			"%s",
    64  			"unknownFile",
    65  		},
    66  		{
    67  			frame(0),
    68  			"%n",
    69  			"unknownFunc",
    70  		},
    71  		{
    72  			frame(0),
    73  			"%d",
    74  			"0",
    75  		},
    76  	}
    77  
    78  	for _, tc := range testcases {
    79  		expr := regexp.MustCompile(tc.expectedExpr)
    80  		require.NotNil(t, expr)
    81  		frameRes := fmt.Sprintf(tc.format, tc.frame)
    82  		require.True(t, expr.MatchString(frameRes))
    83  	}
    84  }
    85  
    86  func TestFrameMarshalText(t *testing.T) {
    87  	testcases := []struct {
    88  		frame
    89  		expectedExpr string
    90  	}{
    91  		{
    92  			initPC,
    93  			"^github.com/benz9527/xboot/lib/infra.init .*xboot/lib/infra/err_stack_test.go:\\d+$",
    94  		},
    95  		{
    96  			frame(0),
    97  			"unknownFrame",
    98  		},
    99  	}
   100  	for _, tc := range testcases {
   101  		_bytes, err := tc.frame.MarshalText()
   102  		require.NoError(t, err)
   103  		require.Greater(t, len(_bytes), 0)
   104  		expr := regexp.MustCompile(tc.expectedExpr)
   105  		require.True(t, expr.MatchString(string(_bytes)))
   106  	}
   107  }
   108  
   109  func TestFrameMarshalJSON(t *testing.T) {
   110  	testcases := []struct {
   111  		frame
   112  		expectedExpr string
   113  	}{
   114  		{
   115  			initPC,
   116  			"^{\"func\":\"github\\.com/benz9527/xboot/lib/infra\\.init\",\"fileAndLine\":\".*xboot/lib/infra/err_stack_test\\.go:\\d+\"}$",
   117  		},
   118  		{
   119  			frame(0),
   120  			"{\"frame\":\"unknownFrame\"}",
   121  		},
   122  	}
   123  	for _, tc := range testcases {
   124  		_bytes, err := json.Marshal(tc.frame)
   125  		require.NoError(t, err)
   126  		require.Greater(t, len(_bytes), 0)
   127  		expr := regexp.MustCompile(tc.expectedExpr)
   128  		require.NotNil(t, expr)
   129  		require.True(t, expr.MatchString(string(_bytes)))
   130  	}
   131  }
   132  
   133  func TestStackTraceFormat(t *testing.T) {
   134  	testcases := []struct {
   135  		stackTrace
   136  		format       string
   137  		expectedExpr string
   138  	}{
   139  		{
   140  			[]frame{},
   141  			"%s",
   142  			"\\[\\]",
   143  		},
   144  		{
   145  			nil,
   146  			"%s",
   147  			"\\[\\]",
   148  		},
   149  		{
   150  			nil,
   151  			"%+v",
   152  			"nil",
   153  		},
   154  		{
   155  			nil,
   156  			"%#v",
   157  			"infra.stackTrace\\(nil\\)",
   158  		},
   159  		{
   160  			testStackTrace()[:4],
   161  			"%v",
   162  			"\\[(.*\\.(go|s):\\d+ ?)+\\]",
   163  		},
   164  		{
   165  			testStackTrace()[:4],
   166  			"%#v",
   167  			"infra.stackTrace\\(\\[(.*\\.(go|s):\\d+,?)+\\]\\)",
   168  		},
   169  		{
   170  			testStackTrace()[:2],
   171  			"%+v",
   172  			`(github\.com/benz9527/xboot/lib/infra\..*\n\t.*\.go:\d+\n?){2}`,
   173  		},
   174  		{
   175  			testStackTrace()[:2],
   176  			"%+s",
   177  			`(github\.com/benz9527/xboot/lib/infra\..*\n\t.*\.go\n?){2}`,
   178  		},
   179  	}
   180  	for _, tc := range testcases {
   181  		expr := regexp.MustCompile(tc.expectedExpr)
   182  		require.NotNil(t, expr)
   183  		st := fmt.Sprintf(tc.format, tc.stackTrace)
   184  		require.True(t, expr.MatchString(st))
   185  	}
   186  }
   187  
   188  func testStackTrace() stackTrace {
   189  	pcs := make([]uintptr, 8)
   190  	n := runtime.Callers(1, pcs[:])
   191  	var st stack = pcs[0:n]
   192  	return st.StackTrace()
   193  }
   194  
   195  func TestStackTraceMarshalText(t *testing.T) {
   196  	testcases := []struct {
   197  		stackTrace
   198  		expectedExpr string
   199  	}{
   200  		{
   201  			[]frame{},
   202  			"",
   203  		},
   204  		{
   205  			nil,
   206  			"",
   207  		},
   208  		{
   209  			testStackTrace()[:2],
   210  			`(github\.com/benz9527/xboot/lib/infra\..* .*\.go:\d+\n?){2}`,
   211  		},
   212  	}
   213  	for _, tc := range testcases {
   214  		expr := regexp.MustCompile(tc.expectedExpr)
   215  		require.NotNil(t, expr)
   216  		st, err := tc.stackTrace.MarshalText()
   217  		require.NoError(t, err)
   218  		require.GreaterOrEqual(t, len(st), 0)
   219  		require.True(t, expr.MatchString(string(st)))
   220  	}
   221  }
   222  
   223  func TestStackTraceMarshalJSON(t *testing.T) {
   224  	testcases := []struct {
   225  		stackTrace
   226  		expectedExpr string
   227  	}{
   228  		{
   229  			[]frame{},
   230  			"\\[\\]",
   231  		},
   232  		{
   233  			nil,
   234  			"\\[\\]",
   235  		},
   236  		{
   237  			testStackTrace()[:2],
   238  			"\\[({\"func\":\"github\\.com/benz9527/xboot/lib/infra\\..*\",\"fileAndLine\":\".*/lib/infra/.*\\.go:\\d+\"},?)+\\]",
   239  		},
   240  		{
   241  			testStackTrace()[:2],
   242  			"\\[[^]]+\\]",
   243  		},
   244  	}
   245  	for _, tc := range testcases {
   246  		expr := regexp.MustCompile(tc.expectedExpr)
   247  		require.NotNil(t, expr)
   248  		st, err := json.Marshal(tc.stackTrace)
   249  		require.NoError(t, err)
   250  		require.GreaterOrEqual(t, len(st), 0)
   251  		require.True(t, expr.MatchString(string(st)))
   252  	}
   253  }
   254  
   255  func TestStackFormat(t *testing.T) {
   256  	c1 := getCallers(32)
   257  	testcases := []struct {
   258  		stack
   259  		format       string
   260  		expectedExpr string
   261  	}{
   262  		{
   263  			[]uintptr{},
   264  			"%+v",
   265  			"stack:\nnil",
   266  		},
   267  		{
   268  			nil,
   269  			"%+v",
   270  			"stack:\nnil",
   271  		},
   272  		{
   273  			getCallers(32),
   274  			"%+v",
   275  			`stack:\n(.*\..*\n\t.*\.(go|s):\d+)+`,
   276  		},
   277  		{
   278  			c1,
   279  			"%+v",
   280  			`stack:\n(.*\..*\n\t.*\.(go|s):\d+)+`,
   281  		},
   282  		{
   283  			nil,
   284  			"%#v",
   285  			"infra\\.stack\\(infra\\.stackTrace\\(nil\\)\\)",
   286  		},
   287  		{
   288  			getCallers(32),
   289  			"%#v",
   290  			`infra\.stack\(infra\.stackTrace\(\[(.*\.(go|s):\d+,?)+\]\)\)`,
   291  		},
   292  		{
   293  			c1,
   294  			"%#v",
   295  			`infra\.stack\(infra\.stackTrace\(\[(.*\.(go|s):\d+,?)+\]\)\)`,
   296  		},
   297  	}
   298  	for _, tc := range testcases {
   299  		expr := regexp.MustCompile(tc.expectedExpr)
   300  		require.NotNil(t, expr)
   301  		s := fmt.Sprintf(tc.format, tc.stack)
   302  		require.True(t, expr.MatchString(s))
   303  	}
   304  }
   305  
   306  func TestErrorStackMarshalJSON(t *testing.T) {
   307  	_errors := []error{
   308  		fmt.Errorf("test"),
   309  		fmt.Errorf("test2"),
   310  	}
   311  	es := AppendErrorStack(nil, _errors...)
   312  	_bytes, err := json.Marshal(es)
   313  	require.NoError(t, err)
   314  	t.Log(string(_bytes))
   315  
   316  	t.Logf("%v\n", es)
   317  	t.Logf("%s\n", es)
   318  	t.Logf("%+v\n", es)
   319  	t.Logf("%#v\n", es)
   320  
   321  	t.Log("=== 1 ===")
   322  	es = AppendErrorStack(es, errors.New("test3"))
   323  	_bytes, err = json.Marshal(es)
   324  	require.NoError(t, err)
   325  	t.Log(string(_bytes))
   326  
   327  	t.Logf("%v\n", es)
   328  	t.Logf("%s\n", es)
   329  	t.Logf("%+v\n", es)
   330  	t.Logf("%#v\n", es)
   331  
   332  	t.Log("=== 2 ===")
   333  	es = &errorStack{}
   334  	_bytes, err = json.Marshal(es)
   335  	require.NoError(t, err)
   336  	t.Log(string(_bytes))
   337  
   338  	t.Logf("%v\n", es)
   339  	t.Logf("%s\n", es)
   340  	t.Logf("%+v\n", es)
   341  	t.Logf("%#v\n", es)
   342  
   343  	t.Log("=== 3 ===")
   344  	es = AppendErrorStack(es, errors.New("test4"))
   345  	_bytes, err = json.Marshal(es)
   346  	require.NoError(t, err)
   347  	t.Log(string(_bytes))
   348  
   349  	t.Logf("%v\n", es)
   350  	t.Logf("%s\n", es)
   351  	t.Logf("%+v\n", es)
   352  	t.Logf("%#v\n", es)
   353  
   354  	t.Log("=== 4 ===")
   355  	es = AppendErrorStack(es, errors.New("test5"))
   356  	_bytes, err = json.Marshal(es)
   357  	require.NoError(t, err)
   358  	t.Log(string(_bytes))
   359  
   360  	t.Logf("%v\n", es)
   361  	t.Logf("%s\n", es)
   362  	t.Logf("%+v\n", es)
   363  	t.Logf("%#v\n", es)
   364  
   365  	t.Log("=== 5 ===")
   366  	es = &errorStack{}
   367  	es = WrapErrorStackWithMessage(es, "test6")
   368  	_bytes, err = json.Marshal(es)
   369  	require.NoError(t, err)
   370  	t.Log(string(_bytes))
   371  
   372  	t.Logf("%v\n", es)
   373  	t.Logf("%s\n", es)
   374  	t.Logf("%+v\n", es)
   375  	t.Logf("%#v\n", es)
   376  
   377  	t.Log("=== 6 ===")
   378  	es = WrapErrorStackWithMessage(nil, "test7")
   379  	_bytes, err = json.Marshal(es)
   380  	require.NoError(t, err)
   381  	t.Log(string(_bytes))
   382  
   383  	t.Logf("%v\n", es)
   384  	t.Logf("%s\n", es)
   385  	t.Logf("%+v\n", es)
   386  	t.Logf("%#v\n", es)
   387  
   388  	t.Log("=== 7 ===")
   389  	es = AppendErrorStack(nil, errors.New("test8"))
   390  	_bytes, err = json.Marshal(es)
   391  	require.NoError(t, err)
   392  	t.Log(string(_bytes))
   393  
   394  	t.Logf("%v\n", es)
   395  	t.Logf("%s\n", es)
   396  	t.Logf("%+v\n", es)
   397  	t.Logf("%#v\n", es)
   398  
   399  	l, err := zap.NewDevelopment()
   400  	require.NoError(t, err)
   401  
   402  	var _es *errorStack = nil
   403  	l.Error("test zap nil err", zap.Inline(_es))
   404  
   405  	es = WrapErrorStackWithMessage(nil, "")
   406  	require.Nil(t, es)
   407  
   408  	es = AppendErrorStack(nil)
   409  	require.Nil(t, es)
   410  
   411  	es = WrapErrorStack(nil)
   412  	require.Nil(t, es)
   413  
   414  	es = WrapErrorStack(errors.New("test9"))
   415  	require.NotNil(t, es)
   416  
   417  	es = AppendErrorStack(es, errors.New("test10"))
   418  	require.NotNil(t, es)
   419  
   420  	es = WrapErrorStackWithMessage(es, "test11")
   421  	require.NotNil(t, es)
   422  
   423  	es = AppendErrorStack(errors.New("test12"), errors.New("test13"))
   424  	require.NotNil(t, es)
   425  	l.Info("test zap dev logger", zap.Inline(es.(*errorStack)))
   426  
   427  	es = WrapErrorStackWithMessage(errors.New("test14"), "test15")
   428  	require.NotNil(t, es)
   429  
   430  	es = WrapErrorStack(es)
   431  	require.NotNil(t, es)
   432  
   433  	es = NewErrorStack("")
   434  	require.Nil(t, es)
   435  
   436  	es = NewErrorStack("test16")
   437  	require.NotNil(t, es)
   438  }
   439  
   440  type testStrError string
   441  
   442  func (e testStrError) Error() string {
   443  	return string(e)
   444  }
   445  
   446  func TestErrorStack_StrError(t *testing.T) {
   447  	const (
   448  		stErr1 = testStrError("test1-error")
   449  		stErr2 = testStrError("test2-error")
   450  		stErr3 = testStrError("test3-error")
   451  	)
   452  	es := WrapErrorStack(stErr1)
   453  	require.True(t, errors.Is(es, stErr1))
   454  
   455  	_bytes, err := json.Marshal(es)
   456  	require.NoError(t, err)
   457  	t.Log(string(_bytes))
   458  
   459  	t.Logf("%v\n", es)
   460  	t.Logf("%s\n", es)
   461  	t.Logf("%+v\n", es)
   462  	t.Logf("%#v\n", es)
   463  
   464  	merr := AppendErrorStack(es, stErr2, stErr3)
   465  	require.True(t, errors.Is(merr, stErr2))
   466  }