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  }