reports.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package reporting
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "runtime"
  6. "strings"
  7. "github.com/smartystreets/goconvey/convey/gotest"
  8. )
  9. ////////////////// ScopeReport ////////////////////
  10. type ScopeReport struct {
  11. Title string
  12. File string
  13. Line int
  14. }
  15. func NewScopeReport(title string) *ScopeReport {
  16. file, line, _ := gotest.ResolveExternalCaller()
  17. self := new(ScopeReport)
  18. self.Title = title
  19. self.File = file
  20. self.Line = line
  21. return self
  22. }
  23. ////////////////// ScopeResult ////////////////////
  24. type ScopeResult struct {
  25. Title string
  26. File string
  27. Line int
  28. Depth int
  29. Assertions []*AssertionResult
  30. Output string
  31. }
  32. func newScopeResult(title string, depth int, file string, line int) *ScopeResult {
  33. self := new(ScopeResult)
  34. self.Title = title
  35. self.Depth = depth
  36. self.File = file
  37. self.Line = line
  38. self.Assertions = []*AssertionResult{}
  39. return self
  40. }
  41. /////////////////// StoryReport /////////////////////
  42. type StoryReport struct {
  43. Test T
  44. Name string
  45. File string
  46. Line int
  47. }
  48. func NewStoryReport(test T) *StoryReport {
  49. file, line, name := gotest.ResolveExternalCaller()
  50. name = removePackagePath(name)
  51. self := new(StoryReport)
  52. self.Test = test
  53. self.Name = name
  54. self.File = file
  55. self.Line = line
  56. return self
  57. }
  58. // name comes in looking like "github.com/smartystreets/goconvey/examples.TestName".
  59. // We only want the stuff after the last '.', which is the name of the test function.
  60. func removePackagePath(name string) string {
  61. parts := strings.Split(name, ".")
  62. return parts[len(parts)-1]
  63. }
  64. /////////////////// FailureView ////////////////////////
  65. // This struct is also declared in github.com/smartystreets/assertions.
  66. // The json struct tags should be equal in both declarations.
  67. type FailureView struct {
  68. Message string `json:"Message"`
  69. Expected string `json:"Expected"`
  70. Actual string `json:"Actual"`
  71. }
  72. ////////////////////AssertionResult //////////////////////
  73. type AssertionResult struct {
  74. File string
  75. Line int
  76. Expected string
  77. Actual string
  78. Failure string
  79. Error interface{}
  80. StackTrace string
  81. Skipped bool
  82. }
  83. func NewFailureReport(failure string) *AssertionResult {
  84. report := new(AssertionResult)
  85. report.File, report.Line = caller()
  86. report.StackTrace = stackTrace()
  87. parseFailure(failure, report)
  88. return report
  89. }
  90. func parseFailure(failure string, report *AssertionResult) {
  91. view := new(FailureView)
  92. err := json.Unmarshal([]byte(failure), view)
  93. if err == nil {
  94. report.Failure = view.Message
  95. report.Expected = view.Expected
  96. report.Actual = view.Actual
  97. } else {
  98. report.Failure = failure
  99. }
  100. }
  101. func NewErrorReport(err interface{}) *AssertionResult {
  102. report := new(AssertionResult)
  103. report.File, report.Line = caller()
  104. report.StackTrace = fullStackTrace()
  105. report.Error = fmt.Sprintf("%v", err)
  106. return report
  107. }
  108. func NewSuccessReport() *AssertionResult {
  109. return new(AssertionResult)
  110. }
  111. func NewSkipReport() *AssertionResult {
  112. report := new(AssertionResult)
  113. report.File, report.Line = caller()
  114. report.StackTrace = fullStackTrace()
  115. report.Skipped = true
  116. return report
  117. }
  118. func caller() (file string, line int) {
  119. file, line, _ = gotest.ResolveExternalCaller()
  120. return
  121. }
  122. func stackTrace() string {
  123. buffer := make([]byte, 1024*64)
  124. n := runtime.Stack(buffer, false)
  125. return removeInternalEntries(string(buffer[:n]))
  126. }
  127. func fullStackTrace() string {
  128. buffer := make([]byte, 1024*64)
  129. n := runtime.Stack(buffer, true)
  130. return removeInternalEntries(string(buffer[:n]))
  131. }
  132. func removeInternalEntries(stack string) string {
  133. lines := strings.Split(stack, newline)
  134. filtered := []string{}
  135. for _, line := range lines {
  136. if !isExternal(line) {
  137. filtered = append(filtered, line)
  138. }
  139. }
  140. return strings.Join(filtered, newline)
  141. }
  142. func isExternal(line string) bool {
  143. for _, p := range internalPackages {
  144. if strings.Contains(line, p) {
  145. return true
  146. }
  147. }
  148. return false
  149. }
  150. // NOTE: any new packages that host goconvey packages will need to be added here!
  151. // An alternative is to scan the goconvey directory and then exclude stuff like
  152. // the examples package but that's nasty too.
  153. var internalPackages = []string{
  154. "goconvey/assertions",
  155. "goconvey/convey",
  156. "goconvey/execution",
  157. "goconvey/gotest",
  158. "goconvey/reporting",
  159. }