trpc.group/trpc-go/trpc-go@v1.0.3/errs/stack_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package errs
    15  
    16  import (
    17  	"fmt"
    18  	"regexp"
    19  	"runtime"
    20  	"strings"
    21  	"testing"
    22  )
    23  
    24  var initpc = caller()
    25  
    26  type X struct{}
    27  
    28  // val returns a Frame pointing to itself.
    29  func (x X) val() frame {
    30  	return caller()
    31  }
    32  
    33  // ptr returns a Frame pointing to itself.
    34  func (x *X) ptr() frame {
    35  	return caller()
    36  }
    37  
    38  func TestFrameFormat(t *testing.T) {
    39  	var tests = []struct {
    40  		frame
    41  		format string
    42  		want   string
    43  	}{{
    44  		initpc,
    45  		"%s",
    46  		"stack_test.go",
    47  	}, {
    48  		initpc,
    49  		"%+s",
    50  		"trpc.group/trpc-go/trpc-go/errs.init\n" +
    51  			"\t.+errs/stack_test.go",
    52  	}, {
    53  		0,
    54  		"%s",
    55  		"unknown",
    56  	}, {
    57  		0,
    58  		"%+s",
    59  		"unknown",
    60  	}, {
    61  		initpc,
    62  		"%d",
    63  		"24",
    64  	}, {
    65  		0,
    66  		"%d",
    67  		"0",
    68  	}, {
    69  		initpc,
    70  		"%n",
    71  		"init",
    72  	}, {
    73  		func() frame {
    74  			var x X
    75  			return x.ptr()
    76  		}(),
    77  		"%n",
    78  		`\(\*X\).ptr`,
    79  	}, {
    80  		func() frame {
    81  			var x X
    82  			return x.val()
    83  		}(),
    84  		"%n",
    85  		"X.val",
    86  	}, {
    87  		0,
    88  		"%n",
    89  		"",
    90  	}, {
    91  		initpc,
    92  		"%v",
    93  		"stack_test.go:24",
    94  	}, {
    95  		initpc,
    96  		"%+v",
    97  		"trpc.group/trpc-go/trpc-go/errs.init\n" +
    98  			"\t.+errs/stack_test.go",
    99  	}, {
   100  		0,
   101  		"%v",
   102  		"unknown:0",
   103  	}}
   104  
   105  	for i, tt := range tests {
   106  		testFormatRegexp(t, i, tt.frame, tt.format, tt.want)
   107  	}
   108  }
   109  
   110  func TestFuncname(t *testing.T) {
   111  	tests := []struct {
   112  		name, want string
   113  	}{
   114  		{"", ""},
   115  		{"runtime.main", "main"},
   116  		{"github.com/pkg/errors.funcname", "funcname"},
   117  		{"funcname", "funcname"},
   118  		{"io.copyBuffer", "copyBuffer"},
   119  		{"main.(*R).Write", "(*R).Write"},
   120  	}
   121  
   122  	for _, tt := range tests {
   123  		got := funcName(tt.name)
   124  		want := tt.want
   125  		if got != want {
   126  			t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got)
   127  		}
   128  	}
   129  }
   130  
   131  func getStackTrace() stackTrace {
   132  	const depth = 8
   133  	var pcs [depth]uintptr
   134  	n := runtime.Callers(1, pcs[:])
   135  	stack := pcs[0:n]
   136  	// convert to errors.StackTrace
   137  	st := make([]frame, len(stack))
   138  	for i := 0; i < len(st); i++ {
   139  		st[i] = frame((stack)[i])
   140  	}
   141  	return st
   142  }
   143  
   144  func TestStackTraceFormat(t *testing.T) {
   145  	tests := []struct {
   146  		stackTrace
   147  		format string
   148  		want   string
   149  	}{{
   150  		nil,
   151  		"%s",
   152  		`\[\]`,
   153  	}, {
   154  		nil,
   155  		"%v",
   156  		`\[\]`,
   157  	}, {
   158  		nil,
   159  		"%+v",
   160  		"",
   161  	}, {
   162  		nil,
   163  		"%#v",
   164  		`\[\]errs\.frame\(nil\)`,
   165  	}, {
   166  		make(stackTrace, 0),
   167  		"%s",
   168  		`\[\]`,
   169  	}, {
   170  		make(stackTrace, 0),
   171  		"%v",
   172  		`\[\]`,
   173  	}, {
   174  		make(stackTrace, 0),
   175  		"%+v",
   176  		"",
   177  	}, {
   178  		make(stackTrace, 0),
   179  		"%#v",
   180  		`\[\]errs\.frame{}`,
   181  	}, {
   182  		getStackTrace()[:2],
   183  		"%s",
   184  		`\[stack_test.go stack_test.go\]`,
   185  	}, {
   186  		getStackTrace()[:2],
   187  		"%v",
   188  		`\[stack_test.go:134 stack_test.go:186\]`,
   189  	}, {
   190  		getStackTrace()[:2],
   191  		"%+v",
   192  		"\n" +
   193  			"trpc.group/trpc-go/trpc-go/errs.getStackTrace\n" +
   194  			"\t.+errs/stack_test.go:134\n" +
   195  			"trpc.group/trpc-go/trpc-go/errs.TestStackTraceFormat\n" +
   196  			"\t.+errs/stack_test.go:190",
   197  	}, {
   198  		getStackTrace()[:2],
   199  		"%#v",
   200  		`\[\]errs\.frame{stack_test.go:134, stack_test.go:198}`,
   201  	}}
   202  
   203  	for i, tt := range tests {
   204  		testFormatRegexp(t, i, tt.stackTrace, tt.format, tt.want)
   205  	}
   206  }
   207  
   208  // a version of runtime.Caller that returns a Frame, not a uintptr.
   209  func caller() frame {
   210  	var pcs [3]uintptr
   211  	n := runtime.Callers(2, pcs[:])
   212  	frames := runtime.CallersFrames(pcs[:n])
   213  	nextFrame, _ := frames.Next()
   214  	return frame(nextFrame.PC)
   215  }
   216  
   217  func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
   218  	t.Helper()
   219  	got := fmt.Sprintf(format, arg)
   220  	gotLines := strings.SplitN(got, "\n", -1)
   221  	wantLines := strings.SplitN(want, "\n", -1)
   222  
   223  	if len(wantLines) > len(gotLines) {
   224  		t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want)
   225  		return
   226  	}
   227  
   228  	for i, w := range wantLines {
   229  		match, err := regexp.MatchString(w, gotLines[i])
   230  		if err != nil {
   231  			t.Fatal(err)
   232  		}
   233  		if !match {
   234  			t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
   235  		}
   236  	}
   237  }