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 }