Replace godep with dep

This commit is contained in:
Manuel de Brito Fontes 2017-10-06 17:26:14 -03:00
parent 1e7489927c
commit bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions

View file

@ -0,0 +1,172 @@
package pool
import (
"sync"
"testing"
"time"
. "gopkg.in/go-playground/assert.v1"
)
// NOTES:
// - Run "go test" to run tests
// - Run "gocov test | gocov report" to report on test converage by file
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
//
// or
//
// -- may be a good idea to change to output path to somewherelike /tmp
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
//
func TestLimitedBatch(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := NewLimited(4)
defer pool.Close()
batch := pool.Batch()
for i := 0; i < 4; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 4)
}
func TestLimitedBatchGlobalPool(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
batch := limitedGpool.Batch()
for i := 0; i < 4; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 4)
}
func TestLimitedBatchCancelItemsThrownAway(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := NewLimited(4)
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 40; i++ {
batch.Queue(newFunc(i))
}
}()
batch.Cancel()
var count int
for range batch.Results() {
count++
}
NotEqual(t, count, 40)
}
func TestLimitedBatchCancelItemsCancelledAfterward(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := NewLimited(4)
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 40; i++ {
batch.Queue(newFunc(i))
}
}()
time.Sleep(time.Second * 2)
batch.Cancel()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 40)
}
func TestLimitedBatchWaitAll(t *testing.T) {
var count int
var m sync.Mutex
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
m.Lock()
count++
m.Unlock()
return i, nil
}
}
pool := NewLimited(4)
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 10; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
}()
batch.WaitAll()
Equal(t, count, 10)
}

View file

@ -0,0 +1,172 @@
package pool
import (
"sync"
"testing"
"time"
. "gopkg.in/go-playground/assert.v1"
)
// NOTES:
// - Run "go test" to run tests
// - Run "gocov test | gocov report" to report on test converage by file
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
//
// or
//
// -- may be a good idea to change to output path to somewherelike /tmp
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
//
func TestUnlimitedBatch(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := New()
defer pool.Close()
batch := pool.Batch()
for i := 0; i < 4; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 4)
}
func TestUnlimitedBatchGlobalPool(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
batch := unlimitedGpool.Batch()
for i := 0; i < 4; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 4)
}
func TestUnlimitedBatchCancelItemsThrownAway(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := New()
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 40; i++ {
batch.Queue(newFunc(i))
}
}()
batch.Cancel()
var count int
for range batch.Results() {
count++
}
NotEqual(t, count, 40)
}
func TestUnlimitedBatchCancelItemsCancelledAfterward(t *testing.T) {
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
return i, nil
}
}
pool := New()
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 40; i++ {
batch.Queue(newFunc(i))
}
}()
time.Sleep(time.Second * 2)
batch.Cancel()
var count int
for range batch.Results() {
count++
}
Equal(t, count, 40)
}
func TestUnlimitedBatchWaitAll(t *testing.T) {
var count int
var m sync.Mutex
newFunc := func(i int) func(WorkUnit) (interface{}, error) {
return func(WorkUnit) (interface{}, error) {
time.Sleep(time.Second * 1)
m.Lock()
count++
m.Unlock()
return i, nil
}
}
pool := New()
defer pool.Close()
batch := pool.Batch()
go func() {
for i := 0; i < 10; i++ {
batch.Queue(newFunc(i))
}
batch.QueueComplete()
}()
batch.WaitAll()
Equal(t, count, 10)
}

View file

@ -0,0 +1,66 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
var gpool = pool.NewLimited(5)
func main() {
// OK so maybe you want a long running pool to maximize throughput
// yet limit the # of workers eg. email provider may limit the # of
// concurrent connection you can have so spin up a pool with the #
// of workers being that limit and then can batch
// (or send per unit if desired) then can maximize email sending throughput
// without breaking your providers limits.
batch := gpool.Batch()
// for max speed Queue in another goroutine
// but it is not required, just can't start reading results
// until all items are Queued.
go func() {
for i := 0; i < 10; i++ {
batch.Queue(sendEmail("email content"))
}
// DO NOT FORGET THIS OR GOROUTINES WILL DEADLOCK
// if calling Cancel() it calles QueueComplete() internally
batch.QueueComplete()
}()
for email := range batch.Results() {
if err := email.Error(); err != nil {
// handle error
// maybe call batch.Cancel()
}
// use return value
fmt.Println(email.Value().(bool))
}
}
func sendEmail(email string) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return true, nil // everything ok, send nil, error if not
}
}

View file

