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