github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/watcher/hubwatcher_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package watcher_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/loggo" 11 "github.com/juju/pubsub" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/worker.v1" 15 "gopkg.in/tomb.v2" 16 17 "github.com/juju/juju/state/watcher" 18 "github.com/juju/juju/testing" 19 ) 20 21 type HubWatcherSuite struct { 22 testing.BaseSuite 23 24 w watcher.BaseWatcher 25 hub *pubsub.SimpleHub 26 ch chan watcher.Change 27 } 28 29 var _ = gc.Suite(&HubWatcherSuite{}) 30 31 func (s *HubWatcherSuite) SetUpTest(c *gc.C) { 32 s.BaseSuite.SetUpTest(c) 33 34 logger := loggo.GetLogger("HubWatcherSuite") 35 logger.SetLogLevel(loggo.TRACE) 36 37 s.hub = pubsub.NewSimpleHub(nil) 38 s.ch = make(chan watcher.Change) 39 var started <-chan struct{} 40 s.w, started = watcher.NewTestHubWatcher(s.hub, clock.WallClock, "model-uuid", logger) 41 s.AddCleanup(func(c *gc.C) { 42 worker.Stop(s.w) 43 }) 44 select { 45 case <-started: 46 // all good 47 case <-time.After(testing.LongWait): 48 c.Error("hub watcher worker didn't start") 49 } 50 } 51 52 func (s *HubWatcherSuite) publish(c *gc.C, changes ...watcher.Change) { 53 var processed <-chan struct{} 54 for _, change := range changes { 55 processed = s.hub.Publish(watcher.TxnWatcherCollection, change) 56 } 57 select { 58 case <-processed: 59 // all good. 60 case <-time.After(testing.LongWait): 61 c.Error("event not processed") 62 } 63 64 } 65 66 func (s *HubWatcherSuite) TestErrAndDead(c *gc.C) { 67 c.Assert(s.w.Err(), gc.Equals, tomb.ErrStillAlive) 68 select { 69 case <-s.w.Dead(): 70 c.Fatalf("Dead channel fired unexpectedly") 71 default: 72 } 73 c.Assert(worker.Stop(s.w), jc.ErrorIsNil) 74 select { 75 case <-s.w.Dead(): 76 default: 77 c.Fatalf("Dead channel should have fired") 78 } 79 } 80 81 func (s *HubWatcherSuite) TestTxnWatcherSyncErrWorker(c *gc.C) { 82 // When the TxnWatcher hits a sync error and restarts, the hub watcher needs 83 // to restart too as there may be missed events, so all the watches this hub 84 // has need to be invalidated. This happens by the worker dying. 85 s.hub.Publish(watcher.TxnWatcherSyncErr, nil) 86 87 select { 88 case <-s.w.Dead(): 89 case <-time.After(testing.LongWait): 90 c.Fatalf("Dead channel should have fired") 91 } 92 93 c.Assert(s.w.Err(), gc.ErrorMatches, "txn watcher sync error") 94 } 95 96 func (s *HubWatcherSuite) TestWatchBeforeKnown(c *gc.C) { 97 s.w.Watch("test", "a", s.ch) 98 assertNoChange(c, s.ch) 99 100 change := watcher.Change{"test", "a", 5} 101 s.publish(c, change) 102 103 assertChange(c, s.ch, change) 104 assertNoChange(c, s.ch) 105 } 106 107 func (s *HubWatcherSuite) TestWatchAfterKnown(c *gc.C) { 108 change := watcher.Change{"test", "a", 5} 109 s.publish(c, change) 110 111 // Watch doesn't publish an initial event, whether or not we've 112 // seen the document before. 113 s.w.Watch("test", "a", s.ch) 114 assertNoChange(c, s.ch) 115 } 116 117 func (s *HubWatcherSuite) TestWatchIgnoreUnwatched(c *gc.C) { 118 s.w.Watch("test", "a", s.ch) 119 120 s.publish(c, watcher.Change{"test", "b", 5}) 121 122 assertNoChange(c, s.ch) 123 } 124 125 func (s *HubWatcherSuite) TestWatchMultiBeforeKnown(c *gc.C) { 126 err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch) 127 c.Assert(err, jc.ErrorIsNil) 128 assertNoChange(c, s.ch) 129 130 change := watcher.Change{"test", "a", 5} 131 s.publish(c, change) 132 133 assertChange(c, s.ch, change) 134 assertNoChange(c, s.ch) 135 } 136 137 func (s *HubWatcherSuite) TestWatchMultiDuplicateWatch(c *gc.C) { 138 s.w.Watch("test", "b", s.ch) 139 assertNoChange(c, s.ch) 140 err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch) 141 c.Assert(err, gc.ErrorMatches, `tried to re-add channel .* for document "b" in collection "test"`) 142 // Changes to "a" should not be watched as we had an error 143 s.publish(c, watcher.Change{"test", "a", 5}) 144 assertNoChange(c, s.ch) 145 } 146 147 func (s *HubWatcherSuite) TestWatchMultiInvalidId(c *gc.C) { 148 err := s.w.WatchMulti("test", []interface{}{"a", nil}, s.ch) 149 c.Assert(err, gc.ErrorMatches, `cannot watch a document with nil id`) 150 // Changes to "a" should not be watched as we had an error 151 s.publish(c, watcher.Change{"test", "a", 5}) 152 assertNoChange(c, s.ch) 153 } 154 155 func (s *HubWatcherSuite) TestWatchMultiAfterKnown(c *gc.C) { 156 s.publish(c, watcher.Change{"test", "a", 5}) 157 err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch) 158 c.Assert(err, jc.ErrorIsNil) 159 assertNoChange(c, s.ch) 160 // We don't see the change that occurred before we started watching, but we see any changes after that fact 161 change := watcher.Change{"test", "a", 6} 162 s.publish(c, change) 163 assertChange(c, s.ch, change) 164 assertNoChange(c, s.ch) 165 } 166 167 func (s *HubWatcherSuite) TestWatchOrder(c *gc.C) { 168 first := watcher.Change{"test", "a", 3} 169 second := watcher.Change{"test", "b", 4} 170 third := watcher.Change{"test", "c", 5} 171 172 for _, id := range []string{"a", "b", "c", "d"} { 173 s.w.Watch("test", id, s.ch) 174 } 175 176 s.publish(c, first, second, third) 177 178 assertChange(c, s.ch, first) 179 assertChange(c, s.ch, second) 180 assertChange(c, s.ch, third) 181 assertNoChange(c, s.ch) 182 } 183 184 func (s *HubWatcherSuite) TestWatchMultipleChannels(c *gc.C) { 185 ch1 := make(chan watcher.Change) 186 ch2 := make(chan watcher.Change) 187 ch3 := make(chan watcher.Change) 188 s.w.Watch("test1", 1, ch1) 189 s.w.Watch("test2", 2, ch2) 190 s.w.Watch("test3", 3, ch3) 191 192 first := watcher.Change{"test1", 1, 3} 193 second := watcher.Change{"test2", 2, 4} 194 third := watcher.Change{"test3", 3, 5} 195 s.publish(c, first, second, third) 196 197 s.w.Unwatch("test2", 2, ch2) 198 assertChange(c, ch1, first) 199 assertChange(c, ch3, third) 200 assertNoChange(c, ch1) 201 assertNoChange(c, ch2) 202 assertNoChange(c, ch3) 203 } 204 205 func (s *HubWatcherSuite) TestWatchAlreadyRemoved(c *gc.C) { 206 change := watcher.Change{"test", "a", -1} 207 s.publish(c, change) 208 209 s.w.Watch("test", "a", s.ch) 210 assertNoChange(c, s.ch) 211 } 212 213 func (s *HubWatcherSuite) TestWatchUnwatchOnQueue(c *gc.C) { 214 const N = 10 215 for i := 0; i < N; i++ { 216 s.w.Watch("test", i, s.ch) 217 } 218 for i := 0; i < N; i++ { 219 s.publish(c, watcher.Change{"test", i, int64(i + 3)}) 220 } 221 for i := 1; i < N; i += 2 { 222 s.w.Unwatch("test", i, s.ch) 223 } 224 seen := make(map[interface{}]bool) 225 for i := 0; i < N/2; i++ { 226 select { 227 case change := <-s.ch: 228 seen[change.Id] = true 229 case <-time.After(worstCase): 230 c.Fatalf("not enough changes: got %d, want %d", len(seen), N/2) 231 } 232 } 233 c.Assert(len(seen), gc.Equals, N/2) 234 assertNoChange(c, s.ch) 235 } 236 237 func (s *HubWatcherSuite) TestWatchCollection(c *gc.C) { 238 chA1 := make(chan watcher.Change) 239 chB1 := make(chan watcher.Change) 240 chA := make(chan watcher.Change) 241 chB := make(chan watcher.Change) 242 243 s.w.Watch("testA", 1, chA1) 244 s.w.Watch("testB", 1, chB1) 245 s.w.WatchCollection("testA", chA) 246 s.w.WatchCollection("testB", chB) 247 248 changes := []watcher.Change{ 249 {"testA", 1, 3}, 250 {"testA", 2, 2}, 251 {"testB", 1, 5}, 252 {"testB", 2, 6}, 253 } 254 s.publish(c, changes...) 255 256 seen := map[chan<- watcher.Change][]watcher.Change{} 257 258 waitForChanges := func(count int, seen map[chan<- watcher.Change][]watcher.Change, timeout time.Duration) { 259 tooLong := time.After(timeout) 260 for n := 0; n < count; n++ { 261 select { 262 case chg := <-chA1: 263 seen[chA1] = append(seen[chA1], chg) 264 case chg := <-chB1: 265 seen[chB1] = append(seen[chB1], chg) 266 case chg := <-chA: 267 seen[chA] = append(seen[chA], chg) 268 case chg := <-chB: 269 seen[chB] = append(seen[chB], chg) 270 case <-tooLong: 271 return 272 } 273 } 274 } 275 276 waitForChanges(6, seen, testing.LongWait) 277 278 c.Check(seen[chA1], jc.DeepEquals, []watcher.Change{changes[0]}) 279 c.Check(seen[chB1], jc.DeepEquals, []watcher.Change{changes[2]}) 280 c.Check(seen[chA], jc.DeepEquals, []watcher.Change{changes[0], changes[1]}) 281 c.Assert(seen[chB], jc.DeepEquals, []watcher.Change{changes[2], changes[3]}) 282 283 s.w.UnwatchCollection("testB", chB) 284 s.w.Unwatch("testB", 1, chB1) 285 286 next := watcher.Change{"testA", 1, 4} 287 s.publish(c, next) 288 289 seen = map[chan<- watcher.Change][]watcher.Change{} 290 waitForChanges(2, seen, testing.LongWait) 291 292 c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{next}) 293 c.Check(seen[chB1], gc.IsNil) 294 c.Check(seen[chA], gc.DeepEquals, []watcher.Change{next}) 295 c.Assert(seen[chB], gc.IsNil) 296 297 // Check that no extra events arrive. 298 seen = map[chan<- watcher.Change][]watcher.Change{} 299 waitForChanges(1, seen, testing.ShortWait) 300 301 c.Check(seen[chA1], gc.IsNil) 302 c.Check(seen[chB1], gc.IsNil) 303 c.Check(seen[chA], gc.IsNil) 304 c.Check(seen[chB], gc.IsNil) 305 } 306 307 func (s *HubWatcherSuite) TestUnwatchCollectionWithFilter(c *gc.C) { 308 filter := func(key interface{}) bool { 309 id := key.(int) 310 return id != 2 311 } 312 313 change := watcher.Change{"testA", 1, 3} 314 s.w.WatchCollectionWithFilter("testA", s.ch, filter) 315 s.publish(c, change) 316 assertChange(c, s.ch, change) 317 s.publish(c, watcher.Change{"testA", 2, 2}) 318 assertNoChange(c, s.ch) 319 320 change = watcher.Change{"testA", 3, 3} 321 s.publish(c, change) 322 assertChange(c, s.ch, change) 323 } 324 325 func (s *HubWatcherSuite) TestWatchBeforeRemoveKnown(c *gc.C) { 326 added := watcher.Change{"test", "a", 2} 327 s.publish(c, added) 328 329 s.w.Watch("test", "a", s.ch) 330 331 removed := watcher.Change{"test", "a", -1} 332 s.publish(c, removed) 333 assertChange(c, s.ch, removed) 334 } 335 336 func (s *HubWatcherSuite) TestWatchStoppedWhileFlushing(c *gc.C) { 337 first := watcher.Change{"test", "a", 2} 338 second := watcher.Change{"test", "a", 3} 339 340 s.w.Watch("test", "a", s.ch) 341 342 s.publish(c, first) 343 // The second event forces a reallocation of the slice in the 344 // watcher. 345 s.publish(c, second) 346 // Unwatching should nil out the channel for all pending sync events. 347 s.w.Unwatch("test", "a", s.ch) 348 349 // Since we haven't removed anything off the channel before the 350 // unwatch, all the pending events should be cleared. 351 assertNoChange(c, s.ch) 352 }