@ -0,0 +1,59 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
func main() {
p := pool.NewLimited(10)
defer p.Close()
batch := p.Batch()
// for max speed Queue in another goroutine
// but it is not required, just can't start reading results
// until all items are Queued.
go func() {
for i := 0; i < 10; i++ {
batch.Queue(sendEmail("email content"))
}
// DO NOT FORGET THIS OR GOROUTINES WILL DEADLOCK
// if calling Cancel() it calles QueueComplete() internally
batch.QueueComplete()
}()
for email := range batch.Results() {
if err := email.Error(); err != nil {
// handle error
// maybe call batch.Cancel()
}
// use return value
fmt.Println(email.Value().(bool))
}
}
func sendEmail(email string) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return true, nil // everything ok, send nil, error if not
}
}

View file

@ -0,0 +1,73 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
func main() {
p := pool.NewLimited(10)
defer p.Close()
user := p.Queue(getUser(13))
other := p.Queue(getOtherInfo(13))
user.Wait()
if err := user.Error(); err != nil {
// handle error
}
// do stuff with user
username := user.Value().(string)
fmt.Println(username)
other.Wait()
if err := other.Error(); err != nil {
// handle error
}
// do stuff with other
otherInfo := other.Value().(string)
fmt.Println(otherInfo)
}
func getUser(id int) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return "Joeybloggs", nil
}
}
func getOtherInfo(id int) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return "Other Info", nil
}
}

View file

@ -0,0 +1,66 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
var gpool = pool.New()
func main() {
// OK so maybe you want a long running pool to maximize throughput
// yet limit the # of workers eg. email provider may limit the # of
// concurrent connection you can have so spin up a pool with the #
// of workers being that limit and then can batch
// (or send per unit if desired) then can maximize email sending throughput
// without breaking your providers limits.
batch := gpool.Batch()
// for max speed Queue in another goroutine
// but it is not required, just can't start reading results
// until all items are Queued.
go func() {
for i := 0; i < 10; i++ {
batch.Queue(sendEmail("email content"))
}
// DO NOT FORGET THIS OR GOROUTINES WILL DEADLOCK
// if calling Cancel() it calles QueueComplete() internally
batch.QueueComplete()
}()
for email := range batch.Results() {
if err := email.Error(); err != nil {
// handle error
// maybe call batch.Cancel()
}
// use return value
fmt.Println(email.Value().(bool))
}
}
func sendEmail(email string) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return true, nil // everything ok, send nil, error if not
}
}

View file

@ -0,0 +1,59 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
func main() {
p := pool.New()
defer p.Close()
batch := p.Batch()
// for max speed Queue in another goroutine
// but it is not required, just can't start reading results
// until all items are Queued.
go func() {
for i := 0; i < 10; i++ {
batch.Queue(sendEmail("email content"))
}
// DO NOT FORGET THIS OR GOROUTINES WILL DEADLOCK
// if calling Cancel() it calles QueueComplete() internally
batch.QueueComplete()
}()
for email := range batch.Results() {
if err := email.Error(); err != nil {
// handle error
// maybe call batch.Cancel()
}
// use return value
fmt.Println(email.Value().(bool))
}
}
func sendEmail(email string) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return true, nil // everything ok, send nil, error if not
}
}

View file

@ -0,0 +1,73 @@
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/pool.v3"
)
func main() {
p := pool.New()
defer p.Close()
user := p.Queue(getUser(13))
other := p.Queue(getOtherInfo(13))
user.Wait()
if err := user.Error(); err != nil {
// handle error
}
// do stuff with user
username := user.Value().(string)
fmt.Println(username)
other.Wait()
if err := other.Error(); err != nil {
// handle error
}
// do stuff with other
otherInfo := other.Value().(string)
fmt.Println(otherInfo)
}
func getUser(id int) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return "Joeybloggs", nil
}
}
func getOtherInfo(id int) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
// simulate waiting for something, like TCP connection to be established
// or connection from pool grabbed
time.Sleep(time.Second * 1)
if wu.IsCancelled() {
// return values not used
return nil, nil
}
// ready for processing...
return "Other Info", nil
}
}

View file

