launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/life_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	gc "launchpad.net/gocheck"
     8  
     9  	"launchpad.net/juju-core/state"
    10  )
    11  
    12  type LifeSuite struct {
    13  	ConnSuite
    14  	charm *state.Charm
    15  	svc   *state.Service
    16  }
    17  
    18  func (s *LifeSuite) SetUpTest(c *gc.C) {
    19  	s.ConnSuite.SetUpTest(c)
    20  	s.charm = s.AddTestingCharm(c, "dummy")
    21  	s.svc = s.AddTestingService(c, "dummysvc", s.charm)
    22  }
    23  
    24  var _ = gc.Suite(&LifeSuite{})
    25  
    26  var stateChanges = []struct {
    27  	cached, desired    state.Life
    28  	dbinitial, dbfinal state.Life
    29  }{
    30  	{
    31  		state.Alive, state.Dying,
    32  		state.Alive, state.Dying,
    33  	},
    34  	{
    35  		state.Alive, state.Dying,
    36  		state.Dying, state.Dying,
    37  	},
    38  	{
    39  		state.Alive, state.Dying,
    40  		state.Dead, state.Dead,
    41  	},
    42  	{
    43  		state.Alive, state.Dead,
    44  		state.Alive, state.Dead,
    45  	},
    46  	{
    47  		state.Alive, state.Dead,
    48  		state.Dying, state.Dead,
    49  	},
    50  	{
    51  		state.Alive, state.Dead,
    52  		state.Dead, state.Dead,
    53  	},
    54  	{
    55  		state.Dying, state.Dying,
    56  		state.Dying, state.Dying,
    57  	},
    58  	{
    59  		state.Dying, state.Dying,
    60  		state.Dead, state.Dead,
    61  	},
    62  	{
    63  		state.Dying, state.Dead,
    64  		state.Dying, state.Dead,
    65  	},
    66  	{
    67  		state.Dying, state.Dead,
    68  		state.Dead, state.Dead,
    69  	},
    70  	{
    71  		state.Dead, state.Dying,
    72  		state.Dead, state.Dead,
    73  	},
    74  	{
    75  		state.Dead, state.Dead,
    76  		state.Dead, state.Dead,
    77  	},
    78  }
    79  
    80  type lifeFixture interface {
    81  	id() (coll string, id interface{})
    82  	setup(s *LifeSuite, c *gc.C) state.AgentLiving
    83  }
    84  
    85  type unitLife struct {
    86  	unit *state.Unit
    87  }
    88  
    89  func (l *unitLife) id() (coll string, id interface{}) {
    90  	return "units", l.unit.Name()
    91  }
    92  
    93  func (l *unitLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving {
    94  	unit, err := s.svc.AddUnit()
    95  	c.Assert(err, gc.IsNil)
    96  	preventUnitDestroyRemove(c, unit)
    97  	l.unit = unit
    98  	return l.unit
    99  }
   100  
   101  type machineLife struct {
   102  	machine *state.Machine
   103  }
   104  
   105  func (l *machineLife) id() (coll string, id interface{}) {
   106  	return "machines", l.machine.Id()
   107  }
   108  
   109  func (l *machineLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving {
   110  	var err error
   111  	l.machine, err = s.State.AddMachine("quantal", state.JobHostUnits)
   112  	c.Assert(err, gc.IsNil)
   113  	return l.machine
   114  }
   115  
   116  func (s *LifeSuite) prepareFixture(living state.Living, lfix lifeFixture, cached, dbinitial state.Life, c *gc.C) {
   117  	collName, id := lfix.id()
   118  	coll := s.MgoSuite.Session.DB("juju").C(collName)
   119  
   120  	err := coll.UpdateId(id, D{{"$set", D{
   121  		{"life", cached},
   122  	}}})
   123  	c.Assert(err, gc.IsNil)
   124  	err = living.Refresh()
   125  	c.Assert(err, gc.IsNil)
   126  
   127  	err = coll.UpdateId(id, D{{"$set", D{
   128  		{"life", dbinitial},
   129  	}}})
   130  	c.Assert(err, gc.IsNil)
   131  }
   132  
   133  func (s *LifeSuite) TestLifecycleStateChanges(c *gc.C) {
   134  	for i, lfix := range []lifeFixture{&unitLife{}, &machineLife{}} {
   135  		c.Logf("fixture %d", i)
   136  		for j, v := range stateChanges {
   137  			c.Logf("sequence %d", j)
   138  			living := lfix.setup(s, c)
   139  			s.prepareFixture(living, lfix, v.cached, v.dbinitial, c)
   140  			switch v.desired {
   141  			case state.Dying:
   142  				err := living.Destroy()
   143  				c.Assert(err, gc.IsNil)
   144  			case state.Dead:
   145  				err := living.EnsureDead()
   146  				c.Assert(err, gc.IsNil)
   147  			default:
   148  				panic("desired lifecycle can only be dying or dead")
   149  			}
   150  			err := living.Refresh()
   151  			c.Assert(err, gc.IsNil)
   152  			c.Assert(living.Life(), gc.Equals, v.dbfinal)
   153  			err = living.EnsureDead()
   154  			c.Assert(err, gc.IsNil)
   155  			err = living.Remove()
   156  			c.Assert(err, gc.IsNil)
   157  		}
   158  	}
   159  }
   160  
   161  const (
   162  	notAliveErr = ".*: not found or not alive"
   163  	deadErr     = ".*: not found or dead"
   164  	noErr       = ""
   165  )
   166  
   167  type lifer interface {
   168  	EnsureDead() error
   169  	Destroy() error
   170  	Life() state.Life
   171  }
   172  
   173  func runLifeChecks(c *gc.C, obj lifer, expectErr string, checks []func() error) {
   174  	for i, check := range checks {
   175  		c.Logf("check %d when %v", i, obj.Life())
   176  		err := check()
   177  		if expectErr == noErr {
   178  			c.Assert(err, gc.IsNil)
   179  		} else {
   180  			c.Assert(err, gc.ErrorMatches, expectErr)
   181  		}
   182  	}
   183  }
   184  
   185  // testWhenDying sets obj to Dying and Dead in turn, and asserts
   186  // that the errors from the given checks match aliveErr, dyingErr and deadErr
   187  // in each respective life state.
   188  func testWhenDying(c *gc.C, obj lifer, dyingErr, deadErr string, checks ...func() error) {
   189  	c.Logf("checking life of %v (%T)", obj, obj)
   190  	err := obj.Destroy()
   191  	c.Assert(err, gc.IsNil)
   192  	runLifeChecks(c, obj, dyingErr, checks)
   193  	err = obj.EnsureDead()
   194  	c.Assert(err, gc.IsNil)
   195  	runLifeChecks(c, obj, deadErr, checks)
   196  }