github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/dependency/util_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package dependency_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/tomb.v1"
    13  
    14  	coretesting "github.com/juju/juju/testing"
    15  	"github.com/juju/juju/worker"
    16  	"github.com/juju/juju/worker/dependency"
    17  	"github.com/juju/juju/worker/workertest"
    18  )
    19  
    20  type engineFixture struct {
    21  	isFatal    dependency.IsFatalFunc
    22  	worstError dependency.WorstErrorFunc
    23  	filter     dependency.FilterFunc
    24  	dirty      bool
    25  }
    26  
    27  func (fix *engineFixture) isFatalFunc() dependency.IsFatalFunc {
    28  	if fix.isFatal != nil {
    29  		return fix.isFatal
    30  	}
    31  	return neverFatal
    32  }
    33  
    34  func (fix *engineFixture) worstErrorFunc() dependency.WorstErrorFunc {
    35  	if fix.worstError != nil {
    36  		return fix.worstError
    37  	}
    38  	return firstError
    39  }
    40  
    41  func (fix *engineFixture) run(c *gc.C, test func(*dependency.Engine)) {
    42  	config := dependency.EngineConfig{
    43  		IsFatal:     fix.isFatalFunc(),
    44  		WorstError:  fix.worstErrorFunc(),
    45  		Filter:      fix.filter, // can be nil anyway
    46  		ErrorDelay:  coretesting.ShortWait / 2,
    47  		BounceDelay: coretesting.ShortWait / 10,
    48  	}
    49  
    50  	engine, err := dependency.NewEngine(config)
    51  	c.Assert(err, jc.ErrorIsNil)
    52  	defer fix.kill(c, engine)
    53  	test(engine)
    54  }
    55  
    56  func (fix *engineFixture) kill(c *gc.C, engine *dependency.Engine) {
    57  	if fix.dirty {
    58  		workertest.DirtyKill(c, engine)
    59  	} else {
    60  		workertest.CleanKill(c, engine)
    61  	}
    62  }
    63  
    64  type manifoldHarness struct {
    65  	inputs             []string
    66  	errors             chan error
    67  	starts             chan struct{}
    68  	requireResources   bool
    69  	ignoreExternalKill bool
    70  }
    71  
    72  func newManifoldHarness(inputs ...string) *manifoldHarness {
    73  	return &manifoldHarness{
    74  		inputs:           inputs,
    75  		errors:           make(chan error, 1000),
    76  		starts:           make(chan struct{}, 1000),
    77  		requireResources: true,
    78  	}
    79  }
    80  
    81  func newResourceIgnoringManifoldHarness(inputs ...string) *manifoldHarness {
    82  	mh := newManifoldHarness(inputs...)
    83  	mh.requireResources = false
    84  	return mh
    85  }
    86  
    87  // newErrorIgnoringManifoldHarness starts a minimal worker that ignores
    88  // fatal errors - and will never die.
    89  // This is potentially nasty, but it's useful in tests where we want
    90  // to generate fatal errors but not race on which one the engine see first.
    91  func newErrorIgnoringManifoldHarness(inputs ...string) *manifoldHarness {
    92  	mh := newManifoldHarness(inputs...)
    93  	mh.ignoreExternalKill = true
    94  	return mh
    95  }
    96  
    97  func (mh *manifoldHarness) Manifold() dependency.Manifold {
    98  	return dependency.Manifold{
    99  		Inputs: mh.inputs,
   100  		Start:  mh.start,
   101  	}
   102  }
   103  
   104  func (mh *manifoldHarness) start(context dependency.Context) (worker.Worker, error) {
   105  	for _, resourceName := range mh.inputs {
   106  		if err := context.Get(resourceName, nil); err != nil {
   107  			if mh.requireResources {
   108  				return nil, err
   109  			}
   110  		}
   111  	}
   112  	w := &minimalWorker{tomb.Tomb{}, mh.ignoreExternalKill}
   113  	go func() {
   114  		defer w.tomb.Done()
   115  		mh.starts <- struct{}{}
   116  		select {
   117  		case <-w.tombDying():
   118  		case err := <-mh.errors:
   119  			w.tomb.Kill(err)
   120  		}
   121  	}()
   122  	return w, nil
   123  }
   124  
   125  func (mh *manifoldHarness) AssertOneStart(c *gc.C) {
   126  	mh.AssertStart(c)
   127  	mh.AssertNoStart(c)
   128  }
   129  
   130  func (mh *manifoldHarness) AssertStart(c *gc.C) {
   131  	select {
   132  	case <-mh.starts:
   133  	case <-time.After(coretesting.LongWait):
   134  		c.Fatalf("never started")
   135  	}
   136  }
   137  
   138  func (mh *manifoldHarness) AssertNoStart(c *gc.C) {
   139  	select {
   140  	case <-time.After(coretesting.ShortWait):
   141  	case <-mh.starts:
   142  		c.Fatalf("started unexpectedly")
   143  	}
   144  }
   145  
   146  func (mh *manifoldHarness) InjectError(c *gc.C, err error) {
   147  	select {
   148  	case mh.errors <- err:
   149  	case <-time.After(coretesting.LongWait):
   150  		c.Fatalf("never sent")
   151  	}
   152  }
   153  
   154  func newTracedManifoldHarness(inputs ...string) *tracedManifoldHarness {
   155  	return &tracedManifoldHarness{
   156  		&manifoldHarness{
   157  			inputs:             inputs,
   158  			errors:             make(chan error, 1000),
   159  			starts:             make(chan struct{}, 1000),
   160  			ignoreExternalKill: false,
   161  		},
   162  	}
   163  }
   164  
   165  type tracedManifoldHarness struct {
   166  	*manifoldHarness
   167  }
   168  
   169  func (mh *tracedManifoldHarness) Manifold() dependency.Manifold {
   170  	return dependency.Manifold{
   171  		Inputs: mh.inputs,
   172  		Start:  mh.start,
   173  	}
   174  }
   175  
   176  func (mh *tracedManifoldHarness) start(context dependency.Context) (worker.Worker, error) {
   177  	for _, resourceName := range mh.inputs {
   178  		if err := context.Get(resourceName, nil); err != nil {
   179  			return nil, errors.Trace(err)
   180  		}
   181  	}
   182  	w := &minimalWorker{tomb.Tomb{}, mh.ignoreExternalKill}
   183  	go func() {
   184  		defer w.tomb.Done()
   185  		mh.starts <- struct{}{}
   186  		select {
   187  		case <-w.tombDying():
   188  		case err := <-mh.errors:
   189  			w.tomb.Kill(err)
   190  		}
   191  	}()
   192  	return w, nil
   193  }
   194  
   195  type minimalWorker struct {
   196  	tomb               tomb.Tomb
   197  	ignoreExternalKill bool
   198  }
   199  
   200  func (w *minimalWorker) tombDying() <-chan struct{} {
   201  	if w.ignoreExternalKill {
   202  		return nil
   203  	}
   204  	return w.tomb.Dying()
   205  }
   206  
   207  func (w *minimalWorker) Kill() {
   208  	w.tomb.Kill(nil)
   209  }
   210  
   211  func (w *minimalWorker) Wait() error {
   212  	return w.tomb.Wait()
   213  }
   214  
   215  func (w *minimalWorker) Report() map[string]interface{} {
   216  	return map[string]interface{}{
   217  		"key1": "hello there",
   218  	}
   219  }
   220  
   221  func startMinimalWorker(_ dependency.Context) (worker.Worker, error) {
   222  	w := &minimalWorker{}
   223  	go func() {
   224  		<-w.tomb.Dying()
   225  		w.tomb.Done()
   226  	}()
   227  	return w, nil
   228  }
   229  
   230  func isFatalIf(expect error) func(error) bool {
   231  	return func(actual error) bool {
   232  		return actual == expect
   233  	}
   234  }
   235  
   236  func neverFatal(_ error) bool {
   237  	return false
   238  }
   239  
   240  func alwaysFatal(_ error) bool {
   241  	return true
   242  }
   243  
   244  func firstError(err, _ error) error {
   245  	return err
   246  }