@ -0,0 +1,185 @@
package pool
import (
"testing"
"time"
)
func BenchmarkLimitedSmallRun(b *testing.B) {
res := make([]WorkUnit, 10)
b.ReportAllocs()
pool := NewLimited(10)
defer pool.Close()
fn := func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
for i := 0; i < 10; i++ {
res[i] = pool.Queue(fn)
}
var count int
for _, cw := range res {
cw.Wait()
if cw.Error() == nil {
count += cw.Value().(int)
}
}
if count != 10 {
b.Fatal("Count Incorrect")
}
}
func BenchmarkLimitedSmallCancel(b *testing.B) {
res := make([]WorkUnit, 0, 20)
b.ReportAllocs()
pool := NewLimited(4)
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return i, nil
}
}
for i := 0; i < 20; i++ {
if i == 6 {
pool.Cancel()
}
res = append(res, pool.Queue(newFunc(i)))
}
for _, wrk := range res {
if wrk == nil {
continue
}
wrk.Wait()
}
}
func BenchmarkLimitedLargeCancel(b *testing.B) {
res := make([]WorkUnit, 0, 1000)
b.ReportAllocs()
pool := NewLimited(4)
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return i, nil
}
}
for i := 0; i < 1000; i++ {
if i == 6 {
pool.Cancel()
}
res = append(res, pool.Queue(newFunc(i)))
}
for _, wrk := range res {
if wrk == nil {
continue
}
wrk.Wait()
}
}
func BenchmarkLimitedOverconsumeLargeRun(b *testing.B) {
res := make([]WorkUnit, 100)
b.ReportAllocs()
pool := NewLimited(25)
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
}
for i := 0; i < 100; i++ {
res[i] = pool.Queue(newFunc(i))
}
var count int
for _, cw := range res {
cw.Wait()
count += cw.Value().(int)
}
if count != 100 {
b.Fatalf("Count Incorrect, Expected '100' Got '%d'", count)
}
}
func BenchmarkLimitedBatchSmallRun(b *testing.B) {
fn := func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
pool := NewLimited(10)
defer pool.Close()
batch := pool.Batch()
for i := 0; i < 10; i++ {
batch.Queue(fn)
}
batch.QueueComplete()
var count int
for cw := range batch.Results() {
count += cw.Value().(int)
}
if count != 10 {
b.Fatal("Count Incorrect")
}
}

View file

@ -0,0 +1,177 @@
package pool
import (
"sync"
"testing"
"time"
. "gopkg.in/go-playground/assert.v1"
)
// NOTES:
// - Run "go test" to run tests
// - Run "gocov test | gocov report" to report on test converage by file
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
//
// or
//
// -- may be a good idea to change to output path to somewherelike /tmp
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
//
func TestPool(t *testing.T) {
var res []WorkUnit
pool := NewLimited(4)
defer pool.Close()
newFunc := func(d time.Duration) WorkFunc {
return func(WorkUnit) (interface{}, error) {
time.Sleep(d)
return nil, nil
}
}
for i := 0; i < 4; i++ {
wu := pool.Queue(newFunc(time.Second * 1))
res = append(res, wu)
}
var count int
for _, wu := range res {
wu.Wait()
Equal(t, wu.Error(), nil)
Equal(t, wu.Value(), nil)
count++
}
Equal(t, count, 4)
pool.Close() // testing no error occurs as Close will be called twice once defer pool.Close() fires
}
func TestCancel(t *testing.T) {
m := new(sync.RWMutex)
var closed bool
c := make(chan WorkUnit, 100)
pool := limitedGpool
defer pool.Close()
newFunc := func(d time.Duration) WorkFunc {
return func(WorkUnit) (interface{}, error) {
time.Sleep(d)
return 1, nil
}
}
go func(ch chan WorkUnit) {
for i := 0; i < 40; i++ {
go func(ch chan WorkUnit) {
m.RLock()
if closed {
m.RUnlock()
return
}
ch <- pool.Queue(newFunc(time.Second * 1))
m.RUnlock()
}(ch)
}
}(c)
time.Sleep(time.Second * 1)
pool.Cancel()
m.Lock()
closed = true
close(c)
m.Unlock()
var count int
for wu := range c {
wu.Wait()
if wu.Error() != nil {
_, ok := wu.Error().(*ErrCancelled)
if !ok {
_, ok = wu.Error().(*ErrPoolClosed)
if ok {
Equal(t, wu.Error().Error(), "ERROR: Work Unit added/run after the pool had been closed or cancelled")
}
} else {
Equal(t, wu.Error().Error(), "ERROR: Work Unit Cancelled")
}
Equal(t, ok, true)
continue
}
count += wu.Value().(int)
}
NotEqual(t, count, 40)
// reset and test again
pool.Reset()
wrk := pool.Queue(newFunc(time.Millisecond * 300))
wrk.Wait()
_, ok := wrk.Value().(int)
Equal(t, ok, true)
wrk = pool.Queue(newFunc(time.Millisecond * 300))
time.Sleep(time.Second * 1)
wrk.Cancel()
wrk.Wait() // proving we don't get stuck here after cancel
Equal(t, wrk.Error(), nil)
pool.Reset() // testing that we can do this and nothing bad will happen as it checks if pool closed
pool.Close()
wu := pool.Queue(newFunc(time.Second * 1))
wu.Wait()
NotEqual(t, wu.Error(), nil)
Equal(t, wu.Error().Error(), "ERROR: Work Unit added/run after the pool had been closed or cancelled")
}
func TestPanicRecovery(t *testing.T) {
pool := NewLimited(2)
defer pool.Close()
newFunc := func(d time.Duration, i int) WorkFunc {
return func(WorkUnit) (interface{}, error) {
if i == 1 {
panic("OMG OMG OMG! something bad happened!")
}
time.Sleep(d)
return 1, nil
}
}
var wrk WorkUnit
for i := 0; i < 4; i++ {
time.Sleep(time.Second * 1)
if i == 1 {
wrk = pool.Queue(newFunc(time.Second*1, i))
continue
}
pool.Queue(newFunc(time.Second*1, i))
}
wrk.Wait()
NotEqual(t, wrk.Error(), nil)
Equal(t, wrk.Error().Error()[0:90], "ERROR: Work Unit failed due to a recoverable error: 'OMG OMG OMG! something bad happened!'")
}
func TestBadWorkerCount(t *testing.T) {
PanicMatches(t, func() { NewLimited(0) }, "invalid workers '0'")
}

