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