2016-12-08 15:32:12 +03:00
package safe
import (
2017-04-30 15:39:49 +03:00
"context"
2016-12-08 15:32:52 +03:00
"fmt"
2017-04-30 15:39:49 +03:00
"sync"
2016-12-08 15:32:12 +03:00
"testing"
2017-04-30 15:39:49 +03:00
"time"
"github.com/cenk/backoff"
2016-12-08 15:32:12 +03:00
)
2017-04-30 15:39:49 +03:00
func TestNewPoolContext ( t * testing . T ) {
type testKeyType string
testKey := testKeyType ( "test" )
ctx := context . WithValue ( context . Background ( ) , testKey , "test" )
p := NewPool ( ctx )
retCtx := p . Ctx ( )
retCtxVal , ok := retCtx . Value ( testKey ) . ( string )
if ! ok || retCtxVal != "test" {
t . Errorf ( "Pool.Ctx() did not return a derived context, got %#v, expected context with test value" , retCtx )
}
}
type fakeRoutine struct {
sync . Mutex
started bool
startSig chan bool
}
func newFakeRoutine ( ) * fakeRoutine {
return & fakeRoutine {
startSig : make ( chan bool ) ,
}
}
func ( tr * fakeRoutine ) routineCtx ( ctx context . Context ) {
tr . Lock ( )
tr . started = true
tr . Unlock ( )
tr . startSig <- true
<- ctx . Done ( )
}
func ( tr * fakeRoutine ) routine ( stop chan bool ) {
tr . Lock ( )
tr . started = true
tr . Unlock ( )
tr . startSig <- true
<- stop
}
func TestPoolWithCtx ( t * testing . T ) {
testRoutine := newFakeRoutine ( )
tt := [ ] struct {
desc string
fn func ( * Pool )
} {
{
desc : "GoCtx()" ,
fn : func ( p * Pool ) {
p . GoCtx ( testRoutine . routineCtx )
} ,
} ,
{
desc : "AddGoCtx()" ,
fn : func ( p * Pool ) {
p . AddGoCtx ( testRoutine . routineCtx )
p . Start ( )
} ,
} ,
}
for _ , tc := range tt {
tc := tc
t . Run ( tc . desc , func ( t * testing . T ) {
// These subtests cannot be run in parallel, since the testRoutine
// is shared across the subtests.
p := NewPool ( context . Background ( ) )
timer := time . NewTimer ( 500 * time . Millisecond )
defer timer . Stop ( )
tc . fn ( p )
defer p . Cleanup ( )
if len ( p . routinesCtx ) != 1 {
t . Fatalf ( "After %s, Pool did have %d goroutineCtxs, expected 1" , tc . desc , len ( p . routinesCtx ) )
}
testDone := make ( chan bool , 1 )
go func ( ) {
<- testRoutine . startSig
p . Cleanup ( )
testDone <- true
} ( )
select {
case <- timer . C :
testRoutine . Lock ( )
defer testRoutine . Unlock ( )
t . Fatalf ( "Pool test did not complete in time, goroutine started equals '%t'" , testRoutine . started )
case <- testDone :
return
}
} )
}
}
func TestPoolWithStopChan ( t * testing . T ) {
testRoutine := newFakeRoutine ( )
ctx := context . Background ( )
p := NewPool ( ctx )
timer := time . NewTimer ( 500 * time . Millisecond )
defer timer . Stop ( )
p . Go ( testRoutine . routine )
if len ( p . routines ) != 1 {
t . Fatalf ( "After Pool.Go(func), Pool did have %d goroutines, expected 1" , len ( p . routines ) )
}
testDone := make ( chan bool , 1 )
go func ( ) {
<- testRoutine . startSig
p . Cleanup ( )
testDone <- true
} ( )
select {
case <- timer . C :
testRoutine . Lock ( )
defer testRoutine . Unlock ( )
t . Fatalf ( "Pool test did not complete in time, goroutine started equals '%t'" , testRoutine . started )
case <- testDone :
return
}
}
func TestPoolStartWithStopChan ( t * testing . T ) {
testRoutine := newFakeRoutine ( )
ctx := context . Background ( )
p := NewPool ( ctx )
timer := time . NewTimer ( 500 * time . Millisecond )
defer timer . Stop ( )
// Insert the stopped test goroutine via private fields into the Pool.
// There currently is no way to insert a routine via exported funcs that is not started immediately.
p . lock . Lock ( )
newRoutine := routine {
goroutine : testRoutine . routine ,
}
p . routines = append ( p . routines , newRoutine )
p . lock . Unlock ( )
p . Start ( )
testDone := make ( chan bool , 1 )
go func ( ) {
<- testRoutine . startSig
p . Cleanup ( )
testDone <- true
} ( )
select {
case <- timer . C :
testRoutine . Lock ( )
defer testRoutine . Unlock ( )
t . Fatalf ( "Pool.Start() did not complete in time, goroutine started equals '%t'" , testRoutine . started )
case <- testDone :
return
}
}
func TestGoroutineRecover ( t * testing . T ) {
// if recover fails the test will panic
Go ( func ( ) {
panic ( "BOOM" )
} )
}
2016-12-08 15:32:12 +03:00
func TestOperationWithRecover ( t * testing . T ) {
2016-12-08 15:32:52 +03:00
operation := func ( ) error {
return nil
}
err := backoff . Retry ( OperationWithRecover ( operation ) , & backoff . StopBackOff { } )
if err != nil {
t . Fatalf ( "Error in OperationWithRecover: %s" , err )
}
}
func TestOperationWithRecoverPanic ( t * testing . T ) {
2016-12-08 15:32:12 +03:00
operation := func ( ) error {
panic ( "BOOM" )
}
err := backoff . Retry ( OperationWithRecover ( operation ) , & backoff . StopBackOff { } )
if err == nil {
t . Fatalf ( "Error in OperationWithRecover: %s" , err )
}
}
2016-12-08 15:32:52 +03:00
func TestOperationWithRecoverError ( t * testing . T ) {
operation := func ( ) error {
return fmt . Errorf ( "ERROR" )
}
err := backoff . Retry ( OperationWithRecover ( operation ) , & backoff . StopBackOff { } )
if err == nil {
t . Fatalf ( "Error in OperationWithRecover: %s" , err )
}
}