36
vendor/gopkg.in/go-playground/pool.v3/pool_test.go generated vendored Normal file
View file

@ -0,0 +1,36 @@
package pool
import (
"os"
"testing"
)
// NOTES:
// - Run "go test" to run tests
// - Run "gocov test | gocov report" to report on test converage by file
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
//
// or
//
// -- may be a good idea to change to output path to somewherelike /tmp
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
//
// global pool for testing long running pool
var limitedGpool Pool
var unlimitedGpool Pool
func TestMain(m *testing.M) {
// setup
limitedGpool = NewLimited(4)
defer limitedGpool.Close()
unlimitedGpool = New()
defer unlimitedGpool.Close()
os.Exit(m.Run())
// teardown
}

View file

@ -0,0 +1,185 @@
package pool
import (
"testing"
"time"
)
func BenchmarkUnlimitedSmallRun(b *testing.B) {
res := make([]WorkUnit, 10)
b.ReportAllocs()
pool := New()
defer pool.Close()
fn := func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
for i := 0; i < 10; i++ {
res[i] = pool.Queue(fn)
}
var count int
for _, cw := range res {
cw.Wait()
if cw.Error() == nil {
count += cw.Value().(int)
}
}
if count != 10 {
b.Fatal("Count Incorrect")
}
}
func BenchmarkUnlimitedSmallCancel(b *testing.B) {
res := make([]WorkUnit, 0, 20)
b.ReportAllocs()
pool := New()
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return i, nil
}
}
for i := 0; i < 20; i++ {
if i == 6 {
pool.Cancel()
}
res = append(res, pool.Queue(newFunc(i)))
}
for _, wrk := range res {
if wrk == nil {
continue
}
wrk.Wait()
}
}
func BenchmarkUnlimitedLargeCancel(b *testing.B) {
res := make([]WorkUnit, 0, 1000)
b.ReportAllocs()
pool := New()
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return i, nil
}
}
for i := 0; i < 1000; i++ {
if i == 6 {
pool.Cancel()
}
res = append(res, pool.Queue(newFunc(i)))
}
for _, wrk := range res {
if wrk == nil {
continue
}
wrk.Wait()
}
}
func BenchmarkUnlimitedLargeRun(b *testing.B) {
res := make([]WorkUnit, 100)
b.ReportAllocs()
pool := New()
defer pool.Close()
newFunc := func(i int) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
}
for i := 0; i < 100; i++ {
res[i] = pool.Queue(newFunc(i))
}
var count int
for _, cw := range res {
cw.Wait()
count += cw.Value().(int)
}
if count != 100 {
b.Fatalf("Count Incorrect, Expected '100' Got '%d'", count)
}
}
func BenchmarkUnlimitedBatchSmallRun(b *testing.B) {
fn := func(wu WorkUnit) (interface{}, error) {
time.Sleep(time.Millisecond * 500)
if wu.IsCancelled() {
return nil, nil
}
time.Sleep(time.Millisecond * 500)
return 1, nil
}
pool := New()
defer pool.Close()
batch := pool.Batch()
for i := 0; i < 10; i++ {
batch.Queue(fn)
}
batch.QueueComplete()
var count int
for cw := range batch.Results() {
count += cw.Value().(int)
}
if count != 10 {
b.Fatal("Count Incorrect")
}
}

