github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/state/testing/watcher.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "time" 8 9 jc "github.com/juju/testing/checkers" 10 "github.com/juju/utils/set" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/state" 14 "github.com/juju/juju/state/multiwatcher" 15 "github.com/juju/juju/testing" 16 ) 17 18 type Stopper interface { 19 Stop() error 20 } 21 22 func AssertStop(c *gc.C, stopper Stopper) { 23 c.Assert(stopper.Stop(), gc.IsNil) 24 } 25 26 type KillWaiter interface { 27 Kill() 28 Wait() error 29 } 30 31 func AssertKillAndWait(c *gc.C, killWaiter KillWaiter) { 32 killWaiter.Kill() 33 c.Assert(killWaiter.Wait(), gc.IsNil) 34 } 35 36 // AssertCanStopWhenSending ensures even when there are changes 37 // pending to be delivered by the watcher it can still stop 38 // cleanly. This is necessary to check for deadlocks in case the 39 // watcher's inner loop is blocked trying to send and its tomb is 40 // already dying. 41 func AssertCanStopWhenSending(c *gc.C, stopper Stopper) { 42 // Leave some time for the event to be delivered and the watcher 43 // to block on sending it. 44 <-time.After(testing.ShortWait) 45 stopped := make(chan bool) 46 // Stop() blocks, so we need to call it in a separate goroutine. 47 go func() { 48 c.Check(stopper.Stop(), gc.IsNil) 49 stopped <- true 50 }() 51 select { 52 case <-time.After(testing.LongWait): 53 // NOTE: If this test fails here it means we have a deadlock 54 // in the client-side watcher implementation. 55 c.Fatalf("watcher did not stop as expected") 56 case <-stopped: 57 } 58 } 59 60 type NotifyWatcher interface { 61 Stop() error 62 Changes() <-chan struct{} 63 } 64 65 // NotifyWatcherC embeds a gocheck.C and adds methods to help verify 66 // the behaviour of any watcher that uses a <-chan struct{}. 67 type NotifyWatcherC struct { 68 *gc.C 69 State *state.State 70 Watcher NotifyWatcher 71 } 72 73 // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive 74 // event coalescence. 75 func NewNotifyWatcherC(c *gc.C, st *state.State, w NotifyWatcher) NotifyWatcherC { 76 return NotifyWatcherC{ 77 C: c, 78 State: st, 79 Watcher: w, 80 } 81 } 82 83 func (c NotifyWatcherC) AssertNoChange() { 84 c.State.StartSync() 85 select { 86 case _, ok := <-c.Watcher.Changes(): 87 c.Fatalf("watcher sent unexpected change: (_, %v)", ok) 88 case <-time.After(testing.ShortWait): 89 } 90 } 91 92 func (c NotifyWatcherC) AssertOneChange() { 93 c.State.StartSync() 94 select { 95 case _, ok := <-c.Watcher.Changes(): 96 c.Assert(ok, jc.IsTrue) 97 case <-time.After(testing.LongWait): 98 c.Fatalf("watcher did not send change") 99 } 100 c.AssertNoChange() 101 } 102 103 func (c NotifyWatcherC) AssertClosed() { 104 select { 105 case _, ok := <-c.Watcher.Changes(): 106 c.Assert(ok, jc.IsFalse) 107 default: 108 c.Fatalf("watcher not closed") 109 } 110 } 111 112 // StringsWatcherC embeds a gocheck.C and adds methods to help verify 113 // the behaviour of any watcher that uses a <-chan []string. 114 type StringsWatcherC struct { 115 *gc.C 116 State *state.State 117 Watcher StringsWatcher 118 } 119 120 // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive 121 // event coalescence. 122 func NewStringsWatcherC(c *gc.C, st *state.State, w StringsWatcher) StringsWatcherC { 123 return StringsWatcherC{ 124 C: c, 125 State: st, 126 Watcher: w, 127 } 128 } 129 130 type StringsWatcher interface { 131 Stop() error 132 Changes() <-chan []string 133 } 134 135 func (c StringsWatcherC) AssertNoChange() { 136 c.State.StartSync() 137 select { 138 case actual, ok := <-c.Watcher.Changes(): 139 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 140 case <-time.After(testing.ShortWait): 141 } 142 } 143 144 func (c StringsWatcherC) AssertChanges() { 145 c.State.StartSync() 146 select { 147 case <-c.Watcher.Changes(): 148 case <-time.After(testing.LongWait): 149 c.Fatalf("watcher did not send change") 150 } 151 } 152 153 func (c StringsWatcherC) AssertChange(expect ...string) { 154 c.assertChange(false, expect...) 155 } 156 157 func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) { 158 c.assertChange(true, expect...) 159 } 160 161 // AssertChangeMaybeIncluding verifies that there is a change that may 162 // contain zero to all of the passed in strings, and no other changes. 163 func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) { 164 maxCount := len(expect) 165 actual := c.collectChanges(true, maxCount) 166 167 if maxCount == 0 { 168 c.Assert(actual, gc.HasLen, 0) 169 } else { 170 actualCount := len(actual) 171 c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount)) 172 unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...)) 173 c.Assert(unexpected.Values(), gc.HasLen, 0) 174 } 175 } 176 177 // assertChange asserts the given list of changes was reported by 178 // the watcher, but does not assume there are no following changes. 179 func (c StringsWatcherC) assertChange(single bool, expect ...string) { 180 actual := c.collectChanges(single, len(expect)) 181 if len(expect) == 0 { 182 c.Assert(actual, gc.HasLen, 0) 183 } else { 184 c.Assert(actual, jc.SameContents, expect) 185 } 186 } 187 188 // collectChanges gets up to the max number of changes within the 189 // testing.LongWait period. 190 func (c StringsWatcherC) collectChanges(single bool, max int) []string { 191 c.State.StartSync() 192 timeout := time.After(testing.LongWait) 193 var actual []string 194 gotOneChange := false 195 loop: 196 for { 197 select { 198 case changes, ok := <-c.Watcher.Changes(): 199 c.Assert(ok, jc.IsTrue) 200 gotOneChange = true 201 actual = append(actual, changes...) 202 if single || len(actual) >= max { 203 break loop 204 } 205 case <-timeout: 206 if !gotOneChange { 207 c.Fatalf("watcher did not send change") 208 } 209 } 210 } 211 return actual 212 } 213 214 func (c StringsWatcherC) AssertClosed() { 215 select { 216 case _, ok := <-c.Watcher.Changes(): 217 c.Assert(ok, jc.IsFalse) 218 default: 219 c.Fatalf("watcher not closed") 220 } 221 } 222 223 // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help 224 // verify the behaviour of any watcher that uses a <-chan 225 // params.RelationUnitsChange. 226 type RelationUnitsWatcherC struct { 227 *gc.C 228 State *state.State 229 Watcher RelationUnitsWatcher 230 // settingsVersions keeps track of the settings version of each 231 // changed unit since the last received changes to ensure version 232 // always increases. 233 settingsVersions map[string]int64 234 } 235 236 // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that 237 // checks for aggressive event coalescence. 238 func NewRelationUnitsWatcherC(c *gc.C, st *state.State, w RelationUnitsWatcher) RelationUnitsWatcherC { 239 return RelationUnitsWatcherC{ 240 C: c, 241 State: st, 242 Watcher: w, 243 settingsVersions: make(map[string]int64), 244 } 245 } 246 247 type RelationUnitsWatcher interface { 248 Stop() error 249 Changes() <-chan multiwatcher.RelationUnitsChange 250 } 251 252 func (c RelationUnitsWatcherC) AssertNoChange() { 253 c.State.StartSync() 254 select { 255 case actual, ok := <-c.Watcher.Changes(): 256 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 257 case <-time.After(testing.ShortWait): 258 } 259 } 260 261 // AssertChange asserts the given changes was reported by the watcher, 262 // but does not assume there are no following changes. 263 func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) { 264 // Get all items in changed in a map for easy lookup. 265 changedNames := make(map[string]bool) 266 for _, name := range changed { 267 changedNames[name] = true 268 } 269 c.State.StartSync() 270 timeout := time.After(testing.LongWait) 271 select { 272 case actual, ok := <-c.Watcher.Changes(): 273 c.Assert(ok, jc.IsTrue) 274 c.Assert(actual.Changed, gc.HasLen, len(changed)) 275 // Because the versions can change, we only need to make sure 276 // the keys match, not the contents (UnitSettings == txnRevno). 277 for k, settings := range actual.Changed { 278 _, ok := changedNames[k] 279 c.Assert(ok, jc.IsTrue) 280 oldVer, ok := c.settingsVersions[k] 281 if !ok { 282 // This is the first time we see this unit, so 283 // save the settings version for later. 284 c.settingsVersions[k] = settings.Version 285 } else { 286 // Already seen; make sure the version increased. 287 if settings.Version <= oldVer { 288 c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version) 289 } 290 } 291 } 292 c.Assert(actual.Departed, jc.SameContents, departed) 293 case <-timeout: 294 c.Fatalf("watcher did not send change") 295 } 296 } 297 298 func (c RelationUnitsWatcherC) AssertClosed() { 299 select { 300 case _, ok := <-c.Watcher.Changes(): 301 c.Assert(ok, jc.IsFalse) 302 default: 303 c.Fatalf("watcher not closed") 304 } 305 }