github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "launchpad.net/tomb" 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) TestAddWhenDyingStopsWorker(c *gc.C) { 222 err := s.fix.run(c, func() { 223 w := s.fix.startErrorWorker(c, nil) 224 s.fix.catacomb.Kill(nil) 225 expect := s.fix.catacomb.ErrDying() 226 227 err := s.fix.catacomb.Add(w) 228 c.Assert(err, gc.Equals, expect) 229 w.assertDead(c) 230 }) 231 c.Check(err, jc.ErrorIsNil) 232 } 233 234 func (s *CatacombSuite) TestAddWhenDyingReturnsWorkerError(c *gc.C) { 235 err := s.fix.run(c, func() { 236 expect := errors.New("squelch") 237 w := s.fix.startErrorWorker(c, expect) 238 s.fix.catacomb.Kill(nil) 239 240 actual := s.fix.catacomb.Add(w) 241 c.Assert(errors.Cause(actual), gc.Equals, expect) 242 w.assertDead(c) 243 }) 244 c.Check(err, jc.ErrorIsNil) 245 } 246 247 func (s *CatacombSuite) TestAddWhenDeadStopsWorker(c *gc.C) { 248 s.fix.run(c, func() {}) 249 expect := s.fix.catacomb.ErrDying() 250 251 w := s.fix.startErrorWorker(c, nil) 252 err := s.fix.catacomb.Add(w) 253 c.Assert(err, gc.Equals, expect) 254 w.assertDead(c) 255 } 256 257 func (s *CatacombSuite) TestAddWhenDeadReturnsWorkerError(c *gc.C) { 258 s.fix.run(c, func() {}) 259 260 expect := errors.New("squelch") 261 w := s.fix.startErrorWorker(c, expect) 262 actual := s.fix.catacomb.Add(w) 263 c.Assert(errors.Cause(actual), gc.Equals, expect) 264 w.assertDead(c) 265 } 266 267 func (s *CatacombSuite) TestFailAddedWorkerKills(c *gc.C) { 268 expect := errors.New("blarft") 269 w := s.fix.startErrorWorker(c, expect) 270 271 err := s.fix.run(c, func() { 272 s.fix.assertAddAlive(c, w) 273 w.Kill() 274 s.fix.waitDying(c) 275 }) 276 c.Check(err, gc.Equals, expect) 277 w.assertDead(c) 278 } 279 280 func (s *CatacombSuite) TestAddFailedWorkerKills(c *gc.C) { 281 expect := errors.New("blarft") 282 w := s.fix.startErrorWorker(c, expect) 283 w.stop() 284 285 err := s.fix.run(c, func() { 286 err := s.fix.catacomb.Add(w) 287 c.Assert(err, jc.ErrorIsNil) 288 s.fix.waitDying(c) 289 }) 290 c.Check(err, gc.Equals, expect) 291 } 292 293 func (s *CatacombSuite) TestInitFailedWorkerKills(c *gc.C) { 294 expect := errors.New("blarft") 295 w := s.fix.startErrorWorker(c, expect) 296 w.stop() 297 298 err := s.fix.run(c, func() { 299 s.fix.waitDying(c) 300 }, w) 301 c.Check(err, gc.Equals, expect) 302 } 303 304 func (s *CatacombSuite) TestFinishAddedWorkerDoesNotKill(c *gc.C) { 305 w := s.fix.startErrorWorker(c, nil) 306 307 err := s.fix.run(c, func() { 308 s.fix.assertAddAlive(c, w) 309 w.Kill() 310 311 w2 := s.fix.startErrorWorker(c, nil) 312 s.fix.assertAddAlive(c, w2) 313 }) 314 c.Check(err, jc.ErrorIsNil) 315 w.assertDead(c) 316 } 317 318 func (s *CatacombSuite) TestAddFinishedWorkerDoesNotKill(c *gc.C) { 319 w := s.fix.startErrorWorker(c, nil) 320 w.stop() 321 322 err := s.fix.run(c, func() { 323 err := s.fix.catacomb.Add(w) 324 c.Assert(err, jc.ErrorIsNil) 325 326 w2 := s.fix.startErrorWorker(c, nil) 327 s.fix.assertAddAlive(c, w2) 328 }) 329 c.Check(err, jc.ErrorIsNil) 330 } 331 332 func (s *CatacombSuite) TestInitFinishedWorkerDoesNotKill(c *gc.C) { 333 w := s.fix.startErrorWorker(c, nil) 334 w.stop() 335 336 err := s.fix.run(c, func() { 337 w2 := s.fix.startErrorWorker(c, nil) 338 s.fix.assertAddAlive(c, w2) 339 }, w) 340 c.Check(err, jc.ErrorIsNil) 341 } 342 343 func (s *CatacombSuite) TestStress(c *gc.C) { 344 const workerCount = 1000 345 workers := make([]*errorWorker, 0, workerCount) 346 347 // Just add a whole bunch of workers... 348 err := s.fix.run(c, func() { 349 for i := 0; i < workerCount; i++ { 350 w := s.fix.startErrorWorker(c, errors.Errorf("error %d", i)) 351 err := s.fix.catacomb.Add(w) 352 c.Check(err, jc.ErrorIsNil) 353 workers = append(workers, w) 354 } 355 }) 356 357 // ...and check that one of them killed the catacomb when it shut down; 358 // and that all of them have been stopped. 359 c.Check(err, gc.ErrorMatches, "error [0-9]+") 360 for _, w := range workers { 361 defer w.assertDead(c) 362 } 363 } 364 365 func (s *CatacombSuite) TestStressAddKillRaces(c *gc.C) { 366 const workerCount = 500 367 368 // This construct lets us run a bunch of funcs "simultaneously"... 369 var wg sync.WaitGroup 370 block := make(chan struct{}) 371 together := func(f func()) { 372 wg.Add(1) 373 go func() { 374 defer wg.Done() 375 <-block 376 f() 377 }() 378 } 379 380 // ...so we can queue up a whole bunch of adds/kills... 381 errFailed := errors.New("pow") 382 w := s.fix.startErrorWorker(c, errFailed) 383 err := s.fix.run(c, func() { 384 for i := 0; i < workerCount; i++ { 385 together(func() { 386 // NOTE: we reuse the same worker, largely for brevity's sake; 387 // the important thing is that it already exists so we can hit 388 // Add() as soon as possible, just like the Kill() below. 389 if err := s.fix.catacomb.Add(w); err != nil { 390 cause := errors.Cause(err) 391 c.Check(cause, gc.Equals, errFailed) 392 } 393 }) 394 together(func() { 395 s.fix.catacomb.Kill(errFailed) 396 }) 397 } 398 399 // ...then activate them all and see what happens. 400 close(block) 401 wg.Wait() 402 }) 403 cause := errors.Cause(err) 404 c.Check(cause, gc.Equals, errFailed) 405 } 406 407 func (s *CatacombSuite) TestReusedCatacomb(c *gc.C) { 408 var site catacomb.Catacomb 409 err := catacomb.Invoke(catacomb.Plan{ 410 Site: &site, 411 Work: func() error { return nil }, 412 }) 413 c.Check(err, jc.ErrorIsNil) 414 err = site.Wait() 415 c.Check(err, jc.ErrorIsNil) 416 417 w := s.fix.startErrorWorker(c, nil) 418 err = catacomb.Invoke(catacomb.Plan{ 419 Site: &site, 420 Work: func() error { return nil }, 421 Init: []worker.Worker{w}, 422 }) 423 c.Check(err, gc.ErrorMatches, "catacomb 0x[0-9a-f]+ has already been used") 424 w.assertDead(c) 425 } 426 427 func (s *CatacombSuite) TestPlanBadSite(c *gc.C) { 428 w := s.fix.startErrorWorker(c, nil) 429 plan := catacomb.Plan{ 430 Work: func() error { panic("no") }, 431 Init: []worker.Worker{w}, 432 } 433 checkInvalid(c, plan, "nil Site not valid") 434 w.assertDead(c) 435 } 436 437 func (s *CatacombSuite) TestPlanBadWork(c *gc.C) { 438 w := s.fix.startErrorWorker(c, nil) 439 plan := catacomb.Plan{ 440 Site: &catacomb.Catacomb{}, 441 Init: []worker.Worker{w}, 442 } 443 checkInvalid(c, plan, "nil Work not valid") 444 w.assertDead(c) 445 } 446 447 func (s *CatacombSuite) TestPlanBadInit(c *gc.C) { 448 w := s.fix.startErrorWorker(c, nil) 449 plan := catacomb.Plan{ 450 Site: &catacomb.Catacomb{}, 451 Work: func() error { panic("no") }, 452 Init: []worker.Worker{w, nil}, 453 } 454 checkInvalid(c, plan, "nil Init item 1 not valid") 455 w.assertDead(c) 456 } 457 458 func (s *CatacombSuite) TestPlanDataRace(c *gc.C) { 459 w := s.fix.startErrorWorker(c, nil) 460 plan := catacomb.Plan{ 461 Site: &catacomb.Catacomb{}, 462 Work: func() error { return nil }, 463 Init: []worker.Worker{w}, 464 } 465 err := catacomb.Invoke(plan) 466 c.Assert(err, jc.ErrorIsNil) 467 468 plan.Init[0] = nil 469 } 470 471 func checkInvalid(c *gc.C, plan catacomb.Plan, match string) { 472 check := func(err error) { 473 c.Check(err, gc.ErrorMatches, match) 474 c.Check(err, jc.Satisfies, errors.IsNotValid) 475 } 476 check(plan.Validate()) 477 check(catacomb.Invoke(plan)) 478 }