github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/uniter/relation/hookqueue_test.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation_test
     5  
     6  import (
     7  	"time"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/charm.v5/hooks"
    12  
    13  	"github.com/juju/juju/state/multiwatcher"
    14  	coretesting "github.com/juju/juju/testing"
    15  	"github.com/juju/juju/worker/uniter/hook"
    16  	"github.com/juju/juju/worker/uniter/relation"
    17  )
    18  
    19  type HookQueueSuite struct{}
    20  
    21  var _ = gc.Suite(&HookQueueSuite{})
    22  
    23  type msi map[string]int64
    24  
    25  type hookQueueTest struct {
    26  	summary string
    27  	initial *relation.State
    28  	steps   []checker
    29  }
    30  
    31  func fullTest(summary string, steps ...checker) hookQueueTest {
    32  	return hookQueueTest{summary, &relation.State{21345, nil, ""}, steps}
    33  }
    34  
    35  func reconcileTest(summary string, members msi, joined string, steps ...checker) hookQueueTest {
    36  	return hookQueueTest{summary, &relation.State{21345, members, joined}, steps}
    37  }
    38  
    39  var aliveHookQueueTests = []hookQueueTest{
    40  	fullTest(
    41  		"Empty initial change causes no hooks.",
    42  		send{nil, nil},
    43  	), fullTest(
    44  		"Joined and changed are both run when unit is first detected.",
    45  		send{msi{"u/0": 0}, nil},
    46  		expect{hooks.RelationJoined, "u/0", 0},
    47  		expect{hooks.RelationChanged, "u/0", 0},
    48  	), fullTest(
    49  		"Automatic changed is run with latest settings.",
    50  		send{msi{"u/0": 0}, nil},
    51  		expect{hooks.RelationJoined, "u/0", 0},
    52  		send{msi{"u/0": 7}, nil},
    53  		expect{hooks.RelationChanged, "u/0", 7},
    54  	), fullTest(
    55  		"Joined is also run with latest settings.",
    56  		send{msi{"u/0": 0}, nil},
    57  		send{msi{"u/0": 7}, nil},
    58  		expect{hooks.RelationJoined, "u/0", 7},
    59  		expect{hooks.RelationChanged, "u/0", 7},
    60  	), fullTest(
    61  		"Nothing happens if a unit departs before its joined is run.",
    62  		send{msi{"u/0": 0}, nil},
    63  		send{msi{"u/0": 7}, nil},
    64  		send{nil, []string{"u/0"}},
    65  	), fullTest(
    66  		"A changed is run after a joined, even if a departed is known.",
    67  		send{msi{"u/0": 0}, nil},
    68  		expect{hooks.RelationJoined, "u/0", 0},
    69  		send{nil, []string{"u/0"}},
    70  		expect{hooks.RelationChanged, "u/0", 0},
    71  		expect{hooks.RelationDeparted, "u/0", 0},
    72  	), fullTest(
    73  		"A departed replaces a changed.",
    74  		send{msi{"u/0": 0}, nil},
    75  		advance{2},
    76  		send{msi{"u/0": 7}, nil},
    77  		send{nil, []string{"u/0"}},
    78  		expect{hooks.RelationDeparted, "u/0", 7},
    79  	), fullTest(
    80  		"Changed events are ignored if the version has not changed.",
    81  		send{msi{"u/0": 0}, nil},
    82  		advance{2},
    83  		send{msi{"u/0": 0}, nil},
    84  	), fullTest(
    85  		"Redundant changed events are elided.",
    86  		send{msi{"u/0": 0}, nil},
    87  		advance{2},
    88  		send{msi{"u/0": 3}, nil},
    89  		send{msi{"u/0": 7}, nil},
    90  		send{msi{"u/0": 79}, nil},
    91  		expect{hooks.RelationChanged, "u/0", 79},
    92  	), fullTest(
    93  		"Latest hooks are run in the original unit order.",
    94  		send{msi{"u/0": 0, "u/1": 1}, nil},
    95  		advance{4},
    96  		send{msi{"u/0": 3}, nil},
    97  		send{msi{"u/1": 7}, nil},
    98  		send{nil, []string{"u/0"}},
    99  		expect{hooks.RelationDeparted, "u/0", 3},
   100  		expect{hooks.RelationChanged, "u/1", 7},
   101  	), fullTest(
   102  		"Test everything we can think of at the same time.",
   103  		send{msi{"u/0": 0, "u/1": 0, "u/2": 0, "u/3": 0, "u/4": 0}, nil},
   104  		advance{6},
   105  		// u/0, u/1, u/2 are now up to date; u/3, u/4 are untouched.
   106  		send{msi{"u/0": 1}, nil},
   107  		send{msi{"u/1": 1, "u/2": 1, "u/3": 1, "u/5": 0}, []string{"u/0", "u/4"}},
   108  		send{msi{"u/3": 2}, nil},
   109  		// - Finish off the rest of the initial state, ignoring u/4, but using
   110  		// the latest known settings.
   111  		expect{hooks.RelationJoined, "u/3", 2},
   112  		expect{hooks.RelationChanged, "u/3", 2},
   113  		// - u/0 was queued for change by the first RUC, but this change is
   114  		// no longer relevant; it's departed in the second RUC, so we run
   115  		// that hook instead.
   116  		expect{hooks.RelationDeparted, "u/0", 1},
   117  		// - Handle the remaining changes in the second RUC, still ignoring u/4.
   118  		// We do run new changed hooks for u/1 and u/2, because the latest settings
   119  		// are newer than those used in their original changed events.
   120  		expect{hooks.RelationChanged, "u/1", 1},
   121  		expect{hooks.RelationChanged, "u/2", 1},
   122  		expect{hooks.RelationJoined, "u/5", 0},
   123  		expect{hooks.RelationChanged, "u/5", 0},
   124  		// - Ignore the third RUC, because the original joined/changed on u/3
   125  		// was executed after we got the latest settings version.
   126  	), reconcileTest(
   127  		"Check that matching settings versions cause no changes.",
   128  		msi{"u/0": 0}, "",
   129  		send{msi{"u/0": 0}, nil},
   130  	), reconcileTest(
   131  		"Check that new settings versions cause appropriate changes.",
   132  		msi{"u/0": 0}, "",
   133  		send{msi{"u/0": 1}, nil},
   134  		expect{hooks.RelationChanged, "u/0", 1},
   135  	), reconcileTest(
   136  		"Check that a just-joined unit gets its changed hook run first.",
   137  		msi{"u/0": 0}, "u/0",
   138  		send{msi{"u/0": 0}, nil},
   139  		expect{hooks.RelationChanged, "u/0", 0},
   140  	), reconcileTest(
   141  		"Check that missing units are queued for depart as early as possible.",
   142  		msi{"u/0": 0}, "",
   143  		send{msi{"u/1": 0}, nil},
   144  		expect{hooks.RelationDeparted, "u/0", 0},
   145  		expect{hooks.RelationJoined, "u/1", 0},
   146  		expect{hooks.RelationChanged, "u/1", 0},
   147  	), reconcileTest(
   148  		"Double-check that a pending changed happens before an injected departed.",
   149  		msi{"u/0": 0}, "u/0",
   150  		send{nil, nil},
   151  		expect{hooks.RelationChanged, "u/0", 0},
   152  		expect{hooks.RelationDeparted, "u/0", 0},
   153  	), reconcileTest(
   154  		"Check that missing units don't slip in front of required changed hooks.",
   155  		msi{"u/0": 0}, "u/0",
   156  		send{msi{"u/1": 0}, nil},
   157  		expect{hooks.RelationChanged, "u/0", 0},
   158  		expect{hooks.RelationDeparted, "u/0", 0},
   159  		expect{hooks.RelationJoined, "u/1", 0},
   160  		expect{hooks.RelationChanged, "u/1", 0},
   161  	),
   162  }
   163  
   164  func (s *HookQueueSuite) TestAliveHookQueue(c *gc.C) {
   165  	for i, t := range aliveHookQueueTests[4:5] {
   166  		c.Logf("test %d: %s", i, t.summary)
   167  		out := make(chan hook.Info)
   168  		in := make(chan multiwatcher.RelationUnitsChange)
   169  		ruw := &RUW{in, false}
   170  		q := relation.NewAliveHookQueue(t.initial, out, ruw)
   171  		for i, step := range t.steps {
   172  			c.Logf("  step %d", i)
   173  			step.check(c, in, out)
   174  		}
   175  		expect{}.check(c, in, out)
   176  		q.Stop()
   177  		c.Assert(ruw.stopped, jc.IsTrue)
   178  	}
   179  }
   180  
   181  var dyingHookQueueTests = []hookQueueTest{
   182  	fullTest(
   183  		"Empty state just gets a broken hook.",
   184  		expect{hook: hooks.RelationBroken},
   185  	), reconcileTest(
   186  		"Each current member is departed before broken is sent.",
   187  		msi{"u/1": 7, "u/4": 33}, "",
   188  		expect{hooks.RelationDeparted, "u/1", 7},
   189  		expect{hooks.RelationDeparted, "u/4", 33},
   190  		expect{hook: hooks.RelationBroken},
   191  	), reconcileTest(
   192  		"If there's a pending changed, that must still be respected.",
   193  		msi{"u/0": 3}, "u/0",
   194  		expect{hooks.RelationChanged, "u/0", 3},
   195  		expect{hooks.RelationDeparted, "u/0", 3},
   196  		expect{hook: hooks.RelationBroken},
   197  	),
   198  }
   199  
   200  func (s *HookQueueSuite) TestDyingHookQueue(c *gc.C) {
   201  	for i, t := range dyingHookQueueTests {
   202  		c.Logf("test %d: %s", i, t.summary)
   203  		out := make(chan hook.Info)
   204  		q := relation.NewDyingHookQueue(t.initial, out)
   205  		for i, step := range t.steps {
   206  			c.Logf("  step %d", i)
   207  			step.check(c, nil, out)
   208  		}
   209  		expect{}.check(c, nil, out)
   210  		q.Stop()
   211  	}
   212  }
   213  
   214  // RUW exists entirely to send RelationUnitsChanged events to a tested
   215  // HookQueue in a synchronous and predictable fashion.
   216  type RUW struct {
   217  	in      chan multiwatcher.RelationUnitsChange
   218  	stopped bool
   219  }
   220  
   221  func (w *RUW) Changes() <-chan multiwatcher.RelationUnitsChange {
   222  	return w.in
   223  }
   224  
   225  func (w *RUW) Stop() error {
   226  	close(w.in)
   227  	w.stopped = true
   228  	return nil
   229  }
   230  
   231  func (w *RUW) Err() error {
   232  	return nil
   233  }
   234  
   235  type checker interface {
   236  	check(c *gc.C, in chan multiwatcher.RelationUnitsChange, out chan hook.Info)
   237  	checkDirect(c *gc.C, q hook.Source)
   238  }
   239  
   240  type send struct {
   241  	changed  msi
   242  	departed []string
   243  }
   244  
   245  func (d send) event() multiwatcher.RelationUnitsChange {
   246  	ruc := multiwatcher.RelationUnitsChange{Changed: map[string]multiwatcher.UnitSettings{}}
   247  	for name, version := range d.changed {
   248  		ruc.Changed[name] = multiwatcher.UnitSettings{Version: version}
   249  	}
   250  	for _, name := range d.departed {
   251  		ruc.Departed = append(ruc.Departed, name)
   252  	}
   253  	return ruc
   254  }
   255  
   256  func (d send) check(c *gc.C, in chan multiwatcher.RelationUnitsChange, out chan hook.Info) {
   257  	in <- d.event()
   258  }
   259  
   260  func (d send) checkDirect(c *gc.C, q hook.Source) {
   261  	q.(interface {
   262  		Update(change multiwatcher.RelationUnitsChange) error
   263  	}).Update(d.event())
   264  }
   265  
   266  type advance struct {
   267  	count int
   268  }
   269  
   270  func (d advance) check(c *gc.C, in chan multiwatcher.RelationUnitsChange, out chan hook.Info) {
   271  	for i := 0; i < d.count; i++ {
   272  		select {
   273  		case <-out:
   274  		case <-time.After(coretesting.LongWait):
   275  			c.Fatalf("timed out waiting for event %d", i)
   276  		}
   277  	}
   278  }
   279  
   280  func (d advance) checkDirect(c *gc.C, q hook.Source) {
   281  	for i := 0; i < d.count; i++ {
   282  		c.Assert(q.Empty(), jc.IsFalse)
   283  		q.Pop()
   284  	}
   285  }
   286  
   287  type expect struct {
   288  	hook    hooks.Kind
   289  	unit    string
   290  	version int64
   291  }
   292  
   293  func (d expect) info() hook.Info {
   294  	return hook.Info{
   295  		Kind:          d.hook,
   296  		RelationId:    21345,
   297  		RemoteUnit:    d.unit,
   298  		ChangeVersion: d.version,
   299  	}
   300  }
   301  
   302  func (d expect) check(c *gc.C, in chan multiwatcher.RelationUnitsChange, out chan hook.Info) {
   303  	if d.hook == "" {
   304  		select {
   305  		case unexpected := <-out:
   306  			c.Fatalf("got %#v", unexpected)
   307  		case <-time.After(coretesting.ShortWait):
   308  		}
   309  		return
   310  	}
   311  	select {
   312  	case actual := <-out:
   313  		c.Assert(actual, jc.DeepEquals, d.info())
   314  	case <-time.After(coretesting.LongWait):
   315  		c.Fatalf("timed out waiting for %#v", d.info())
   316  	}
   317  }
   318  
   319  func (d expect) checkDirect(c *gc.C, q hook.Source) {
   320  	if d.hook == "" {
   321  		c.Check(q.Empty(), jc.IsTrue)
   322  	} else {
   323  		c.Check(q.Empty(), jc.IsFalse)
   324  		c.Check(q.Next(), jc.DeepEquals, d.info())
   325  		q.Pop()
   326  	}
   327  }