woodpecker/vendor/github.com/franela/goblin/goblin.go
6543 91d37be1da
Update Dependencies (#486)
* github.com/Microsoft/go-winio

* github.com/bradrydzewski/togo

* github.com/containerd/containerd

* github.com/docker/cli

* github.com/docker/docker

* github.com/docker/docker-credential-helpers

* github.com/franela/goblin

* github.com/google/go-github/v39

* github.com/joho/godotenv

* github.com/lib/pq

* github.com/moby/moby

* github.com/prometheus/client_golang

* github.com/tevino/abool

* github.com/woodpecker-ci/togo

* github.com/xanzy/go-gitlab

* github.com/xeipuuv/gojsonschema

* github.com/mattn/go-sqlite3
2021-10-28 12:11:52 +02:00

403 lines
7.4 KiB
Go

package goblin
import (
"flag"
"fmt"
"regexp"
"runtime"
"sync"
"testing"
"time"
)
type Done func(error ...interface{})
type Runnable interface {
run(*G) bool
}
type Itable interface {
run(*G) bool
failed(string, []string)
}
func (g *G) Describe(name string, h func()) {
d := &Describe{name: name, h: h, parent: g.parent}
if d.parent != nil {
d.parent.children = append(d.parent.children, Runnable(d))
}
g.parent = d
h()
g.parent = d.parent
if g.parent == nil && d.hasTests {
g.reporter.Begin()
if d.run(g) {
g.t.Fail()
}
g.reporter.End()
}
}
func (g *G) Timeout(time time.Duration) {
g.timeout = time
g.timer.Reset(time)
}
type Describe struct {
name string
h func()
children []Runnable
befores []func()
afters []func()
afterEach []func()
beforeEach []func()
justBeforeEach []func()
hasTests bool
parent *Describe
}
func (d *Describe) runBeforeEach() {
if d.parent != nil {
d.parent.runBeforeEach()
}
for _, b := range d.beforeEach {
b()
}
}
func (d *Describe) runJustBeforeEach() {
if d.parent != nil {
d.parent.runJustBeforeEach()
}
for _, b := range d.justBeforeEach {
b()
}
}
func (d *Describe) runAfterEach() {
for _, a := range d.afterEach {
a()
}
if d.parent != nil {
d.parent.runAfterEach()
}
}
func (d *Describe) run(g *G) bool {
failed := false
if d.hasTests {
g.reporter.BeginDescribe(d.name)
for _, b := range d.befores {
b()
}
for _, r := range d.children {
if r.run(g) {
failed = true
}
}
for _, a := range d.afters {
a()
}
g.reporter.EndDescribe()
}
return failed
}
type Failure struct {
Stack []string
TestName string
Message string
}
type It struct {
h interface{}
name string
parent *Describe
failure *Failure
failureMu sync.RWMutex
reporter Reporter
isAsync bool
}
func (it *It) run(g *G) bool {
g.currentIt = it
if it.h == nil {
g.reporter.ItIsPending(it.name)
return false
}
runIt(g, it)
failed := false
it.failureMu.RLock()
if it.failure != nil {
failed = true
}
it.failureMu.RUnlock()
if failed {
g.reporter.ItFailed(it.name)
g.reporter.Failure(it.failure)
} else {
g.reporter.ItPassed(it.name)
}
return failed
}
func (it *It) failed(msg string, stack []string) {
it.failureMu.Lock()
defer it.failureMu.Unlock()
it.failure = &Failure{Stack: stack, Message: msg, TestName: it.parent.name + " " + it.name}
}
type Xit struct {
h interface{}
name string
parent *Describe
failure *Failure
reporter Reporter
isAsync bool
}
func (xit *Xit) run(g *G) bool {
g.currentIt = xit
g.reporter.ItIsExcluded(xit.name)
return false
}
func (xit *Xit) failed(msg string, stack []string) {
xit.failure = nil
}
func parseFlags() {
//Flag parsing
flag.Parse()
if *regexParam != "" {
runRegex = regexp.MustCompile(*regexParam)
} else {
runRegex = nil
}
}
var doParseOnce sync.Once
var timeout = flag.Duration("goblin.timeout", 5*time.Second, "Sets default timeouts for all tests")
var isTty = flag.Bool("goblin.tty", true, "Sets the default output format (color / monochrome)")
var regexParam = flag.String("goblin.run", "", "Runs only tests which match the supplied regex")
var runRegex *regexp.Regexp
func Goblin(t *testing.T, arguments ...string) *G {
doParseOnce.Do(func() {
parseFlags()
})
g := &G{t: t, timeout: *timeout}
var fancy TextFancier
if *isTty {
fancy = &TerminalFancier{}
} else {
fancy = &Monochrome{}
}
g.reporter = Reporter(&DetailedReporter{fancy: fancy})
return g
}
func runIt(g *G, it *It) {
g.mutex.Lock()
g.timedOut = false
g.mutex.Unlock()
g.timer = time.NewTimer(g.timeout)
g.shouldContinue = make(chan bool)
if call, ok := it.h.(func()); ok {
// the test is synchronous
go func(c chan bool) {
it.parent.runBeforeEach()
it.parent.runJustBeforeEach()
timeTrack(g, func() { call() })
it.parent.runAfterEach()
c <- true
}(g.shouldContinue)
} else if call, ok := it.h.(func(Done)); ok {
doneCalled := 0
go func(c chan bool) {
it.parent.runBeforeEach()
it.parent.runJustBeforeEach()
timeTrack(g, func() {
call(func(msg ...interface{}) {
if len(msg) > 0 {
g.Fail(msg)
} else {
doneCalled++
if doneCalled > 1 {
g.Fail("Done called multiple times")
}
it.parent.runAfterEach()
c <- true
}
})
})
}(g.shouldContinue)
} else {
panic("Not implemented.")
}
select {
case <-g.shouldContinue:
case <-g.timer.C:
//Set to nil as it shouldn't continue
g.shouldContinue = nil
g.timedOut = true
g.Fail("Test exceeded " + fmt.Sprintf("%s", g.timeout))
}
// Reset timeout value
g.timeout = *timeout
}
type G struct {
t *testing.T
parent *Describe
currentIt Itable
timeout time.Duration
reporter Reporter
timedOut bool
shouldContinue chan bool
mutex sync.Mutex
timer *time.Timer
}
func (g *G) SetReporter(r Reporter) {
g.reporter = r
}
func (g *G) It(name string, h ...interface{}) {
if matchesRegex(name) {
it := &It{name: name, parent: g.parent, reporter: g.reporter}
if g.parent == nil {
panic(fmt.Sprintf("It(\"%s\") block should be written inside Describe() block.", name))
}
notifyParents(g.parent)
if len(h) > 0 {
it.h = h[0]
}
g.parent.children = append(g.parent.children, Runnable(it))
}
}
func (g *G) Xit(name string, h ...interface{}) {
if matchesRegex(name) {
xit := &Xit{name: name, parent: g.parent, reporter: g.reporter}
notifyParents(g.parent)
if len(h) > 0 {
xit.h = h[0]
}
g.parent.children = append(g.parent.children, Runnable(xit))
}
}
func matchesRegex(value string) bool {
if runRegex != nil {
return runRegex.MatchString(value)
}
return true
}
func notifyParents(d *Describe) {
d.hasTests = true
if d.parent != nil {
notifyParents(d.parent)
}
}
func (g *G) Before(h func()) {
g.parent.befores = append(g.parent.befores, h)
}
func (g *G) BeforeEach(h func()) {
g.parent.beforeEach = append(g.parent.beforeEach, h)
}
func (g *G) JustBeforeEach(h func()) {
g.parent.justBeforeEach = append(g.parent.justBeforeEach, h)
}
func (g *G) After(h func()) {
g.parent.afters = append(g.parent.afters, h)
}
func (g *G) AfterEach(h func()) {
g.parent.afterEach = append(g.parent.afterEach, h)
}
func (g *G) Assert(src interface{}) *Assertion {
return &Assertion{src: src, fail: g.Fail}
}
func timeTrack(g *G, call func()) {
t := time.Now()
defer func() {
g.reporter.ItTook(time.Since(t))
}()
call()
}
func (g *G) errorCommon(msg string, fatal bool) {
if g.currentIt == nil {
panic("Asserts should be written inside an It() block.")
}
g.currentIt.failed(msg, ResolveStack(9))
if g.shouldContinue != nil {
g.shouldContinue <- true
}
if fatal {
g.mutex.Lock()
defer g.mutex.Unlock()
if !g.timedOut {
//Stop test function execution
runtime.Goexit()
}
}
}
func (g *G) Fail(error interface{}) {
message := fmt.Sprintf("%v", error)
g.errorCommon(message, true)
}
func (g *G) FailNow() {
g.t.FailNow()
}
func (g *G) Failf(format string, args ...interface{}) {
message := fmt.Sprintf(format, args...)
g.errorCommon(message, true)
}
func (g *G) Fatalf(format string, args ...interface{}) {
message := fmt.Sprintf(format, args...)
g.errorCommon(message, true)
}
func (g *G) Errorf(format string, args ...interface{}) {
message := fmt.Sprintf(format, args...)
g.errorCommon(message, false)
}
func (g *G) Helper() {
g.t.Helper()
}