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)