View file

@ -0,0 +1,194 @@
package pool
import (
"sync"
"testing"
"time"
. "gopkg.in/go-playground/assert.v1"
)
// NOTES:
// - Run "go test" to run tests
// - Run "gocov test | gocov report" to report on test converage by file
// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
//
// or
//
// -- may be a good idea to change to output path to somewherelike /tmp
// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
//
func TestUnlimitedPool(t *testing.T) {
var res []WorkUnit
pool := New()
defer pool.Close()
newFunc := func(d time.Duration) WorkFunc {
return func(WorkUnit) (interface{}, error) {
time.Sleep(d)
return nil, nil
}
}
for i := 0; i < 4; i++ {
wu := pool.Queue(newFunc(time.Second * 1))
res = append(res, wu)
}
var count int
for _, wu := range res {
wu.Wait()
Equal(t, wu.Error(), nil)
Equal(t, wu.Value(), nil)
count++
}
Equal(t, count, 4)
pool.Close() // testing no error occurs as Close will be called twice once defer pool.Close() fires
}
func TestUnlimitedCancel(t *testing.T) {
m := new(sync.RWMutex)
var closed bool
c := make(chan WorkUnit, 100)
pool := unlimitedGpool
defer pool.Close()
newFunc := func(d time.Duration) WorkFunc {
return func(WorkUnit) (interface{}, error) {
time.Sleep(d)
return 1, nil
}
}
go func(ch chan WorkUnit) {
for i := 0; i < 40; i++ {
go func(ch chan WorkUnit) {
m.RLock()
if !closed {
ch <- pool.Queue(newFunc(time.Second * 1))
}
m.RUnlock()
}(ch)
}
}(c)
time.Sleep(time.Second * 1)
pool.Cancel()
m.Lock()
closed = true
close(c)
m.Unlock()
var count int
for wu := range c {
wu.Wait()
if wu.Error() != nil {
_, ok := wu.Error().(*ErrCancelled)
if !ok {
_, ok = wu.Error().(*ErrPoolClosed)
if ok {
Equal(t, wu.Error().Error(), "ERROR: Work Unit added/run after the pool had been closed or cancelled")
}
} else {
Equal(t, wu.Error().Error(), "ERROR: Work Unit Cancelled")
}
Equal(t, ok, true)
continue
}
count += wu.Value().(int)
}
NotEqual(t, count, 40)
// reset and test again
pool.Reset()
wrk := pool.Queue(newFunc(time.Millisecond * 300))
wrk.Wait()
_, ok := wrk.Value().(int)
Equal(t, ok, true)
wrk = pool.Queue(newFunc(time.Millisecond * 300))
time.Sleep(time.Second * 1)
wrk.Cancel()
wrk.Wait() // proving we don't get stuck here after cancel
Equal(t, wrk.Error(), nil)
pool.Reset() // testing that we can do this and nothing bad will happen as it checks if pool closed
pool.Close()
wu := pool.Queue(newFunc(time.Second * 1))
wu.Wait()
NotEqual(t, wu.Error(), nil)
Equal(t, wu.Error().Error(), "ERROR: Work Unit added/run after the pool had been closed or cancelled")
}
func TestCancelFromWithin(t *testing.T) {
pool := New()
defer pool.Close()
newFunc := func(d time.Duration) WorkFunc {
return func(wu WorkUnit) (interface{}, error) {
time.Sleep(d)
if wu.IsCancelled() {
return nil, nil
}
return 1, nil
}
}
q := pool.Queue(newFunc(time.Second * 5))
time.Sleep(time.Second * 2)
pool.Cancel()
Equal(t, q.Value() == nil, true)
NotEqual(t, q.Error(), nil)
Equal(t, q.Error().Error(), "ERROR: Work Unit Cancelled")
}
func TestUnlimitedPanicRecovery(t *testing.T) {
pool := New()
defer pool.Close()
newFunc := func(d time.Duration, i int) WorkFunc {
return func(WorkUnit) (interface{}, error) {
if i == 1 {
panic("OMG OMG OMG! something bad happened!")
}
time.Sleep(d)
return 1, nil
}
}
var wrk WorkUnit
for i := 0; i < 4; i++ {
time.Sleep(time.Second * 1)
if i == 1 {
wrk = pool.Queue(newFunc(time.Second*1, i))
continue
}
pool.Queue(newFunc(time.Second*1, i))
}
wrk.Wait()
NotEqual(t, wrk.Error(), nil)
Equal(t, wrk.Error().Error()[0:90], "ERROR: Work Unit failed due to a recoverable error: 'OMG OMG OMG! something bad happened!'")
}