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