github.com/efureev/go-poool@v1.0.0/pool_test.go (about)

     1  package poool
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	. "github.com/smartystreets/goconvey/convey"
     9  )
    10  
    11  func TestPool(t *testing.T) {
    12  	Convey("create Pool", t, func() {
    13  		var res []Job
    14  
    15  		pool := New(4)
    16  		defer pool.Close()
    17  
    18  		newJobFunc := func(d time.Duration) JobFn {
    19  			return func(Job) (interface{}, error) {
    20  				time.Sleep(d)
    21  				return nil, nil
    22  			}
    23  		}
    24  
    25  		Convey("Run Jobs", func() {
    26  			for i := 0; i < 10; i++ {
    27  				j := pool.Queue(newJobFunc(time.Second * 1))
    28  				res = append(res, j)
    29  			}
    30  
    31  			var count int
    32  
    33  			for _, j := range res {
    34  				j.Wait()
    35  
    36  				So(j.Error(), ShouldBeNil)
    37  				So(j.Value(), ShouldBeNil)
    38  				count++
    39  			}
    40  
    41  			So(count, ShouldEqual, 10)
    42  		})
    43  
    44  		pool.Close() // testing no error occurs as Close will be called twice once defer pool.Close() fires
    45  
    46  	})
    47  }
    48  
    49  func TestCancel(t *testing.T) {
    50  
    51  	Convey("Pool: test Cancel", t, func() {
    52  
    53  		m := new(sync.RWMutex)
    54  		var closed bool
    55  		c := make(chan Job, 100)
    56  
    57  		pool := New(4)
    58  		defer pool.Close()
    59  
    60  		newFunc := func(d time.Duration) JobFn {
    61  			return func(Job) (interface{}, error) {
    62  				time.Sleep(d)
    63  				return 1, nil
    64  			}
    65  		}
    66  
    67  		go func(ch chan Job) {
    68  			for i := 0; i < 40; i++ {
    69  
    70  				go func(ch chan Job) {
    71  					m.RLock()
    72  					if closed {
    73  						m.RUnlock()
    74  						return
    75  					}
    76  
    77  					ch <- pool.Queue(newFunc(time.Second * 1))
    78  					m.RUnlock()
    79  				}(ch)
    80  			}
    81  		}(c)
    82  
    83  		time.Sleep(time.Second * 1)
    84  		pool.Cancel()
    85  		m.Lock()
    86  		closed = true
    87  		close(c)
    88  		m.Unlock()
    89  
    90  		var count int
    91  
    92  		for j := range c {
    93  			j.Wait()
    94  
    95  			if j.Error() != nil {
    96  				_, ok := j.Error().(*ErrCancelled)
    97  				if !ok {
    98  					_, ok = j.Error().(*ErrPoolClosed)
    99  					if ok {
   100  						So(j.Error().Error(), ShouldEqual, "ERROR: Job added/run after the pool had been closed or cancelled")
   101  					}
   102  				} else {
   103  					So(j.Error().Error(), ShouldEqual, "ERROR: Job Cancelled")
   104  				}
   105  
   106  				So(ok, ShouldBeTrue)
   107  
   108  				continue
   109  			}
   110  
   111  			count += j.Value().(int)
   112  		}
   113  
   114  		So(count, ShouldNotEqual, 40)
   115  
   116  		// reset and test again
   117  		pool.Reset()
   118  
   119  		wrk := pool.Queue(newFunc(time.Millisecond * 300))
   120  		wrk.Wait()
   121  
   122  		_, ok := wrk.Value().(int)
   123  		So(ok, ShouldBeTrue)
   124  
   125  		wrk = pool.Queue(newFunc(time.Millisecond * 300))
   126  		time.Sleep(time.Second * 1)
   127  		wrk.Cancel()
   128  		wrk.Wait() // proving we don't get stuck here after cancel
   129  		So(wrk.Error(), ShouldBeNil)
   130  
   131  		pool.Reset() // testing that we can do this and nothing bad will happen as it checks if pool closed
   132  
   133  		pool.Close()
   134  
   135  		wu := pool.Queue(newFunc(time.Second * 1))
   136  		wu.Wait()
   137  
   138  		So(wu.Error(), ShouldNotBeNil)
   139  		So(wu.Error(), ShouldBeError)
   140  
   141  		So(wu.Error().Error(), ShouldEqual, "ERROR: Job added/run after the pool had been closed or cancelled")
   142  	})
   143  }
   144  
   145  func TestBadWorkerCount(t *testing.T) {
   146  	Convey("create Pool without workers", t, func() {
   147  		So(func() { New(0) }, ShouldPanicWith, "invalid workers '0'")
   148  	})
   149  }
   150  
   151  func TestPanicRecovery(t *testing.T) {
   152  
   153  	Convey("Test Panic recovery", t, func() {
   154  		pool := New(2)
   155  		defer pool.Close()
   156  
   157  		newFunc := func(d time.Duration, i int) JobFn {
   158  			return func(Job) (interface{}, error) {
   159  				if i == 1 {
   160  					panic("OMG OMG OMG! something bad happened!")
   161  				}
   162  				time.Sleep(d)
   163  				return 1, nil
   164  			}
   165  		}
   166  
   167  		var j Job
   168  		for i := 0; i < 4; i++ {
   169  			time.Sleep(time.Second * 1)
   170  			if i == 1 {
   171  				j = pool.Queue(newFunc(time.Second*1, i))
   172  				continue
   173  			}
   174  			pool.Queue(newFunc(time.Second*1, i))
   175  		}
   176  
   177  		j.Wait()
   178  
   179  		So(j.Error(), ShouldBeError)
   180  		So(j.Error().Error()[:84], ShouldEqual, "ERROR: Job failed due to a recoverable error: 'OMG OMG OMG! something bad happened!'")
   181  	})
   182  }
   183  
   184  func BenchmarkPoolSmallRun(b *testing.B) {
   185  
   186  	res := make([]Job, 10)
   187  
   188  	b.ReportAllocs()
   189  
   190  	pool := New(10)
   191  	defer pool.Close()
   192  
   193  	fn := func(j Job) (interface{}, error) {
   194  		time.Sleep(time.Millisecond * 500)
   195  		if j.IsCancelled() {
   196  			return nil, nil
   197  		}
   198  		time.Sleep(time.Millisecond * 500)
   199  		return 1, nil
   200  	}
   201  
   202  	for i := 0; i < 10; i++ {
   203  		res[i] = pool.Queue(fn)
   204  	}
   205  
   206  	var count int
   207  
   208  	for _, cw := range res {
   209  
   210  		cw.Wait()
   211  
   212  		if cw.Error() == nil {
   213  			count += cw.Value().(int)
   214  		}
   215  	}
   216  
   217  	if count != 10 {
   218  		b.Fatal("Count Incorrect")
   219  	}
   220  }
   221  
   222  func BenchmarkPoolSmallCancel(b *testing.B) {
   223  
   224  	res := make([]Job, 0, 20)
   225  
   226  	b.ReportAllocs()
   227  
   228  	pool := New(4)
   229  	defer pool.Close()
   230  
   231  	newFunc := func(i int) JobFn {
   232  		return func(j Job) (interface{}, error) {
   233  			time.Sleep(time.Millisecond * 500)
   234  			if j.IsCancelled() {
   235  				return nil, nil
   236  			}
   237  			time.Sleep(time.Millisecond * 500)
   238  			return i, nil
   239  		}
   240  	}
   241  
   242  	for i := 0; i < 20; i++ {
   243  		if i == 6 {
   244  			pool.Cancel()
   245  		}
   246  		res = append(res, pool.Queue(newFunc(i)))
   247  	}
   248  
   249  	for _, wrk := range res {
   250  		if wrk == nil {
   251  			continue
   252  		}
   253  		wrk.Wait()
   254  	}
   255  }
   256  
   257  func BenchmarkPoolOverConsumeLargeRun(b *testing.B) {
   258  
   259  	res := make([]Job, 100)
   260  
   261  	b.ReportAllocs()
   262  
   263  	pool := New(25)
   264  	defer pool.Close()
   265  
   266  	newFunc := func(i int) JobFn {
   267  		return func(j Job) (interface{}, error) {
   268  			time.Sleep(time.Millisecond * 500)
   269  			if j.IsCancelled() {
   270  				return nil, nil
   271  			}
   272  			time.Sleep(time.Millisecond * 500)
   273  			return 1, nil
   274  		}
   275  	}
   276  
   277  	for i := 0; i < 100; i++ {
   278  		res[i] = pool.Queue(newFunc(i))
   279  	}
   280  
   281  	var count int
   282  
   283  	for _, cw := range res {
   284  
   285  		cw.Wait()
   286  
   287  		count += cw.Value().(int)
   288  	}
   289  
   290  	if count != 100 {
   291  		b.Fatalf("Count Incorrect, Expected '100' Got '%d'", count)
   292  	}
   293  }
   294  
   295  func BenchmarkPoolLargeCancel(b *testing.B) {
   296  
   297  	res := make([]Job, 0, 1000)
   298  
   299  	b.ReportAllocs()
   300  
   301  	pool := New(4)
   302  	defer pool.Close()
   303  
   304  	newFunc := func(i int) JobFn {
   305  		return func(j Job) (interface{}, error) {
   306  			time.Sleep(time.Millisecond * 500)
   307  			if j.IsCancelled() {
   308  				return nil, nil
   309  			}
   310  			time.Sleep(time.Millisecond * 500)
   311  			return i, nil
   312  		}
   313  	}
   314  
   315  	for i := 0; i < 1000; i++ {
   316  		if i == 6 {
   317  			pool.Cancel()
   318  		}
   319  		res = append(res, pool.Queue(newFunc(i)))
   320  	}
   321  
   322  	for _, wrk := range res {
   323  		if wrk == nil {
   324  			continue
   325  		}
   326  		wrk.Wait()
   327  	}
   328  }