mirror of https://github.com/fatedier/frp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
3.1 KiB
94 lines
3.1 KiB
package framework |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"regexp" |
|
"runtime/debug" |
|
"time" |
|
|
|
"github.com/onsi/ginkgo" |
|
|
|
e2eginkgowrapper "github.com/fatedier/frp/test/e2e/framework/ginkgowrapper" |
|
) |
|
|
|
func nowStamp() string { |
|
return time.Now().Format(time.StampMilli) |
|
} |
|
|
|
func log(level string, format string, args ...interface{}) { |
|
fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...) |
|
} |
|
|
|
// Logf logs the info. |
|
func Logf(format string, args ...interface{}) { |
|
log("INFO", format, args...) |
|
} |
|
|
|
// Failf logs the fail info, including a stack trace. |
|
func Failf(format string, args ...interface{}) { |
|
FailfWithOffset(1, format, args...) |
|
} |
|
|
|
// FailfWithOffset calls "Fail" and logs the error with a stack trace that starts at "offset" levels above its caller |
|
// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f"). |
|
func FailfWithOffset(offset int, format string, args ...interface{}) { |
|
msg := fmt.Sprintf(format, args...) |
|
skip := offset + 1 |
|
log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip)) |
|
e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip) |
|
} |
|
|
|
// Fail is a replacement for ginkgo.Fail which logs the problem as it occurs |
|
// together with a stack trace and then calls ginkgowrapper.Fail. |
|
func Fail(msg string, callerSkip ...int) { |
|
skip := 1 |
|
if len(callerSkip) > 0 { |
|
skip += callerSkip[0] |
|
} |
|
log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip)) |
|
e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip) |
|
} |
|
|
|
var codeFilterRE = regexp.MustCompile(`/github.com/onsi/ginkgo/`) |
|
|
|
// PrunedStack is a wrapper around debug.Stack() that removes information |
|
// about the current goroutine and optionally skips some of the initial stack entries. |
|
// With skip == 0, the returned stack will start with the caller of PruneStack. |
|
// From the remaining entries it automatically filters out useless ones like |
|
// entries coming from Ginkgo. |
|
// |
|
// This is a modified copy of PruneStack in https://github.com/onsi/ginkgo/blob/f90f37d87fa6b1dd9625e2b1e83c23ffae3de228/internal/codelocation/code_location.go#L25: |
|
// - simplified API and thus renamed (calls debug.Stack() instead of taking a parameter) |
|
// - source code filtering updated to be specific to Kubernetes |
|
// - optimized to use bytes and in-place slice filtering from |
|
// https://github.com/golang/go/wiki/SliceTricks#filter-in-place |
|
func PrunedStack(skip int) []byte { |
|
fullStackTrace := debug.Stack() |
|
stack := bytes.Split(fullStackTrace, []byte("\n")) |
|
// Ensure that the even entries are the method names and the |
|
// the odd entries the source code information. |
|
if len(stack) > 0 && bytes.HasPrefix(stack[0], []byte("goroutine ")) { |
|
// Ignore "goroutine 29 [running]:" line. |
|
stack = stack[1:] |
|
} |
|
// The "+2" is for skipping over: |
|
// - runtime/debug.Stack() |
|
// - PrunedStack() |
|
skip += 2 |
|
if len(stack) > 2*skip { |
|
stack = stack[2*skip:] |
|
} |
|
n := 0 |
|
for i := 0; i < len(stack)/2; i++ { |
|
// We filter out based on the source code file name. |
|
if !codeFilterRE.Match([]byte(stack[i*2+1])) { |
|
stack[n] = stack[i*2] |
|
stack[n+1] = stack[i*2+1] |
|
n += 2 |
|
} |
|
} |
|
stack = stack[:n] |
|
|
|
return bytes.Join(stack, []byte("\n")) |
|
}
|
|
|