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 }