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