github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/operation/runhook_test.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation_test
     5  
     6  import (
     7  	"time"
     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/juju/charm.v4/hooks"
    14  
    15  	"github.com/juju/juju/worker/uniter/hook"
    16  	"github.com/juju/juju/worker/uniter/operation"
    17  	"github.com/juju/juju/worker/uniter/runner"
    18  )
    19  
    20  type RunHookSuite struct {
    21  	testing.IsolationSuite
    22  }
    23  
    24  var _ = gc.Suite(&RunHookSuite{})
    25  
    26  type newHook func(operation.Factory, hook.Info) (operation.Operation, error)
    27  
    28  func (s *RunHookSuite) testClearResolvedFlagError(c *gc.C, newHook newHook) {
    29  	callbacks := &PrepareHookCallbacks{
    30  		MockClearResolvedFlag: &MockNoArgs{err: errors.New("biff")},
    31  	}
    32  	factory := operation.NewFactory(nil, nil, callbacks, nil)
    33  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    34  	c.Assert(err, jc.ErrorIsNil)
    35  
    36  	newState, err := op.Prepare(operation.State{})
    37  	c.Check(newState, gc.IsNil)
    38  	c.Check(callbacks.MockClearResolvedFlag.called, jc.IsTrue)
    39  	c.Check(err, gc.ErrorMatches, "biff")
    40  }
    41  
    42  func (s *RunHookSuite) TestClearResolvedFlagError_Retry(c *gc.C) {
    43  	s.testClearResolvedFlagError(c, (operation.Factory).NewRetryHook)
    44  }
    45  
    46  func (s *RunHookSuite) TestClearResolvedFlagError_Skip(c *gc.C) {
    47  	s.testClearResolvedFlagError(c, (operation.Factory).NewSkipHook)
    48  }
    49  
    50  func (s *RunHookSuite) testPrepareHookError(
    51  	c *gc.C, newHook newHook, expectClearResolvedFlag, expectSkip bool,
    52  ) {
    53  	callbacks := &PrepareHookCallbacks{
    54  		MockPrepareHook:       &MockPrepareHook{err: errors.New("pow")},
    55  		MockClearResolvedFlag: &MockNoArgs{},
    56  	}
    57  	factory := operation.NewFactory(nil, nil, callbacks, nil)
    58  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    59  	c.Assert(err, jc.ErrorIsNil)
    60  
    61  	newState, err := op.Prepare(operation.State{})
    62  	c.Check(newState, gc.IsNil)
    63  	c.Check(callbacks.MockClearResolvedFlag.called, gc.Equals, expectClearResolvedFlag)
    64  	if expectSkip {
    65  		c.Check(err, gc.Equals, operation.ErrSkipExecute)
    66  		c.Check(callbacks.MockPrepareHook.gotHook, gc.IsNil)
    67  		return
    68  	}
    69  	c.Check(err, gc.ErrorMatches, "pow")
    70  	c.Check(callbacks.MockPrepareHook.gotHook, gc.DeepEquals, &hook.Info{
    71  		Kind: hooks.ConfigChanged,
    72  	})
    73  }
    74  
    75  func (s *RunHookSuite) TestPrepareHookError_Run(c *gc.C) {
    76  	s.testPrepareHookError(c, (operation.Factory).NewRunHook, false, false)
    77  }
    78  
    79  func (s *RunHookSuite) TestPrepareHookError_Retry(c *gc.C) {
    80  	s.testPrepareHookError(c, (operation.Factory).NewRetryHook, true, false)
    81  }
    82  
    83  func (s *RunHookSuite) TestPrepareHookError_Skip(c *gc.C) {
    84  	s.testPrepareHookError(c, (operation.Factory).NewSkipHook, true, true)
    85  }
    86  
    87  func (s *RunHookSuite) testPrepareRunnerError(c *gc.C, newHook newHook) {
    88  	callbacks := NewPrepareHookCallbacks()
    89  	runnerFactory := &MockRunnerFactory{
    90  		MockNewHookRunner: &MockNewHookRunner{err: errors.New("splat")},
    91  	}
    92  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil)
    93  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
    94  	c.Assert(err, jc.ErrorIsNil)
    95  
    96  	newState, err := op.Prepare(operation.State{})
    97  	c.Check(newState, gc.IsNil)
    98  	c.Check(err, gc.ErrorMatches, "splat")
    99  	c.Check(runnerFactory.MockNewHookRunner.gotHook, gc.DeepEquals, &hook.Info{
   100  		Kind: hooks.ConfigChanged,
   101  	})
   102  }
   103  
   104  func (s *RunHookSuite) TestPrepareRunnerError_Run(c *gc.C) {
   105  	s.testPrepareRunnerError(c, (operation.Factory).NewRunHook)
   106  }
   107  
   108  func (s *RunHookSuite) TestPrepareRunnerError_Retry(c *gc.C) {
   109  	s.testPrepareRunnerError(c, (operation.Factory).NewRetryHook)
   110  }
   111  
   112  func (s *RunHookSuite) testPrepareSuccess(
   113  	c *gc.C, newHook newHook, before, after operation.State,
   114  ) {
   115  	runnerFactory := NewRunHookRunnerFactory(errors.New("should not call"))
   116  	callbacks := NewPrepareHookCallbacks()
   117  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil)
   118  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   119  	c.Assert(err, jc.ErrorIsNil)
   120  
   121  	newState, err := op.Prepare(before)
   122  	c.Check(err, jc.ErrorIsNil)
   123  	c.Check(newState, gc.DeepEquals, &after)
   124  }
   125  
   126  func (s *RunHookSuite) TestPrepareSuccess_BlankSlate(c *gc.C) {
   127  	for i, newHook := range []newHook{
   128  		(operation.Factory).NewRunHook,
   129  		(operation.Factory).NewRetryHook,
   130  	} {
   131  		c.Logf("variant %d", i)
   132  		s.testPrepareSuccess(c,
   133  			newHook,
   134  			operation.State{},
   135  			operation.State{
   136  				Kind: operation.RunHook,
   137  				Step: operation.Pending,
   138  				Hook: &hook.Info{Kind: hooks.ConfigChanged},
   139  			},
   140  		)
   141  	}
   142  }
   143  
   144  func (s *RunHookSuite) TestPrepareSuccess_Preserve(c *gc.C) {
   145  	for i, newHook := range []newHook{
   146  		(operation.Factory).NewRunHook,
   147  		(operation.Factory).NewRetryHook,
   148  	} {
   149  		c.Logf("variant %d", i)
   150  		s.testPrepareSuccess(c,
   151  			newHook,
   152  			overwriteState,
   153  			operation.State{
   154  				Started:            true,
   155  				CollectMetricsTime: 1234567,
   156  				Kind:               operation.RunHook,
   157  				Step:               operation.Pending,
   158  				Hook:               &hook.Info{Kind: hooks.ConfigChanged},
   159  			},
   160  		)
   161  	}
   162  }
   163  
   164  func (s *RunHookSuite) testExecuteLockError(c *gc.C, newHook newHook) {
   165  	runnerFactory := NewRunHookRunnerFactory(errors.New("should not call"))
   166  	callbacks := &ExecuteHookCallbacks{
   167  		PrepareHookCallbacks:     NewPrepareHookCallbacks(),
   168  		MockAcquireExecutionLock: &MockAcquireExecutionLock{err: errors.New("blart")},
   169  	}
   170  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil)
   171  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	_, err = op.Prepare(operation.State{})
   174  	c.Assert(err, jc.ErrorIsNil)
   175  
   176  	newState, err := op.Execute(operation.State{})
   177  	c.Assert(newState, gc.IsNil)
   178  	c.Assert(err, gc.ErrorMatches, "blart")
   179  	c.Assert(*callbacks.MockAcquireExecutionLock.gotMessage, gc.Equals, "running hook some-hook-name")
   180  }
   181  
   182  func (s *RunHookSuite) TestExecuteLockError_Run(c *gc.C) {
   183  	s.testExecuteLockError(c, (operation.Factory).NewRunHook)
   184  }
   185  
   186  func (s *RunHookSuite) TestExecuteLockError_Retry(c *gc.C) {
   187  	s.testExecuteLockError(c, (operation.Factory).NewRetryHook)
   188  }
   189  
   190  func (s *RunHookSuite) getExecuteRunnerTest(c *gc.C, newHook newHook, runErr error) (operation.Operation, *ExecuteHookCallbacks, *MockRunnerFactory) {
   191  	runnerFactory := NewRunHookRunnerFactory(runErr)
   192  	callbacks := &ExecuteHookCallbacks{
   193  		PrepareHookCallbacks:     NewPrepareHookCallbacks(),
   194  		MockAcquireExecutionLock: &MockAcquireExecutionLock{},
   195  		MockNotifyHookCompleted:  &MockNotify{},
   196  		MockNotifyHookFailed:     &MockNotify{},
   197  	}
   198  	factory := operation.NewFactory(nil, runnerFactory, callbacks, nil)
   199  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	return op, callbacks, runnerFactory
   202  }
   203  
   204  func (s *RunHookSuite) testExecuteMissingHookError(c *gc.C, newHook newHook) {
   205  	runErr := runner.NewMissingHookError("blah-blah")
   206  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, runErr)
   207  	_, err := op.Prepare(operation.State{})
   208  	c.Assert(err, jc.ErrorIsNil)
   209  
   210  	newState, err := op.Execute(operation.State{})
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	c.Assert(newState, gc.DeepEquals, &operation.State{
   213  		Kind: operation.RunHook,
   214  		Step: operation.Done,
   215  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   216  	})
   217  	c.Assert(*callbacks.MockAcquireExecutionLock.gotMessage, gc.Equals, "running hook some-hook-name")
   218  	c.Assert(callbacks.MockAcquireExecutionLock.didUnlock, jc.IsTrue)
   219  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   220  	c.Assert(callbacks.MockNotifyHookCompleted.gotName, gc.IsNil)
   221  	c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   222  }
   223  
   224  func (s *RunHookSuite) TestExecuteMissingHookError_Run(c *gc.C) {
   225  	s.testExecuteMissingHookError(c, (operation.Factory).NewRunHook)
   226  }
   227  
   228  func (s *RunHookSuite) TestExecuteMissingHookError_Retry(c *gc.C) {
   229  	s.testExecuteMissingHookError(c, (operation.Factory).NewRetryHook)
   230  }
   231  
   232  func (s *RunHookSuite) testExecuteRequeueRebootError(c *gc.C, newHook newHook) {
   233  	runErr := runner.ErrRequeueAndReboot
   234  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, runErr)
   235  	_, err := op.Prepare(operation.State{})
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	newState, err := op.Execute(operation.State{})
   239  	c.Assert(err, gc.Equals, operation.ErrNeedsReboot)
   240  	c.Assert(newState, gc.DeepEquals, &operation.State{
   241  		Kind: operation.RunHook,
   242  		Step: operation.Queued,
   243  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   244  	})
   245  	c.Assert(*callbacks.MockAcquireExecutionLock.gotMessage, gc.Equals, "running hook some-hook-name")
   246  	c.Assert(callbacks.MockAcquireExecutionLock.didUnlock, jc.IsTrue)
   247  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   248  	c.Assert(*callbacks.MockNotifyHookCompleted.gotName, gc.Equals, "some-hook-name")
   249  	c.Assert(*callbacks.MockNotifyHookCompleted.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   250  	c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   251  }
   252  
   253  func (s *RunHookSuite) TestExecuteRequeueRebootError_Run(c *gc.C) {
   254  	s.testExecuteRequeueRebootError(c, (operation.Factory).NewRunHook)
   255  }
   256  
   257  func (s *RunHookSuite) TestExecuteRequeueRebootError_Retry(c *gc.C) {
   258  	s.testExecuteRequeueRebootError(c, (operation.Factory).NewRetryHook)
   259  }
   260  
   261  func (s *RunHookSuite) testExecuteRebootError(c *gc.C, newHook newHook) {
   262  	runErr := runner.ErrReboot
   263  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, runErr)
   264  	_, err := op.Prepare(operation.State{})
   265  	c.Assert(err, jc.ErrorIsNil)
   266  
   267  	newState, err := op.Execute(operation.State{})
   268  	c.Assert(err, gc.Equals, operation.ErrNeedsReboot)
   269  	c.Assert(newState, gc.DeepEquals, &operation.State{
   270  		Kind: operation.RunHook,
   271  		Step: operation.Done,
   272  		Hook: &hook.Info{Kind: hooks.ConfigChanged},
   273  	})
   274  	c.Assert(*callbacks.MockAcquireExecutionLock.gotMessage, gc.Equals, "running hook some-hook-name")
   275  	c.Assert(callbacks.MockAcquireExecutionLock.didUnlock, jc.IsTrue)
   276  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   277  	c.Assert(*callbacks.MockNotifyHookCompleted.gotName, gc.Equals, "some-hook-name")
   278  	c.Assert(*callbacks.MockNotifyHookCompleted.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   279  	c.Assert(callbacks.MockNotifyHookFailed.gotName, gc.IsNil)
   280  }
   281  
   282  func (s *RunHookSuite) TestExecuteRebootError_Run(c *gc.C) {
   283  	s.testExecuteRebootError(c, (operation.Factory).NewRunHook)
   284  }
   285  
   286  func (s *RunHookSuite) TestExecuteRebootError_Retry(c *gc.C) {
   287  	s.testExecuteRebootError(c, (operation.Factory).NewRetryHook)
   288  }
   289  
   290  func (s *RunHookSuite) testExecuteOtherError(c *gc.C, newHook newHook) {
   291  	runErr := errors.New("graaargh")
   292  	op, callbacks, runnerFactory := s.getExecuteRunnerTest(c, newHook, runErr)
   293  	_, err := op.Prepare(operation.State{})
   294  	c.Assert(err, jc.ErrorIsNil)
   295  
   296  	newState, err := op.Execute(operation.State{})
   297  	c.Assert(err, gc.Equals, operation.ErrHookFailed)
   298  	c.Assert(newState, gc.IsNil)
   299  	c.Assert(*callbacks.MockAcquireExecutionLock.gotMessage, gc.Equals, "running hook some-hook-name")
   300  	c.Assert(callbacks.MockAcquireExecutionLock.didUnlock, jc.IsTrue)
   301  	c.Assert(*runnerFactory.MockNewHookRunner.runner.MockRunHook.gotName, gc.Equals, "some-hook-name")
   302  	c.Assert(*callbacks.MockNotifyHookFailed.gotName, gc.Equals, "some-hook-name")
   303  	c.Assert(*callbacks.MockNotifyHookFailed.gotContext, gc.Equals, runnerFactory.MockNewHookRunner.runner.context)
   304  	c.Assert(callbacks.MockNotifyHookCompleted.gotName, gc.IsNil)
   305  }
   306  
   307  func (s *RunHookSuite) TestExecuteOtherError_Run(c *gc.C) {
   308  	s.testExecuteOtherError(c, (operation.Factory).NewRunHook)
   309  }
   310  
   311  func (s *RunHookSuite) TestExecuteOtherError_Retry(c *gc.C) {
   312  	s.testExecuteOtherError(c, (operation.Factory).NewRetryHook)
   313  }
   314  
   315  func (s *RunHookSuite) testExecuteSuccess(
   316  	c *gc.C, newHook newHook, before, after operation.State,
   317  ) {
   318  	op, _, _ := s.getExecuteRunnerTest(c, newHook, nil)
   319  	midState, err := op.Prepare(before)
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	c.Assert(midState, gc.NotNil)
   322  
   323  	newState, err := op.Execute(*midState)
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	c.Assert(newState, gc.DeepEquals, &after)
   326  }
   327  
   328  func (s *RunHookSuite) TestExecuteSuccess_BlankSlate(c *gc.C) {
   329  	for i, newHook := range []newHook{
   330  		(operation.Factory).NewRunHook,
   331  		(operation.Factory).NewRetryHook,
   332  	} {
   333  		c.Logf("variant %d", i)
   334  		s.testExecuteSuccess(c,
   335  			newHook,
   336  			operation.State{},
   337  			operation.State{
   338  				Kind: operation.RunHook,
   339  				Step: operation.Done,
   340  				Hook: &hook.Info{Kind: hooks.ConfigChanged},
   341  			},
   342  		)
   343  	}
   344  }
   345  
   346  func (s *RunHookSuite) TestExecuteSuccess_Preserve(c *gc.C) {
   347  	for i, newHook := range []newHook{
   348  		(operation.Factory).NewRunHook,
   349  		(operation.Factory).NewRetryHook,
   350  	} {
   351  		c.Logf("variant %d", i)
   352  		s.testExecuteSuccess(c,
   353  			newHook,
   354  			overwriteState,
   355  			operation.State{
   356  				Started:            true,
   357  				CollectMetricsTime: 1234567,
   358  				Kind:               operation.RunHook,
   359  				Step:               operation.Done,
   360  				Hook:               &hook.Info{Kind: hooks.ConfigChanged},
   361  			},
   362  		)
   363  	}
   364  }
   365  
   366  func (s *RunHookSuite) testCommitError(c *gc.C, newHook newHook) {
   367  	callbacks := &CommitHookCallbacks{
   368  		MockCommitHook: &MockCommitHook{nil, errors.New("pow")},
   369  	}
   370  	factory := operation.NewFactory(nil, nil, callbacks, nil)
   371  	op, err := newHook(factory, hook.Info{Kind: hooks.ConfigChanged})
   372  	c.Assert(err, jc.ErrorIsNil)
   373  
   374  	newState, err := op.Commit(operation.State{})
   375  	c.Assert(newState, gc.IsNil)
   376  	c.Assert(err, gc.ErrorMatches, "pow")
   377  }
   378  
   379  func (s *RunHookSuite) TestCommitError_Run(c *gc.C) {
   380  	s.testCommitError(c, (operation.Factory).NewRunHook)
   381  }
   382  
   383  func (s *RunHookSuite) TestCommitError_Retry(c *gc.C) {
   384  	s.testCommitError(c, (operation.Factory).NewRetryHook)
   385  }
   386  
   387  func (s *RunHookSuite) TestCommitError_Skip(c *gc.C) {
   388  	s.testCommitError(c, (operation.Factory).NewSkipHook)
   389  }
   390  
   391  func (s *RunHookSuite) testCommitSuccess(c *gc.C, newHook newHook, hookInfo hook.Info, before, after operation.State) {
   392  	callbacks := &CommitHookCallbacks{
   393  		MockCommitHook: &MockCommitHook{},
   394  	}
   395  	factory := operation.NewFactory(nil, nil, callbacks, nil)
   396  	op, err := newHook(factory, hookInfo)
   397  	c.Assert(err, jc.ErrorIsNil)
   398  
   399  	newState, err := op.Commit(before)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	c.Assert(newState, gc.DeepEquals, &after)
   402  }
   403  
   404  func (s *RunHookSuite) TestCommitSuccess_ConfigChanged_QueueStartHook(c *gc.C) {
   405  	for i, newHook := range []newHook{
   406  		(operation.Factory).NewRunHook,
   407  		(operation.Factory).NewRetryHook,
   408  		(operation.Factory).NewSkipHook,
   409  	} {
   410  		c.Logf("variant %d", i)
   411  		s.testCommitSuccess(c,
   412  			newHook,
   413  			hook.Info{Kind: hooks.ConfigChanged},
   414  			operation.State{},
   415  			operation.State{
   416  				Kind: operation.RunHook,
   417  				Step: operation.Queued,
   418  				Hook: &hook.Info{Kind: hooks.Start},
   419  			},
   420  		)
   421  	}
   422  }
   423  
   424  func (s *RunHookSuite) TestCommitSuccess_ConfigChanged_Preserve(c *gc.C) {
   425  	for i, newHook := range []newHook{
   426  		(operation.Factory).NewRunHook,
   427  		(operation.Factory).NewRetryHook,
   428  		(operation.Factory).NewSkipHook,
   429  	} {
   430  		c.Logf("variant %d", i)
   431  		s.testCommitSuccess(c,
   432  			newHook,
   433  			hook.Info{Kind: hooks.ConfigChanged},
   434  			overwriteState,
   435  			operation.State{
   436  				Started:            true,
   437  				CollectMetricsTime: 1234567,
   438  				Kind:               operation.Continue,
   439  				Step:               operation.Pending,
   440  				Hook:               &hook.Info{Kind: hooks.ConfigChanged},
   441  			},
   442  		)
   443  	}
   444  }
   445  
   446  func (s *RunHookSuite) TestCommitSuccess_Start_SetStarted(c *gc.C) {
   447  	for i, newHook := range []newHook{
   448  		(operation.Factory).NewRunHook,
   449  		(operation.Factory).NewRetryHook,
   450  		(operation.Factory).NewSkipHook,
   451  	} {
   452  		c.Logf("variant %d", i)
   453  		s.testCommitSuccess(c,
   454  			newHook,
   455  			hook.Info{Kind: hooks.Start},
   456  			operation.State{},
   457  			operation.State{
   458  				Started: true,
   459  				Kind:    operation.Continue,
   460  				Step:    operation.Pending,
   461  				Hook:    &hook.Info{Kind: hooks.Start},
   462  			},
   463  		)
   464  	}
   465  }
   466  
   467  func (s *RunHookSuite) TestCommitSuccess_Start_Preserve(c *gc.C) {
   468  	for i, newHook := range []newHook{
   469  		(operation.Factory).NewRunHook,
   470  		(operation.Factory).NewRetryHook,
   471  		(operation.Factory).NewSkipHook,
   472  	} {
   473  		c.Logf("variant %d", i)
   474  		s.testCommitSuccess(c,
   475  			newHook,
   476  			hook.Info{Kind: hooks.Start},
   477  			overwriteState,
   478  			operation.State{
   479  				Started:            true,
   480  				CollectMetricsTime: 1234567,
   481  				Kind:               operation.Continue,
   482  				Step:               operation.Pending,
   483  				Hook:               &hook.Info{Kind: hooks.Start},
   484  			},
   485  		)
   486  	}
   487  }
   488  
   489  func (s *RunHookSuite) testQueueHook_BlankSlate(c *gc.C, cause, effect hooks.Kind) {
   490  	for i, newHook := range []newHook{
   491  		(operation.Factory).NewRunHook,
   492  		(operation.Factory).NewRetryHook,
   493  		(operation.Factory).NewSkipHook,
   494  	} {
   495  		c.Logf("variant %d", i)
   496  		s.testCommitSuccess(c,
   497  			newHook,
   498  			hook.Info{Kind: cause},
   499  			operation.State{},
   500  			operation.State{
   501  				Kind: operation.RunHook,
   502  				Step: operation.Queued,
   503  				Hook: &hook.Info{Kind: effect},
   504  			},
   505  		)
   506  	}
   507  }
   508  
   509  func (s *RunHookSuite) testQueueHook_Preserve(c *gc.C, cause, effect hooks.Kind) {
   510  	for i, newHook := range []newHook{
   511  		(operation.Factory).NewRunHook,
   512  		(operation.Factory).NewRetryHook,
   513  		(operation.Factory).NewSkipHook,
   514  	} {
   515  		c.Logf("variant %d", i)
   516  		s.testCommitSuccess(c,
   517  			newHook,
   518  			hook.Info{Kind: cause},
   519  			overwriteState,
   520  			operation.State{
   521  				Kind:               operation.RunHook,
   522  				Step:               operation.Queued,
   523  				Hook:               &hook.Info{Kind: effect},
   524  				Started:            true,
   525  				CollectMetricsTime: 1234567,
   526  			},
   527  		)
   528  	}
   529  }
   530  
   531  func (s *RunHookSuite) TestQueueHook_Install_BlankSlate(c *gc.C) {
   532  	s.testQueueHook_BlankSlate(c, hooks.Install, hooks.ConfigChanged)
   533  }
   534  
   535  func (s *RunHookSuite) TestQueueHook_Install_Preserve(c *gc.C) {
   536  	s.testQueueHook_Preserve(c, hooks.Install, hooks.ConfigChanged)
   537  }
   538  
   539  func (s *RunHookSuite) TestQueueHook_UpgradeCharm_BlankSlate(c *gc.C) {
   540  	s.testQueueHook_BlankSlate(c, hooks.UpgradeCharm, hooks.ConfigChanged)
   541  }
   542  
   543  func (s *RunHookSuite) TestQueueHook_UpgradeCharm_Preserve(c *gc.C) {
   544  	s.testQueueHook_Preserve(c, hooks.UpgradeCharm, hooks.ConfigChanged)
   545  }
   546  
   547  func (s *RunHookSuite) testQueueNothing_BlankSlate(c *gc.C, hookInfo hook.Info) {
   548  	for i, newHook := range []newHook{
   549  		(operation.Factory).NewRunHook,
   550  		(operation.Factory).NewRetryHook,
   551  		(operation.Factory).NewSkipHook,
   552  	} {
   553  		c.Logf("variant %d", i)
   554  		s.testCommitSuccess(c,
   555  			newHook,
   556  			hookInfo,
   557  			operation.State{},
   558  			operation.State{
   559  				Kind: operation.Continue,
   560  				Step: operation.Pending,
   561  				Hook: &hookInfo,
   562  			},
   563  		)
   564  	}
   565  }
   566  
   567  func (s *RunHookSuite) testQueueNothing_Preserve(c *gc.C, hookInfo hook.Info) {
   568  	for i, newHook := range []newHook{
   569  		(operation.Factory).NewRunHook,
   570  		(operation.Factory).NewRetryHook,
   571  		(operation.Factory).NewSkipHook,
   572  	} {
   573  		c.Logf("variant %d", i)
   574  		s.testCommitSuccess(c,
   575  			newHook,
   576  			hookInfo,
   577  			overwriteState,
   578  			operation.State{
   579  				Kind:               operation.Continue,
   580  				Step:               operation.Pending,
   581  				Hook:               &hookInfo,
   582  				Started:            true,
   583  				CollectMetricsTime: 1234567,
   584  			},
   585  		)
   586  	}
   587  }
   588  
   589  func (s *RunHookSuite) TestQueueNothing_Stop_BlankSlate(c *gc.C) {
   590  	s.testQueueNothing_BlankSlate(c, hook.Info{
   591  		Kind: hooks.Stop,
   592  	})
   593  }
   594  
   595  func (s *RunHookSuite) TestQueueNothing_Stop_Preserve(c *gc.C) {
   596  	s.testQueueNothing_Preserve(c, hook.Info{
   597  		Kind: hooks.Stop,
   598  	})
   599  }
   600  
   601  func (s *RunHookSuite) TestQueueNothing_RelationJoined_BlankSlate(c *gc.C) {
   602  	s.testQueueNothing_BlankSlate(c, hook.Info{
   603  		Kind:       hooks.RelationJoined,
   604  		RemoteUnit: "u/0",
   605  	})
   606  }
   607  
   608  func (s *RunHookSuite) TestQueueNothing_RelationJoined_Preserve(c *gc.C) {
   609  	s.testQueueNothing_Preserve(c, hook.Info{
   610  		Kind:       hooks.RelationJoined,
   611  		RemoteUnit: "u/0",
   612  	})
   613  }
   614  
   615  func (s *RunHookSuite) TestQueueNothing_RelationChanged_BlankSlate(c *gc.C) {
   616  	s.testQueueNothing_BlankSlate(c, hook.Info{
   617  		Kind:       hooks.RelationChanged,
   618  		RemoteUnit: "u/0",
   619  	})
   620  }
   621  
   622  func (s *RunHookSuite) TestQueueNothing_RelationChanged_Preserve(c *gc.C) {
   623  	s.testQueueNothing_Preserve(c, hook.Info{
   624  		Kind:       hooks.RelationChanged,
   625  		RemoteUnit: "u/0",
   626  	})
   627  }
   628  
   629  func (s *RunHookSuite) TestQueueNothing_RelationDeparted_BlankSlate(c *gc.C) {
   630  	s.testQueueNothing_BlankSlate(c, hook.Info{
   631  		Kind:       hooks.RelationDeparted,
   632  		RemoteUnit: "u/0",
   633  	})
   634  }
   635  
   636  func (s *RunHookSuite) TestQueueNothing_RelationDeparted_Preserve(c *gc.C) {
   637  	s.testQueueNothing_Preserve(c, hook.Info{
   638  		Kind:       hooks.RelationDeparted,
   639  		RemoteUnit: "u/0",
   640  	})
   641  }
   642  
   643  func (s *RunHookSuite) TestQueueNothing_RelationBroken_BlankSlate(c *gc.C) {
   644  	s.testQueueNothing_BlankSlate(c, hook.Info{
   645  		Kind: hooks.RelationBroken,
   646  	})
   647  }
   648  
   649  func (s *RunHookSuite) TestQueueNothing_RelationBroken_Preserve(c *gc.C) {
   650  	s.testQueueNothing_Preserve(c, hook.Info{
   651  		Kind: hooks.RelationBroken,
   652  	})
   653  }
   654  
   655  func (s *RunHookSuite) testCommitSuccess_CollectMetricsTime(c *gc.C, newHook newHook) {
   656  	hookInfo := hook.Info{Kind: hooks.CollectMetrics}
   657  
   658  	callbacks := &CommitHookCallbacks{
   659  		MockCommitHook: &MockCommitHook{},
   660  	}
   661  	factory := operation.NewFactory(nil, nil, callbacks, nil)
   662  	op, err := newHook(factory, hookInfo)
   663  	c.Assert(err, jc.ErrorIsNil)
   664  
   665  	nowBefore := time.Now().Unix()
   666  	newState, err := op.Commit(overwriteState)
   667  	c.Assert(err, jc.ErrorIsNil)
   668  
   669  	nowAfter := time.Now().Unix()
   670  	nowWritten := newState.CollectMetricsTime
   671  	c.Logf("%d <= %d <= %d", nowBefore, nowWritten, nowAfter)
   672  	c.Check(nowBefore <= nowWritten, jc.IsTrue)
   673  	c.Check(nowWritten <= nowAfter, jc.IsTrue)
   674  
   675  	// Check the other fields match.
   676  	newState.CollectMetricsTime = 0
   677  	c.Check(newState, gc.DeepEquals, &operation.State{
   678  		Started: true,
   679  		Kind:    operation.Continue,
   680  		Step:    operation.Pending,
   681  		Hook:    &hookInfo,
   682  	})
   683  }
   684  
   685  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Run(c *gc.C) {
   686  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewRunHook)
   687  }
   688  
   689  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Retry(c *gc.C) {
   690  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewRetryHook)
   691  }
   692  
   693  func (s *RunHookSuite) TestCommitSuccess_CollectMetricsTime_Skip(c *gc.C) {
   694  	s.testCommitSuccess_CollectMetricsTime(c, (operation.Factory).NewSkipHook)
   695  }