goblin.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package goblin
  2. import (
  3. "flag"
  4. "fmt"
  5. "regexp"
  6. "runtime"
  7. "sync"
  8. "testing"
  9. "time"
  10. )
  11. type Done func(error ...interface{})
  12. type Runnable interface {
  13. run(*G) bool
  14. }
  15. type Itable interface {
  16. run(*G) bool
  17. failed(string, []string)
  18. }
  19. func (g *G) Describe(name string, h func()) {
  20. d := &Describe{name: name, h: h, parent: g.parent}
  21. if d.parent != nil {
  22. d.parent.children = append(d.parent.children, Runnable(d))
  23. }
  24. g.parent = d
  25. h()
  26. g.parent = d.parent
  27. if g.parent == nil && d.hasTests {
  28. g.reporter.begin()
  29. if d.run(g) {
  30. g.t.Fail()
  31. }
  32. g.reporter.end()
  33. }
  34. }
  35. func (g *G) Timeout(time time.Duration) {
  36. g.timeout = time
  37. g.timer.Reset(time)
  38. }
  39. type Describe struct {
  40. name string
  41. h func()
  42. children []Runnable
  43. befores []func()
  44. afters []func()
  45. afterEach []func()
  46. beforeEach []func()
  47. hasTests bool
  48. parent *Describe
  49. }
  50. func (d *Describe) runBeforeEach() {
  51. if d.parent != nil {
  52. d.parent.runBeforeEach()
  53. }
  54. for _, b := range d.beforeEach {
  55. b()
  56. }
  57. }
  58. func (d *Describe) runAfterEach() {
  59. if d.parent != nil {
  60. d.parent.runAfterEach()
  61. }
  62. for _, a := range d.afterEach {
  63. a()
  64. }
  65. }
  66. func (d *Describe) run(g *G) bool {
  67. failed := false
  68. if d.hasTests {
  69. g.reporter.beginDescribe(d.name)
  70. for _, b := range d.befores {
  71. b()
  72. }
  73. for _, r := range d.children {
  74. if r.run(g) {
  75. failed = true
  76. }
  77. }
  78. for _, a := range d.afters {
  79. a()
  80. }
  81. g.reporter.endDescribe()
  82. }
  83. return failed
  84. }
  85. type Failure struct {
  86. stack []string
  87. testName string
  88. message string
  89. }
  90. type It struct {
  91. h interface{}
  92. name string
  93. parent *Describe
  94. failure *Failure
  95. reporter Reporter
  96. isAsync bool
  97. }
  98. func (it *It) run(g *G) bool {
  99. g.currentIt = it
  100. if it.h == nil {
  101. g.reporter.itIsPending(it.name)
  102. return false
  103. }
  104. //TODO: should handle errors for beforeEach
  105. it.parent.runBeforeEach()
  106. runIt(g, it.h)
  107. it.parent.runAfterEach()
  108. failed := false
  109. if it.failure != nil {
  110. failed = true
  111. }
  112. if failed {
  113. g.reporter.itFailed(it.name)
  114. g.reporter.failure(it.failure)
  115. } else {
  116. g.reporter.itPassed(it.name)
  117. }
  118. return failed
  119. }
  120. func (it *It) failed(msg string, stack []string) {
  121. it.failure = &Failure{stack: stack, message: msg, testName: it.parent.name + " " + it.name}
  122. }
  123. type Xit struct {
  124. h interface{}
  125. name string
  126. parent *Describe
  127. failure *Failure
  128. reporter Reporter
  129. isAsync bool
  130. }
  131. func (xit *Xit) run(g *G) bool {
  132. g.currentIt = xit
  133. g.reporter.itIsExcluded(xit.name)
  134. return false
  135. }
  136. func (xit *Xit) failed(msg string, stack []string) {
  137. xit.failure = nil
  138. }
  139. func parseFlags() {
  140. //Flag parsing
  141. flag.Parse()
  142. if *regexParam != "" {
  143. runRegex = regexp.MustCompile(*regexParam)
  144. } else {
  145. runRegex = nil
  146. }
  147. }
  148. var timeout = flag.Duration("goblin.timeout", 5*time.Second, "Sets default timeouts for all tests")
  149. var isTty = flag.Bool("goblin.tty", true, "Sets the default output format (color / monochrome)")
  150. var regexParam = flag.String("goblin.run", "", "Runs only tests which match the supplied regex")
  151. var runRegex *regexp.Regexp
  152. func Goblin(t *testing.T, arguments ...string) *G {
  153. if !flag.Parsed() {
  154. parseFlags()
  155. }
  156. g := &G{t: t, timeout: *timeout}
  157. var fancy TextFancier
  158. if *isTty {
  159. fancy = &TerminalFancier{}
  160. } else {
  161. fancy = &Monochrome{}
  162. }
  163. g.reporter = Reporter(&DetailedReporter{fancy: fancy})
  164. return g
  165. }
  166. func runIt(g *G, h interface{}) {
  167. defer timeTrack(time.Now(), g)
  168. g.mutex.Lock()
  169. g.timedOut = false
  170. g.mutex.Unlock()
  171. g.timer = time.NewTimer(g.timeout)
  172. g.shouldContinue = make(chan bool)
  173. if call, ok := h.(func()); ok {
  174. // the test is synchronous
  175. go func(c chan bool) { call(); c <- true }(g.shouldContinue)
  176. } else if call, ok := h.(func(Done)); ok {
  177. doneCalled := 0
  178. go func(c chan bool) {
  179. call(func(msg ...interface{}) {
  180. if len(msg) > 0 {
  181. g.Fail(msg)
  182. } else {
  183. doneCalled++
  184. if doneCalled > 1 {
  185. g.Fail("Done called multiple times")
  186. }
  187. c <- true
  188. }
  189. })
  190. }(g.shouldContinue)
  191. } else {
  192. panic("Not implemented.")
  193. }
  194. select {
  195. case <-g.shouldContinue:
  196. case <-g.timer.C:
  197. //Set to nil as it shouldn't continue
  198. g.shouldContinue = nil
  199. g.timedOut = true
  200. g.Fail("Test exceeded " + fmt.Sprintf("%s", g.timeout))
  201. }
  202. // Reset timeout value
  203. g.timeout = *timeout
  204. }
  205. type G struct {
  206. t *testing.T
  207. parent *Describe
  208. currentIt Itable
  209. timeout time.Duration
  210. reporter Reporter
  211. timedOut bool
  212. shouldContinue chan bool
  213. mutex sync.Mutex
  214. timer *time.Timer
  215. }
  216. func (g *G) SetReporter(r Reporter) {
  217. g.reporter = r
  218. }
  219. func (g *G) It(name string, h ...interface{}) {
  220. if matchesRegex(name) {
  221. it := &It{name: name, parent: g.parent, reporter: g.reporter}
  222. notifyParents(g.parent)
  223. if len(h) > 0 {
  224. it.h = h[0]
  225. }
  226. g.parent.children = append(g.parent.children, Runnable(it))
  227. }
  228. }
  229. func (g *G) Xit(name string, h ...interface{}) {
  230. if matchesRegex(name) {
  231. xit := &Xit{name: name, parent: g.parent, reporter: g.reporter}
  232. notifyParents(g.parent)
  233. if len(h) > 0 {
  234. xit.h = h[0]
  235. }
  236. g.parent.children = append(g.parent.children, Runnable(xit))
  237. }
  238. }
  239. func matchesRegex(value string) bool {
  240. if runRegex != nil {
  241. return runRegex.MatchString(value)
  242. }
  243. return true
  244. }
  245. func notifyParents(d *Describe) {
  246. d.hasTests = true
  247. if d.parent != nil {
  248. notifyParents(d.parent)
  249. }
  250. }
  251. func (g *G) Before(h func()) {
  252. g.parent.befores = append(g.parent.befores, h)
  253. }
  254. func (g *G) BeforeEach(h func()) {
  255. g.parent.beforeEach = append(g.parent.beforeEach, h)
  256. }
  257. func (g *G) After(h func()) {
  258. g.parent.afters = append(g.parent.afters, h)
  259. }
  260. func (g *G) AfterEach(h func()) {
  261. g.parent.afterEach = append(g.parent.afterEach, h)
  262. }
  263. func (g *G) Assert(src interface{}) *Assertion {
  264. return &Assertion{src: src, fail: g.Fail}
  265. }
  266. func timeTrack(start time.Time, g *G) {
  267. g.reporter.itTook(time.Since(start))
  268. }
  269. func (g *G) Fail(error interface{}) {
  270. //Skips 7 stacks due to the functions between the stack and the test
  271. stack := ResolveStack(7)
  272. message := fmt.Sprintf("%v", error)
  273. g.currentIt.failed(message, stack)
  274. if g.shouldContinue != nil {
  275. g.shouldContinue <- true
  276. }
  277. g.mutex.Lock()
  278. defer g.mutex.Unlock()
  279. if !g.timedOut {
  280. //Stop test function execution
  281. runtime.Goexit()
  282. }
  283. }