code_location.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. package codelocation
  2. import (
  3. "regexp"
  4. "runtime"
  5. "runtime/debug"
  6. "strings"
  7. "github.com/onsi/ginkgo/types"
  8. )
  9. func New(skip int) types.CodeLocation {
  10. _, file, line, _ := runtime.Caller(skip + 1)
  11. stackTrace := PruneStack(string(debug.Stack()), skip+1)
  12. return types.CodeLocation{FileName: file, LineNumber: line, FullStackTrace: stackTrace}
  13. }
  14. // PruneStack removes references to functions that are internal to Ginkgo
  15. // and the Go runtime from a stack string and a certain number of stack entries
  16. // at the beginning of the stack. The stack string has the format
  17. // as returned by runtime/debug.Stack. The leading goroutine information is
  18. // optional and always removed if present. Beware that runtime/debug.Stack
  19. // adds itself as first entry, so typically skip must be >= 1 to remove that
  20. // entry.
  21. func PruneStack(fullStackTrace string, skip int) string {
  22. stack := strings.Split(fullStackTrace, "\n")
  23. // Ensure that the even entries are the method names and the
  24. // the odd entries the source code information.
  25. if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") {
  26. // Ignore "goroutine 29 [running]:" line.
  27. stack = stack[1:]
  28. }
  29. // The "+1" is for skipping over the initial entry, which is
  30. // runtime/debug.Stack() itself.
  31. if len(stack) > 2*(skip+1) {
  32. stack = stack[2*(skip+1):]
  33. }
  34. prunedStack := []string{}
  35. re := regexp.MustCompile(`\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
  36. for i := 0; i < len(stack)/2; i++ {
  37. // We filter out based on the source code file name.
  38. if !re.Match([]byte(stack[i*2+1])) {
  39. prunedStack = append(prunedStack, stack[i*2])
  40. prunedStack = append(prunedStack, stack[i*2+1])
  41. }
  42. }
  43. return strings.Join(prunedStack, "\n")
  44. }