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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"reflect"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/juju/worker/dependency"
    11  )
    12  
    13  // StubResource is used to define the behaviour of a StubGetResource func for a
    14  // particular resource name.
    15  type StubResource struct {
    16  	Output interface{}
    17  	Error  error
    18  }
    19  
    20  // NewStubResources converts raw into a StubResources by assuming that any non-error
    21  // value is intended to be an output.
    22  func NewStubResources(raw map[string]interface{}) StubResources {
    23  	resources := StubResources{}
    24  	for name, value := range raw {
    25  		if err, ok := value.(error); ok {
    26  			resources[name] = StubResource{Error: err}
    27  		} else {
    28  			resources[name] = StubResource{Output: value}
    29  		}
    30  	}
    31  	return resources
    32  }
    33  
    34  // StubResources defines the complete behaviour of a StubGetResource func.
    35  type StubResources map[string]StubResource
    36  
    37  // Context returns a dependency.Context that never aborts, backed by resources.
    38  func (resources StubResources) Context() dependency.Context {
    39  	return &Context{
    40  		resources: resources,
    41  	}
    42  }
    43  
    44  // StubContext returns a Context backed by abort and resources derived from raw.
    45  func StubContext(abort <-chan struct{}, raw map[string]interface{}) *Context {
    46  	return &Context{
    47  		abort:     abort,
    48  		resources: NewStubResources(raw),
    49  	}
    50  }
    51  
    52  // Context implements dependency.Context for convenient testing of dependency.StartFuncs.
    53  type Context struct {
    54  	abort     <-chan struct{}
    55  	resources StubResources
    56  }
    57  
    58  // Abort is part of the dependency.Context interface.
    59  func (ctx *Context) Abort() <-chan struct{} {
    60  	return ctx.abort
    61  }
    62  
    63  // Get is part of the dependency.Context interface.
    64  func (ctx *Context) Get(name string, outPtr interface{}) error {
    65  	resource, found := ctx.resources[name]
    66  	if !found {
    67  		return errors.Errorf("unexpected resource name: %s", name)
    68  	} else if resource.Error != nil {
    69  		return resource.Error
    70  	}
    71  	if outPtr != nil {
    72  		outPtrV := reflect.ValueOf(outPtr)
    73  		if outPtrV.Kind() != reflect.Ptr {
    74  			return errors.Errorf("outPtr should be a pointer; is %#v", outPtr)
    75  		}
    76  		outV := outPtrV.Elem()
    77  		outT := outV.Type()
    78  		setV := reflect.ValueOf(resource.Output)
    79  		setT := setV.Type()
    80  		if !setT.ConvertibleTo(outT) {
    81  			return errors.Errorf("cannot set %#v into %T", resource.Output, outPtr)
    82  		}
    83  		outV.Set(setV.Convert(outT))
    84  	}
    85  	return nil
    86  }