launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/api/watcher/watcher_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package watcher_test 5 6 import ( 7 gc "launchpad.net/gocheck" 8 stdtesting "testing" 9 "time" 10 11 "launchpad.net/juju-core/juju/testing" 12 "launchpad.net/juju-core/state" 13 "launchpad.net/juju-core/state/api" 14 "launchpad.net/juju-core/state/api/params" 15 "launchpad.net/juju-core/state/api/watcher" 16 statetesting "launchpad.net/juju-core/state/testing" 17 coretesting "launchpad.net/juju-core/testing" 18 ) 19 20 func TestAll(t *stdtesting.T) { 21 coretesting.MgoTestPackage(t) 22 } 23 24 type watcherSuite struct { 25 testing.JujuConnSuite 26 27 stateAPI *api.State 28 29 // These are raw State objects. Use them for setup and assertions, but 30 // should never be touched by the API calls themselves 31 rawMachine *state.Machine 32 } 33 34 var _ = gc.Suite(&watcherSuite{}) 35 36 func (s *watcherSuite) SetUpTest(c *gc.C) { 37 s.JujuConnSuite.SetUpTest(c) 38 s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c) 39 } 40 41 func (s *watcherSuite) TestWatchInitialEventConsumed(c *gc.C) { 42 // Machiner.Watch should send the initial event as part of the Watch 43 // call (for NotifyWatchers there is no state to be transmitted). So a 44 // call to Next() should not have anything to return. 45 var results params.NotifyWatchResults 46 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} 47 err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) 48 c.Assert(err, gc.IsNil) 49 c.Assert(results.Results, gc.HasLen, 1) 50 result := results.Results[0] 51 c.Assert(result.Error, gc.IsNil) 52 53 // We expect the Call() to "Next" to block, so run it in a goroutine. 54 done := make(chan error) 55 go func() { 56 ignored := struct{}{} 57 done <- s.stateAPI.Call("NotifyWatcher", result.NotifyWatcherId, "Next", nil, &ignored) 58 }() 59 60 select { 61 case err := <-done: 62 c.Errorf("Call(Next) did not block immediately after Watch(): err %v", err) 63 case <-time.After(coretesting.ShortWait): 64 } 65 } 66 67 func (s *watcherSuite) TestWatchMachine(c *gc.C) { 68 var results params.NotifyWatchResults 69 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} 70 err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) 71 c.Assert(err, gc.IsNil) 72 c.Assert(results.Results, gc.HasLen, 1) 73 result := results.Results[0] 74 c.Assert(result.Error, gc.IsNil) 75 76 // params.NotifyWatcher conforms to the state.NotifyWatcher interface 77 w := watcher.NewNotifyWatcher(s.stateAPI, result) 78 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 79 wc.AssertOneChange() 80 statetesting.AssertStop(c, w) 81 wc.AssertClosed() 82 } 83 84 func (s *watcherSuite) TestNotifyWatcherStopsWithPendingSend(c *gc.C) { 85 var results params.NotifyWatchResults 86 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} 87 err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) 88 c.Assert(err, gc.IsNil) 89 c.Assert(results.Results, gc.HasLen, 1) 90 result := results.Results[0] 91 c.Assert(result.Error, gc.IsNil) 92 93 // params.NotifyWatcher conforms to the state.NotifyWatcher interface 94 w := watcher.NewNotifyWatcher(s.stateAPI, result) 95 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 96 97 // Now, without reading any changes try stopping the watcher. 98 statetesting.AssertCanStopWhenSending(c, w) 99 wc.AssertClosed() 100 } 101 102 func (s *watcherSuite) TestWatchUnitsKeepsEvents(c *gc.C) { 103 // Create two services, relate them, and add one unit to each - a 104 // principal and a subordinate. 105 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 106 logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 107 eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) 108 c.Assert(err, gc.IsNil) 109 rel, err := s.State.AddRelation(eps...) 110 c.Assert(err, gc.IsNil) 111 principal, err := mysql.AddUnit() 112 c.Assert(err, gc.IsNil) 113 err = principal.AssignToMachine(s.rawMachine) 114 c.Assert(err, gc.IsNil) 115 relUnit, err := rel.Unit(principal) 116 c.Assert(err, gc.IsNil) 117 err = relUnit.EnterScope(nil) 118 c.Assert(err, gc.IsNil) 119 subordinate, err := logging.Unit("logging/0") 120 c.Assert(err, gc.IsNil) 121 122 // Call the Deployer facade's WatchUnits for machine-0. 123 var results params.StringsWatchResults 124 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} 125 err = s.stateAPI.Call("Deployer", "", "WatchUnits", args, &results) 126 c.Assert(err, gc.IsNil) 127 c.Assert(results.Results, gc.HasLen, 1) 128 result := results.Results[0] 129 c.Assert(result.Error, gc.IsNil) 130 131 // Start a StringsWatcher and check the initial event. 132 w := watcher.NewStringsWatcher(s.stateAPI, result) 133 wc := statetesting.NewStringsWatcherC(c, s.State, w) 134 wc.AssertChange("mysql/0", "logging/0") 135 wc.AssertNoChange() 136 137 // Now, without reading any changes advance the lifecycle of both 138 // units, inducing an update server-side after each two changes to 139 // ensure they're reported as separate events over the API. 140 err = subordinate.EnsureDead() 141 c.Assert(err, gc.IsNil) 142 s.BackingState.StartSync() 143 err = subordinate.Remove() 144 c.Assert(err, gc.IsNil) 145 err = principal.EnsureDead() 146 c.Assert(err, gc.IsNil) 147 s.BackingState.StartSync() 148 149 // Expect these changes as 2 separate events, so that 150 // nothing gets lost. 151 wc.AssertChange("logging/0") 152 wc.AssertChange("mysql/0") 153 wc.AssertNoChange() 154 155 statetesting.AssertStop(c, w) 156 wc.AssertClosed() 157 } 158 159 func (s *watcherSuite) TestStringsWatcherStopsWithPendingSend(c *gc.C) { 160 // Call the Deployer facade's WatchUnits for machine-0. 161 var results params.StringsWatchResults 162 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} 163 err := s.stateAPI.Call("Deployer", "", "WatchUnits", args, &results) 164 c.Assert(err, gc.IsNil) 165 c.Assert(results.Results, gc.HasLen, 1) 166 result := results.Results[0] 167 c.Assert(result.Error, gc.IsNil) 168 169 // Start a StringsWatcher and check the initial event. 170 w := watcher.NewStringsWatcher(s.stateAPI, result) 171 wc := statetesting.NewStringsWatcherC(c, s.State, w) 172 173 // Create a service, deploy a unit of it on the machine. 174 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 175 principal, err := mysql.AddUnit() 176 c.Assert(err, gc.IsNil) 177 err = principal.AssignToMachine(s.rawMachine) 178 c.Assert(err, gc.IsNil) 179 180 // Ensure the initial event is delivered. Then test the watcher 181 // can be stopped cleanly without reading the pending change. 182 s.BackingState.StartSync() 183 statetesting.AssertCanStopWhenSending(c, w) 184 wc.AssertClosed() 185 }