github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/environ/fixture_test.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environ_test
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/testing"
    12  	"github.com/juju/worker/v3"
    13  	"github.com/juju/worker/v3/workertest"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/core/watcher"
    17  	"github.com/juju/juju/environs"
    18  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    19  	"github.com/juju/juju/environs/config"
    20  	coretesting "github.com/juju/juju/testing"
    21  )
    22  
    23  type fixture struct {
    24  	watcherErr    error
    25  	observerErrs  []error
    26  	initialConfig map[string]interface{}
    27  	initialSpec   environscloudspec.CloudSpec
    28  }
    29  
    30  func (fix *fixture) Run(c *gc.C, test func(*runContext)) {
    31  	watcher := newNotifyWatcher(fix.watcherErr)
    32  	defer workertest.DirtyKill(c, watcher)
    33  	cloudWatcher := newNotifyWatcher(fix.watcherErr)
    34  	defer workertest.DirtyKill(c, cloudWatcher)
    35  	context := &runContext{
    36  		cloud:        fix.initialSpec,
    37  		config:       newModelConfig(c, fix.initialConfig),
    38  		watcher:      watcher,
    39  		cloudWatcher: cloudWatcher,
    40  	}
    41  	context.stub.SetErrors(fix.observerErrs...)
    42  	test(context)
    43  }
    44  
    45  type runContext struct {
    46  	mu           sync.Mutex
    47  	stub         testing.Stub
    48  	cloud        environscloudspec.CloudSpec
    49  	config       map[string]interface{}
    50  	watcher      *notifyWatcher
    51  	cloudWatcher *notifyWatcher
    52  	credWatcher  *notifyWatcher
    53  }
    54  
    55  // SetConfig updates the configuration returned by ModelConfig.
    56  func (context *runContext) SetConfig(c *gc.C, extraAttrs coretesting.Attrs) {
    57  	context.mu.Lock()
    58  	defer context.mu.Unlock()
    59  	context.config = newModelConfig(c, extraAttrs)
    60  }
    61  
    62  // SetCloudSpec updates the spec returned by CloudSpec.
    63  func (context *runContext) SetCloudSpec(c *gc.C, spec environscloudspec.CloudSpec) {
    64  	context.mu.Lock()
    65  	defer context.mu.Unlock()
    66  	context.cloud = spec
    67  }
    68  
    69  // CloudSpec is part of the environ.ConfigObserver interface.
    70  func (context *runContext) CloudSpec() (environscloudspec.CloudSpec, error) {
    71  	context.mu.Lock()
    72  	defer context.mu.Unlock()
    73  	context.stub.AddCall("CloudSpec")
    74  	if err := context.stub.NextErr(); err != nil {
    75  		return environscloudspec.CloudSpec{}, err
    76  	}
    77  	return context.cloud, nil
    78  }
    79  
    80  // ModelConfig is part of the environ.ConfigObserver interface.
    81  func (context *runContext) ModelConfig() (*config.Config, error) {
    82  	context.mu.Lock()
    83  	defer context.mu.Unlock()
    84  	context.stub.AddCall("ModelConfig")
    85  	if err := context.stub.NextErr(); err != nil {
    86  		return nil, err
    87  	}
    88  	return config.New(config.NoDefaults, context.config)
    89  }
    90  
    91  // KillModelConfigNotify kills the watcher returned from WatchForModelConfigChanges with
    92  // the error configured in the enclosing fixture.
    93  func (context *runContext) KillModelConfigNotify() {
    94  	context.watcher.Kill()
    95  }
    96  
    97  // SendModelConfigNotify sends a value on the channel used by WatchForModelConfigChanges
    98  // results.
    99  func (context *runContext) SendModelConfigNotify() {
   100  	context.watcher.changes <- struct{}{}
   101  }
   102  
   103  // CloseModelConfigNotify closes the channel used by WatchForModelConfigChanges results.
   104  func (context *runContext) CloseModelConfigNotify() {
   105  	close(context.watcher.changes)
   106  }
   107  
   108  // KillCloudSpecNotify kills the watcher returned from WatchCloudSpecChanges with
   109  // the error configured in the enclosing fixture.
   110  func (context *runContext) KillCloudSpecNotify() {
   111  	context.cloudWatcher.Kill()
   112  }
   113  
   114  // SendCloudSpecNotify sends a value on the channel used by WatchCloudSpecChanges
   115  // results.
   116  func (context *runContext) SendCloudSpecNotify() {
   117  	context.cloudWatcher.changes <- struct{}{}
   118  }
   119  
   120  // CloseCloudSpecNotify closes the channel used by WatchCloudSpecChanges results.
   121  func (context *runContext) CloseCloudSpecNotify() {
   122  	close(context.cloudWatcher.changes)
   123  }
   124  
   125  // WatchForModelConfigChanges is part of the environ.ConfigObserver interface.
   126  func (context *runContext) WatchForModelConfigChanges() (watcher.NotifyWatcher, error) {
   127  	context.mu.Lock()
   128  	defer context.mu.Unlock()
   129  	context.stub.AddCall("WatchForModelConfigChanges")
   130  	if err := context.stub.NextErr(); err != nil {
   131  		return nil, err
   132  	}
   133  	return context.watcher, nil
   134  }
   135  
   136  func (context *runContext) WatchCloudSpecChanges() (watcher.NotifyWatcher, error) {
   137  	context.mu.Lock()
   138  	defer context.mu.Unlock()
   139  	context.stub.AddCall("WatchCloudSpecChanges")
   140  	if err := context.stub.NextErr(); err != nil {
   141  		return nil, err
   142  	}
   143  	return context.cloudWatcher, nil
   144  }
   145  
   146  // KillCredentialNotify kills the watcher returned from WatchCredentialChanges with
   147  // the error configured in the enclosing fixture.
   148  func (context *runContext) KillCredentialNotify() {
   149  	context.credWatcher.Kill()
   150  }
   151  
   152  // SendCredentialNotify sends a value on the channel used by WatchCredentialChanges
   153  // results.
   154  func (context *runContext) SendCredentialNotify() {
   155  	context.credWatcher.changes <- struct{}{}
   156  }
   157  
   158  // CloseCredentialNotify closes the channel used by WatchCredentialChanges results.
   159  func (context *runContext) CloseCredentialNotify() {
   160  	close(context.credWatcher.changes)
   161  }
   162  
   163  // WatchCredential is part of the environ.ConfigObserver interface.
   164  func (context *runContext) WatchCredential(cred names.CloudCredentialTag) (watcher.NotifyWatcher, error) {
   165  	context.mu.Lock()
   166  	defer context.mu.Unlock()
   167  	context.stub.AddCall("WatchCredential")
   168  	if err := context.stub.NextErr(); err != nil {
   169  		return nil, err
   170  	}
   171  	return context.watcher, nil
   172  }
   173  
   174  func (context *runContext) CheckCallNames(c *gc.C, names ...string) {
   175  	context.mu.Lock()
   176  	defer context.mu.Unlock()
   177  	context.stub.CheckCallNames(c, names...)
   178  }
   179  
   180  // newNotifyWatcher returns a watcher.NotifyWatcher that will fail with the
   181  // supplied error when Kill()ed.
   182  func newNotifyWatcher(err error) *notifyWatcher {
   183  	return &notifyWatcher{
   184  		Worker:  workertest.NewErrorWorker(err),
   185  		changes: make(chan struct{}, 1000),
   186  	}
   187  }
   188  
   189  type notifyWatcher struct {
   190  	worker.Worker
   191  	changes chan struct{}
   192  }
   193  
   194  // Changes is part of the watcher.NotifyWatcher interface.
   195  func (w *notifyWatcher) Changes() watcher.NotifyChannel {
   196  	return w.changes
   197  }
   198  
   199  // newModelConfig returns an environment config map with the supplied attrs
   200  // (on top of some default set), or fails the test.
   201  func newModelConfig(c *gc.C, extraAttrs coretesting.Attrs) map[string]interface{} {
   202  	return coretesting.CustomModelConfig(c, extraAttrs).AllAttrs()
   203  }
   204  
   205  type mockEnviron struct {
   206  	environs.Environ
   207  	testing.Stub
   208  	cfg  *config.Config
   209  	spec environscloudspec.CloudSpec
   210  	mu   sync.Mutex
   211  }
   212  
   213  func (e *mockEnviron) Config() *config.Config {
   214  	e.mu.Lock()
   215  	defer e.mu.Unlock()
   216  	e.MethodCall(e, "Config")
   217  	e.PopNoErr()
   218  	return e.cfg
   219  }
   220  
   221  func (e *mockEnviron) SetConfig(cfg *config.Config) error {
   222  	e.mu.Lock()
   223  	defer e.mu.Unlock()
   224  	e.MethodCall(e, "SetConfig", cfg)
   225  	if err := e.NextErr(); err != nil {
   226  		return err
   227  	}
   228  	e.cfg = cfg
   229  	return nil
   230  }
   231  
   232  func (e *mockEnviron) CloudSpec() environscloudspec.CloudSpec {
   233  	e.mu.Lock()
   234  	defer e.mu.Unlock()
   235  	return e.spec
   236  }
   237  
   238  func (e *mockEnviron) SetCloudSpec(_ context.Context, spec environscloudspec.CloudSpec) error {
   239  	e.mu.Lock()
   240  	defer e.mu.Unlock()
   241  	e.MethodCall(e, "SetCloudSpec", spec)
   242  	if err := e.NextErr(); err != nil {
   243  		return err
   244  	}
   245  	e.spec = spec
   246  	return nil
   247  }
   248  
   249  func newMockEnviron(_ context.Context, args environs.OpenParams) (environs.Environ, error) {
   250  	return &mockEnviron{cfg: args.Config, spec: args.Cloud}, nil
   251  }