github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/catacomb/catacomb_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package catacomb_test 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/tomb.v1" 14 15 "github.com/juju/juju/worker" 16 "github.com/juju/juju/worker/catacomb" 17 ) 18 19 type CatacombSuite struct { 20 testing.IsolationSuite 21 fix *fixture 22 } 23 24 var _ = gc.Suite(&CatacombSuite{}) 25 26 func (s *CatacombSuite) SetUpTest(c *gc.C) { 27 s.IsolationSuite.SetUpTest(c) 28 s.fix = &fixture{cleaner: s} 29 } 30 31 func (s *CatacombSuite) TestStartsAlive(c *gc.C) { 32 s.fix.run(c, func() { 33 s.fix.assertNotDying(c) 34 s.fix.assertNotDead(c) 35 }) 36 } 37 38 func (s *CatacombSuite) TestKillClosesDying(c *gc.C) { 39 s.fix.run(c, func() { 40 s.fix.catacomb.Kill(nil) 41 s.fix.assertDying(c) 42 }) 43 } 44 45 func (s *CatacombSuite) TestKillDoesNotCloseDead(c *gc.C) { 46 s.fix.run(c, func() { 47 s.fix.catacomb.Kill(nil) 48 s.fix.assertNotDead(c) 49 }) 50 } 51 52 func (s *CatacombSuite) TestFinishTaskStopsCompletely(c *gc.C) { 53 s.fix.run(c, func() {}) 54 55 s.fix.assertDying(c) 56 s.fix.assertDead(c) 57 } 58 59 func (s *CatacombSuite) TestKillNil(c *gc.C) { 60 err := s.fix.run(c, func() { 61 s.fix.catacomb.Kill(nil) 62 }) 63 c.Check(err, jc.ErrorIsNil) 64 } 65 66 func (s *CatacombSuite) TestKillNonNilOverwritesNil(c *gc.C) { 67 second := errors.New("blah") 68 69 err := s.fix.run(c, func() { 70 s.fix.catacomb.Kill(nil) 71 s.fix.catacomb.Kill(second) 72 }) 73 c.Check(err, gc.Equals, second) 74 } 75 76 func (s *CatacombSuite) TestKillNilDoesNotOverwriteNonNil(c *gc.C) { 77 first := errors.New("blib") 78 79 err := s.fix.run(c, func() { 80 s.fix.catacomb.Kill(first) 81 s.fix.catacomb.Kill(nil) 82 }) 83 c.Check(err, gc.Equals, first) 84 } 85 86 func (s *CatacombSuite) TestKillNonNilDoesNotOverwriteNonNil(c *gc.C) { 87 first := errors.New("blib") 88 second := errors.New("blob") 89 90 err := s.fix.run(c, func() { 91 s.fix.catacomb.Kill(first) 92 s.fix.catacomb.Kill(second) 93 }) 94 c.Check(err, gc.Equals, first) 95 } 96 97 func (s *CatacombSuite) TestAliveErrDyingDifferent(c *gc.C) { 98 s.fix.run(c, func() { 99 notDying := s.fix.catacomb.ErrDying() 100 c.Check(notDying, gc.ErrorMatches, "bad catacomb ErrDying: still alive") 101 102 s.fix.catacomb.Kill(nil) 103 dying := s.fix.catacomb.ErrDying() 104 c.Check(dying, gc.ErrorMatches, "catacomb 0x[0-9a-f]+ is dying") 105 }) 106 } 107 108 func (s *CatacombSuite) TestKillAliveErrDying(c *gc.C) { 109 var notDying error 110 111 err := s.fix.run(c, func() { 112 notDying = s.fix.catacomb.ErrDying() 113 s.fix.catacomb.Kill(notDying) 114 }) 115 c.Check(err, gc.Equals, notDying) 116 } 117 118 func (s *CatacombSuite) TestKillErrDyingDoesNotOverwriteNil(c *gc.C) { 119 err := s.fix.run(c, func() { 120 s.fix.catacomb.Kill(nil) 121 errDying := s.fix.catacomb.ErrDying() 122 s.fix.catacomb.Kill(errDying) 123 }) 124 c.Check(err, jc.ErrorIsNil) 125 } 126 127 func (s *CatacombSuite) TestKillErrDyingDoesNotOverwriteNonNil(c *gc.C) { 128 first := errors.New("FRIST!") 129 130 err := s.fix.run(c, func() { 131 s.fix.catacomb.Kill(first) 132 errDying := s.fix.catacomb.ErrDying() 133 s.fix.catacomb.Kill(errDying) 134 }) 135 c.Check(err, gc.Equals, first) 136 } 137 138 func (s *CatacombSuite) TestKillCauseErrDyingDoesNotOverwriteNil(c *gc.C) { 139 err := s.fix.run(c, func() { 140 s.fix.catacomb.Kill(nil) 141 errDying := s.fix.catacomb.ErrDying() 142 disguised := errors.Annotatef(errDying, "disguised") 143 s.fix.catacomb.Kill(disguised) 144 }) 145 c.Check(err, jc.ErrorIsNil) 146 } 147 148 func (s *CatacombSuite) TestKillCauseErrDyingDoesNotOverwriteNonNil(c *gc.C) { 149 first := errors.New("FRIST!") 150 151 err := s.fix.run(c, func() { 152 s.fix.catacomb.Kill(first) 153 errDying := s.fix.catacomb.ErrDying() 154 disguised := errors.Annotatef(errDying, "disguised") 155 s.fix.catacomb.Kill(disguised) 156 }) 157 c.Check(err, gc.Equals, first) 158 } 159 160 func (s *CatacombSuite) TestKillTombErrDying(c *gc.C) { 161 err := s.fix.run(c, func() { 162 s.fix.catacomb.Kill(tomb.ErrDying) 163 }) 164 c.Check(err, gc.ErrorMatches, "bad catacomb Kill: tomb.ErrDying") 165 } 166 167 func (s *CatacombSuite) TestKillErrDyingFromOtherCatacomb(c *gc.C) { 168 fix2 := &fixture{} 169 fix2.run(c, func() {}) 170 errDying := fix2.catacomb.ErrDying() 171 172 err := s.fix.run(c, func() { 173 s.fix.catacomb.Kill(errDying) 174 }) 175 c.Check(err, gc.ErrorMatches, "bad catacomb Kill: other catacomb's ErrDying") 176 } 177 178 func (s *CatacombSuite) TestStopsAddedWorker(c *gc.C) { 179 w := s.fix.startErrorWorker(c, nil) 180 181 err := s.fix.run(c, func() { 182 s.fix.assertAddAlive(c, w) 183 }) 184 c.Check(err, jc.ErrorIsNil) 185 w.assertDead(c) 186 } 187 188 func (s *CatacombSuite) TestStopsInitWorker(c *gc.C) { 189 w := s.fix.startErrorWorker(c, nil) 190 191 err := s.fix.run(c, func() { 192 w.waitStillAlive(c) 193 }, w) 194 c.Check(err, jc.ErrorIsNil) 195 w.assertDead(c) 196 } 197 198 func (s *CatacombSuite) TestStoppedWorkerErrorOverwritesNil(c *gc.C) { 199 expect := errors.New("splot") 200 w := s.fix.startErrorWorker(c, expect) 201 202 err := s.fix.run(c, func() { 203 s.fix.assertAddAlive(c, w) 204 }) 205 c.Check(err, gc.Equals, expect) 206 w.assertDead(c) 207 } 208 209 func (s *CatacombSuite) TestStoppedWorkerErrorDoesNotOverwriteNonNil(c *gc.C) { 210 expect := errors.New("splot") 211 w := s.fix.startErrorWorker(c, errors.New("not interesting")) 212 213 err := s.fix.run(c, func() { 214 s.fix.assertAddAlive(c, w) 215 s.fix.catacomb.Kill(expect) 216 }) 217 c.Check(err, gc.Equals, expect) 218 w.assertDead(c) 219 } 220 221 func (s *CatacombSuite) TestPanicWorkerStillStops(c *gc.C) { 222 err := s.fix.run(c, func() { 223 panic("failed to startup") 224 }) 225 c.Check(err, gc.ErrorMatches, "panic resulted in: failed to startup") 226 } 227 228 func (s *CatacombSuite) TestAddWhenDyingStopsWorker(c *gc.C) { 229 err := s.fix.run(c, func() { 230 w := s.fix.startErrorWorker(c, nil) 231 s.fix.catacomb.Kill(nil) 232 expect := s.fix.catacomb.ErrDying() 233 234 err := s.fix.catacomb.Add(w) 235 c.Assert(err, gc.Equals, expect) 236 w.assertDead(c) 237 }) 238 c.Check(err, jc.ErrorIsNil) 239 } 240 241 func (s *CatacombSuite) TestAddWhenDyingReturnsWorkerError(c *gc.C) { 242 err := s.fix.run(c, func() { 243 expect := errors.New("squelch") 244 w := s.fix.startErrorWorker(c, expect) 245 s.fix.catacomb.Kill(nil) 246 247 actual := s.fix.catacomb.Add(w) 248 c.Assert(errors.Cause(actual), gc.Equals, expect) 249 w.assertDead(c) 250 }) 251 c.Check(err, jc.ErrorIsNil) 252 } 253 254 func (s *CatacombSuite) TestAddWhenDeadStopsWorker(c *gc.C) { 255 s.fix.run(c, func() {}) 256 expect := s.fix.catacomb.ErrDying() 257 258 w := s.fix.startErrorWorker(c, nil) 259 err := s.fix.catacomb.Add(w) 260 c.Assert(err, gc.Equals, expect) 261 w.assertDead(c) 262 } 263 264 func (s *CatacombSuite) TestAddWhenDeadReturnsWorkerError(c *gc.C) { 265 s.fix.run(c, func() {}) 266 267 expect := errors.New("squelch") 268 w := s.fix.startErrorWorker(c, expect) 269 actual := s.fix.catacomb.Add(w) 270 c.Assert(errors.Cause(actual), gc.Equals, expect) 271 w.assertDead(c) 272 } 273 274 func (s *CatacombSuite) TestFailAddedWorkerKills(c *gc.C) { 275 expect := errors.New("blarft") 276 w := s.fix.startErrorWorker(c, expect) 277 278 err := s.fix.run(c, func() { 279 s.fix.assertAddAlive(c, w) 280 w.Kill() 281 s.fix.waitDying(c) 282 }) 283 c.Check(err, gc.Equals, expect) 284 w.assertDead(c) 285 } 286 287 func (s *CatacombSuite) TestAddFailedWorkerKills(c *gc.C) { 288 expect := errors.New("blarft") 289 w := s.fix.startErrorWorker(c, expect) 290 w.stop() 291 292 err := s.fix.run(c, func() { 293 err := s.fix.catacomb.Add(w) 294 c.Assert(err, jc.ErrorIsNil) 295 s.fix.waitDying(c) 296 }) 297 c.Check(err, gc.Equals, expect) 298 } 299 300 func (s *CatacombSuite) TestInitFailedWorkerKills(c *gc.C) { 301 expect := errors.New("blarft") 302 w := s.fix.startErrorWorker(c, expect) 303 w.stop() 304 305 err := s.fix.run(c, func() { 306 s.fix.waitDying(c) 307 }, w) 308 c.Check(err, gc.Equals, expect) 309 } 310 311 func (s *CatacombSuite) TestFinishAddedWorkerDoesNotKill(c *gc.C) { 312 w := s.fix.startErrorWorker(c, nil) 313 314 err := s.fix.run(c, func() { 315 s.fix.assertAddAlive(c, w) 316 w.Kill() 317 318 w2 := s.fix.startErrorWorker(c, nil) 319 s.fix.assertAddAlive(c, w2) 320 }) 321 c.Check(err, jc.ErrorIsNil) 322 w.assertDead(c) 323 } 324 325 func (s *CatacombSuite) TestAddFinishedWorkerDoesNotKill(c *gc.C) { 326 w := s.fix.startErrorWorker(c, nil) 327 w.stop() 328 329 err := s.fix.run(c, func() { 330 err := s.fix.catacomb.Add(w) 331 c.Assert(err, jc.ErrorIsNil) 332 333 w2 := s.fix.startErrorWorker(c, nil) 334 s.fix.assertAddAlive(c, w2) 335 }) 336 c.Check(err, jc.ErrorIsNil) 337 } 338 339 func (s *CatacombSuite) TestInitFinishedWorkerDoesNotKill(c *gc.C) { 340 w := s.fix.startErrorWorker(c, nil) 341 w.stop() 342 343 err := s.fix.run(c, func() { 344 w2 := s.fix.startErrorWorker(c, nil) 345 s.fix.assertAddAlive(c, w2) 346 }, w) 347 c.Check(err, jc.ErrorIsNil) 348 } 349 350 func (s *CatacombSuite) TestStress(c *gc.C) { 351 const workerCount = 1000 352 workers := make([]*errorWorker, 0, workerCount) 353 354 // Just add a whole bunch of workers... 355 err := s.fix.run(c, func() { 356 for i := 0; i < workerCount; i++ { 357 w := s.fix.startErrorWorker(c, errors.Errorf("error %d", i)) 358 err := s.fix.catacomb.Add(w) 359 c.Check(err, jc.ErrorIsNil) 360 workers = append(workers, w) 361 } 362 }) 363 364 // ...and check that one of them killed the catacomb when it shut down; 365 // and that all of them have been stopped. 366 c.Check(err, gc.ErrorMatches, "error [0-9]+") 367 for _, w := range workers { 368 defer w.assertDead(c) 369 } 370 } 371 372 func (s *CatacombSuite) TestStressAddKillRaces(c *gc.C) { 373 const workerCount = 500 374 375 // This construct lets us run a bunch of funcs "simultaneously"... 376 var wg sync.WaitGroup 377 block := make(chan struct{}) 378 together := func(f func()) { 379 wg.Add(1) 380 go func() { 381 defer wg.Done() 382 <-block 383 f() 384 }() 385 } 386 387 // ...so we can queue up a whole bunch of adds/kills... 388 errFailed := errors.New("pow") 389 w := s.fix.startErrorWorker(c, errFailed) 390 err := s.fix.run(c, func() { 391 for i := 0; i < workerCount; i++ { 392 together(func() { 393 // NOTE: we reuse the same worker, largely for brevity's sake; 394 // the important thing is that it already exists so we can hit 395 // Add() as soon as possible, just like the Kill() below. 396 if err := s.fix.catacomb.Add(w); err != nil { 397 cause := errors.Cause(err) 398 c.Check(cause, gc.Equals, errFailed) 399 } 400 }) 401 together(func() { 402 s.fix.catacomb.Kill(errFailed) 403 }) 404 } 405 406 // ...then activate them all and see what happens. 407 close(block) 408 wg.Wait() 409 }) 410 cause := errors.Cause(err) 411 c.Check(cause, gc.Equals, errFailed) 412 } 413 414 func (s *CatacombSuite) TestReusedCatacomb(c *gc.C) { 415 var site catacomb.Catacomb 416 err := catacomb.Invoke(catacomb.Plan{ 417 Site: &site, 418 Work: func() error { return nil }, 419 }) 420 c.Check(err, jc.ErrorIsNil) 421 err = site.Wait() 422 c.Check(err, jc.ErrorIsNil) 423 424 w := s.fix.startErrorWorker(c, nil) 425 err = catacomb.Invoke(catacomb.Plan{ 426 Site: &site, 427 Work: func() error { return nil }, 428 Init: []worker.Worker{w}, 429 }) 430 c.Check(err, gc.ErrorMatches, "catacomb 0x[0-9a-f]+ has already been used") 431 w.assertDead(c) 432 } 433 434 func (s *CatacombSuite) TestPlanBadSite(c *gc.C) { 435 w := s.fix.startErrorWorker(c, nil) 436 plan := catacomb.Plan{ 437 Work: func() error { panic("no") }, 438 Init: []worker.Worker{w}, 439 } 440 checkInvalid(c, plan, "nil Site not valid") 441 w.assertDead(c) 442 } 443 444 func (s *CatacombSuite) TestPlanBadWork(c *gc.C) { 445 w := s.fix.startErrorWorker(c, nil) 446 plan := catacomb.Plan{ 447 Site: &catacomb.Catacomb{}, 448 Init: []worker.Worker{w}, 449 } 450 checkInvalid(c, plan, "nil Work not valid") 451 w.assertDead(c) 452 } 453 454 func (s *CatacombSuite) TestPlanBadInit(c *gc.C) { 455 w := s.fix.startErrorWorker(c, nil) 456 plan := catacomb.Plan{ 457 Site: &catacomb.Catacomb{}, 458 Work: func() error { panic("no") }, 459 Init: []worker.Worker{w, nil}, 460 } 461 checkInvalid(c, plan, "nil Init item 1 not valid") 462 w.assertDead(c) 463 } 464 465 func (s *CatacombSuite) TestPlanDataRace(c *gc.C) { 466 w := s.fix.startErrorWorker(c, nil) 467 plan := catacomb.Plan{ 468 Site: &catacomb.Catacomb{}, 469 Work: func() error { return nil }, 470 Init: []worker.Worker{w}, 471 } 472 err := catacomb.Invoke(plan) 473 c.Assert(err, jc.ErrorIsNil) 474 475 plan.Init[0] = nil 476 } 477 478 func checkInvalid(c *gc.C, plan catacomb.Plan, match string) { 479 check := func(err error) { 480 c.Check(err, gc.ErrorMatches, match) 481 c.Check(err, jc.Satisfies, errors.IsNotValid) 482 } 483 check(plan.Validate()) 484 check(catacomb.Invoke(plan)) 485 }