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