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  }