Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // +build !solution
- package tparallel
- import "sync"
- type testContext struct {
- mu sync.Mutex
- startParallel chan struct{}
- running int
- numWaiting int
- maxParallel int
- }
- func newTestContext(maxParallel int) *testContext {
- return &testContext{
- startParallel: make(chan struct{}),
- maxParallel: maxParallel,
- running: 1,
- }
- }
- func (c *testContext) waitParallel() {
- c.mu.Lock()
- if c.running < c.maxParallel {
- c.running++
- c.mu.Unlock()
- return
- }
- c.numWaiting++
- c.mu.Unlock()
- <-c.startParallel
- }
- func (c *testContext) release() {
- c.mu.Lock()
- if c.numWaiting == 0 {
- c.running--
- c.mu.Unlock()
- return
- }
- c.numWaiting--
- c.mu.Unlock()
- c.startParallel <- struct {}{}
- }
- type T struct {
- parallel bool
- signal chan struct{}
- barrier chan struct{}
- parent *T
- sub []*T
- ctx *testContext
- }
- func newT(parent *T) *T {
- return &T{signal: make(chan struct{}),
- barrier: make(chan struct{}),
- sub: make([]*T, 0),
- ctx: newTestContext(16),
- parent: parent,
- }
- }
- func (t *T) Parallel() {
- t.parallel = true
- t.parent.sub = append(t.parent.sub, t)
- t.signal <- struct{}{}
- <-t.parent.barrier
- t.ctx.waitParallel()
- }
- func tRunner(t *T, fn func(t *T)) {
- defer func() {
- if len(t.sub) > 0 {
- t.ctx.release()
- close(t.barrier)
- for _, sub := range t.sub {
- <-sub.signal
- }
- if !t.parallel {
- t.ctx.waitParallel()
- }
- } else if t.parallel {
- t.ctx.release()
- }
- t.signal <- struct{}{}
- }()
- fn(t)
- }
- func (t *T) Run(subtest func(t *T)) {
- go tRunner(t, subtest)
- <-t.signal
- }
- func Run(topTests []func(t *T)) {
- t := newT(nil)
- tRunner(t, func(t *T) {
- for _, test := range topTests {
- newT(t).Run(test)
- }
- go func() { <-t.signal }()
- })
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement