github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/instancepoller/mock_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package instancepoller_test 5 6 import ( 7 "sort" 8 "sync" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 "github.com/juju/testing" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/apiserver/instancepoller" 16 "github.com/juju/juju/environs/config" 17 "github.com/juju/juju/instance" 18 "github.com/juju/juju/network" 19 "github.com/juju/juju/state" 20 ) 21 22 // mockState implements StateInterface and allows inspection of called 23 // methods. 24 type mockState struct { 25 *testing.Stub 26 27 mu sync.Mutex 28 29 configWatchers []*mockConfigWatcher 30 machinesWatchers []*mockMachinesWatcher 31 32 config *config.Config 33 machines map[string]*mockMachine 34 } 35 36 func NewMockState() *mockState { 37 return &mockState{ 38 Stub: &testing.Stub{}, 39 machines: make(map[string]*mockMachine), 40 } 41 } 42 43 var _ instancepoller.StateInterface = (*mockState)(nil) 44 45 // CheckFindEntityCall is a helper wrapper aroud 46 // testing.Stub.CheckCall for FindEntity. 47 func (m *mockState) CheckFindEntityCall(c *gc.C, index int, machineId string) { 48 m.CheckCall(c, index, "FindEntity", interface{}(names.NewMachineTag(machineId))) 49 } 50 51 // CheckSetProviderAddressesCall is a helper wrapper aroud 52 // testing.Stub.CheckCall for SetProviderAddresses. 53 func (m *mockState) CheckSetProviderAddressesCall(c *gc.C, index int, addrs []network.Address) { 54 args := make([]interface{}, len(addrs)) 55 for i, addr := range addrs { 56 args[i] = addr 57 } 58 m.CheckCall(c, index, "SetProviderAddresses", args...) 59 } 60 61 // WatchForEnvironConfigChanges implements StateInterface. 62 func (m *mockState) WatchForEnvironConfigChanges() state.NotifyWatcher { 63 m.mu.Lock() 64 defer m.mu.Unlock() 65 66 m.MethodCall(m, "WatchForEnvironConfigChanges") 67 68 w := NewMockConfigWatcher(m.NextErr()) 69 m.configWatchers = append(m.configWatchers, w) 70 return w 71 } 72 73 // EnvironConfig implements StateInterface. 74 func (m *mockState) EnvironConfig() (*config.Config, error) { 75 m.mu.Lock() 76 defer m.mu.Unlock() 77 78 m.MethodCall(m, "EnvironConfig") 79 80 if err := m.NextErr(); err != nil { 81 return nil, err 82 } 83 return m.config, nil 84 } 85 86 // SetConfig updates the environ config stored internally. Triggers a 87 // change event for all created config watchers. 88 func (m *mockState) SetConfig(c *gc.C, newConfig *config.Config) { 89 m.mu.Lock() 90 defer m.mu.Unlock() 91 92 m.config = newConfig 93 94 // Notify any watchers for the changes. 95 for _, w := range m.configWatchers { 96 w.incoming <- struct{}{} 97 } 98 } 99 100 // WatchEnvironMachines implements StateInterface. 101 func (m *mockState) WatchEnvironMachines() state.StringsWatcher { 102 m.mu.Lock() 103 defer m.mu.Unlock() 104 105 m.MethodCall(m, "WatchEnvironMachines") 106 107 ids := make([]string, 0, len(m.machines)) 108 // Initial event - all machine ids, sorted. 109 for id := range m.machines { 110 ids = append(ids, id) 111 } 112 sort.Strings(ids) 113 114 w := NewMockMachinesWatcher(ids, m.NextErr()) 115 m.machinesWatchers = append(m.machinesWatchers, w) 116 return w 117 } 118 119 // FindEntity implements StateInterface. 120 func (m *mockState) FindEntity(tag names.Tag) (state.Entity, error) { 121 m.mu.Lock() 122 defer m.mu.Unlock() 123 124 m.MethodCall(m, "FindEntity", tag) 125 126 if err := m.NextErr(); err != nil { 127 return nil, err 128 } 129 if tag == nil { 130 return nil, errors.NotValidf("nil tag is") // +" not valid" 131 } 132 machine, found := m.machines[tag.Id()] 133 if !found { 134 return nil, errors.NotFoundf("machine %s", tag.Id()) 135 } 136 return machine, nil 137 } 138 139 // SetMachineInfo adds a new or updates existing mockMachine info. 140 // Triggers any created mock machines watchers to return a change. 141 func (m *mockState) SetMachineInfo(c *gc.C, args machineInfo) { 142 m.mu.Lock() 143 defer m.mu.Unlock() 144 145 c.Assert(args.id, gc.Not(gc.Equals), "") 146 147 machine, found := m.machines[args.id] 148 if !found { 149 machine = &mockMachine{ 150 Stub: m.Stub, // reuse parent stub. 151 machineInfo: args, 152 } 153 } else { 154 machine.machineInfo = args 155 } 156 m.machines[args.id] = machine 157 158 // Notify any watchers for the changes. 159 ids := []string{args.id} 160 for _, w := range m.machinesWatchers { 161 w.incoming <- ids 162 } 163 } 164 165 // RemoveMachine removes an existing mockMachine with the given id. 166 // Triggers the machines watchers on success. If the id is not found 167 // no error occurs and no change is reported by the watchers. 168 func (m *mockState) RemoveMachine(c *gc.C, id string) { 169 m.mu.Lock() 170 defer m.mu.Unlock() 171 172 if _, found := m.machines[id]; !found { 173 return 174 } 175 176 delete(m.machines, id) 177 178 // Notify any watchers for the changes. 179 ids := []string{id} 180 for _, w := range m.machinesWatchers { 181 w.incoming <- ids 182 } 183 } 184 185 // Machine implements StateInterface. 186 func (m *mockState) Machine(id string) (instancepoller.StateMachine, error) { 187 m.mu.Lock() 188 defer m.mu.Unlock() 189 190 m.MethodCall(m, "Machine", id) 191 192 if err := m.NextErr(); err != nil { 193 return nil, err 194 } 195 machine, found := m.machines[id] 196 if !found { 197 return nil, errors.NotFoundf("machine %s", id) 198 } 199 return machine, nil 200 } 201 202 // StartSync implements statetesting.SyncStarter, so mockState can be 203 // used with watcher helpers/checkers. 204 func (m *mockState) StartSync() {} 205 206 type machineInfo struct { 207 id string 208 instanceId instance.Id 209 status state.StatusInfo 210 instanceStatus string 211 providerAddresses []network.Address 212 life state.Life 213 isManual bool 214 } 215 216 type mockMachine struct { 217 *testing.Stub 218 instancepoller.StateMachine 219 220 mu sync.Mutex 221 222 machineInfo 223 } 224 225 var _ instancepoller.StateMachine = (*mockMachine)(nil) 226 227 // InstanceId implements StateMachine. 228 func (m *mockMachine) InstanceId() (instance.Id, error) { 229 m.mu.Lock() 230 defer m.mu.Unlock() 231 232 m.MethodCall(m, "InstanceId") 233 if err := m.NextErr(); err != nil { 234 return "", err 235 } 236 return m.instanceId, nil 237 } 238 239 // ProviderAddresses implements StateMachine. 240 func (m *mockMachine) ProviderAddresses() []network.Address { 241 m.mu.Lock() 242 defer m.mu.Unlock() 243 244 m.MethodCall(m, "ProviderAddresses") 245 m.NextErr() // consume the unused error 246 return m.providerAddresses 247 } 248 249 // SetProviderAddresses implements StateMachine. 250 func (m *mockMachine) SetProviderAddresses(addrs ...network.Address) error { 251 m.mu.Lock() 252 defer m.mu.Unlock() 253 254 args := make([]interface{}, len(addrs)) 255 for i, addr := range addrs { 256 args[i] = addr 257 } 258 m.MethodCall(m, "SetProviderAddresses", args...) 259 if err := m.NextErr(); err != nil { 260 return err 261 } 262 m.providerAddresses = addrs 263 return nil 264 } 265 266 // InstanceStatus implements StateMachine. 267 func (m *mockMachine) InstanceStatus() (string, error) { 268 m.mu.Lock() 269 defer m.mu.Unlock() 270 271 m.MethodCall(m, "InstanceStatus") 272 if err := m.NextErr(); err != nil { 273 return "", err 274 } 275 return m.instanceStatus, nil 276 } 277 278 // SetInstanceStatus implements StateMachine. 279 func (m *mockMachine) SetInstanceStatus(status string) error { 280 m.mu.Lock() 281 defer m.mu.Unlock() 282 283 m.MethodCall(m, "SetInstanceStatus", status) 284 if err := m.NextErr(); err != nil { 285 return err 286 } 287 m.instanceStatus = status 288 return nil 289 } 290 291 // Life implements StateMachine. 292 func (m *mockMachine) Life() state.Life { 293 m.mu.Lock() 294 defer m.mu.Unlock() 295 296 m.MethodCall(m, "Life") 297 m.NextErr() // consume the unused error 298 return m.life 299 } 300 301 // IsManual implements StateMachine. 302 func (m *mockMachine) IsManual() (bool, error) { 303 m.mu.Lock() 304 defer m.mu.Unlock() 305 306 m.MethodCall(m, "IsManual") 307 return m.isManual, m.NextErr() 308 } 309 310 // Status implements StateMachine. 311 func (m *mockMachine) Status() (state.StatusInfo, error) { 312 m.mu.Lock() 313 defer m.mu.Unlock() 314 315 m.MethodCall(m, "Status") 316 return m.status, m.NextErr() 317 } 318 319 type mockBaseWatcher struct { 320 err error 321 322 closeChanges func() 323 done chan struct{} 324 } 325 326 var _ state.Watcher = (*mockBaseWatcher)(nil) 327 328 func NewMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher { 329 w := &mockBaseWatcher{ 330 err: err, 331 closeChanges: closeChanges, 332 done: make(chan struct{}), 333 } 334 if err != nil { 335 // Don't start the loop if we should fail. 336 w.Stop() 337 } 338 return w 339 } 340 341 // Kill implements state.Watcher. 342 func (m *mockBaseWatcher) Kill() {} 343 344 // Stop implements state.Watcher. 345 func (m *mockBaseWatcher) Stop() error { 346 select { 347 case <-m.done: 348 // already closed 349 default: 350 // Signal the loop we want to stop. 351 close(m.done) 352 // Signal the clients we've closed. 353 m.closeChanges() 354 } 355 return m.err 356 } 357 358 // Wait implements state.Watcher. 359 func (m *mockBaseWatcher) Wait() error { 360 return m.Stop() 361 } 362 363 // Err implements state.Watcher. 364 func (m *mockBaseWatcher) Err() error { 365 return m.err 366 } 367 368 type mockConfigWatcher struct { 369 *mockBaseWatcher 370 371 incoming chan struct{} 372 changes chan struct{} 373 } 374 375 var _ state.NotifyWatcher = (*mockConfigWatcher)(nil) 376 377 func NewMockConfigWatcher(err error) *mockConfigWatcher { 378 changes := make(chan struct{}) 379 w := &mockConfigWatcher{ 380 changes: changes, 381 incoming: make(chan struct{}), 382 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 383 } 384 if err == nil { 385 go w.loop() 386 } 387 return w 388 } 389 390 func (m *mockConfigWatcher) loop() { 391 // Prepare initial event. 392 outChanges := m.changes 393 // Forward any incoming changes until stopped. 394 for { 395 select { 396 case <-m.done: 397 // We're about to quit. 398 return 399 case outChanges <- struct{}{}: 400 outChanges = nil 401 case <-m.incoming: 402 outChanges = m.changes 403 } 404 } 405 } 406 407 // Changes implements state.NotifyWatcher. 408 func (m *mockConfigWatcher) Changes() <-chan struct{} { 409 return m.changes 410 } 411 412 type mockMachinesWatcher struct { 413 *mockBaseWatcher 414 415 initial []string 416 incoming chan []string 417 changes chan []string 418 } 419 420 func NewMockMachinesWatcher(initial []string, err error) *mockMachinesWatcher { 421 changes := make(chan []string) 422 w := &mockMachinesWatcher{ 423 initial: initial, 424 changes: changes, 425 incoming: make(chan []string), 426 mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }), 427 } 428 if err == nil { 429 go w.loop() 430 } 431 return w 432 } 433 434 func (m *mockMachinesWatcher) loop() { 435 // Prepare initial event. 436 unsent := m.initial 437 outChanges := m.changes 438 // Forward any incoming changes until stopped. 439 for { 440 select { 441 case <-m.done: 442 // We're about to quit. 443 return 444 case outChanges <- unsent: 445 outChanges = nil 446 unsent = nil 447 case ids := <-m.incoming: 448 unsent = append(unsent, ids...) 449 outChanges = m.changes 450 } 451 } 452 } 453 454 // Changes implements state.StringsWatcher. 455 func (m *mockMachinesWatcher) Changes() <-chan []string { 456 return m.changes 457 } 458 459 var _ state.StringsWatcher = (*mockMachinesWatcher)(nil)