terraform: EvalContext.InitProvider (shadow) tests
This commit is contained in:
parent
fb96b0c422
commit
792a9f1de4
|
@ -43,34 +43,44 @@ type ShadowEvalContext interface {
|
|||
// This should be called before the ctx is ever used in order to ensure
|
||||
// a consistent shadow state.
|
||||
func NewShadowEvalContext(ctx EvalContext) (EvalContext, ShadowEvalContext) {
|
||||
real := &shadowEvalContextReal{EvalContext: ctx}
|
||||
var shared shadowEvalContextShared
|
||||
real := &shadowEvalContextReal{
|
||||
EvalContext: ctx,
|
||||
Shared: &shared,
|
||||
}
|
||||
|
||||
// Copy the diff. We do this using some weird scoping so that the
|
||||
// "diff" (real) value never leaks out and can be used.
|
||||
var diffCopy *Diff
|
||||
{
|
||||
diff, lock := ctx.Diff()
|
||||
if lock != nil {
|
||||
lock.RLock()
|
||||
diffCopy = diff
|
||||
// TODO: diffCopy = diff.DeepCopy()
|
||||
lock.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the state. We do this using some weird scoping so that the
|
||||
// "state" (real) value never leaks out and can be used.
|
||||
var stateCopy *State
|
||||
{
|
||||
state, lock := ctx.State()
|
||||
if lock != nil {
|
||||
lock.RLock()
|
||||
stateCopy = state.DeepCopy()
|
||||
lock.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Build the shadow copy. For safety, we don't even give the shadow
|
||||
// copy a reference to the real context. This means that it would be
|
||||
// very difficult (impossible without some real obvious mistakes) for
|
||||
// the shadow context to do "real" work.
|
||||
shadow := &shadowEvalContextShadow{
|
||||
Shared: &shared,
|
||||
|
||||
PathValue: ctx.Path(),
|
||||
StateValue: stateCopy,
|
||||
StateLock: new(sync.RWMutex),
|
||||
|
|
|
@ -1,10 +1,74 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestShadowEvalContext_impl(t *testing.T) {
|
||||
var _ EvalContext = new(shadowEvalContextReal)
|
||||
var _ EvalContext = new(shadowEvalContextShadow)
|
||||
}
|
||||
|
||||
func TestShadowEvalContextInitProvider(t *testing.T) {
|
||||
mock := new(MockEvalContext)
|
||||
real, shadow := NewShadowEvalContext(mock)
|
||||
|
||||
// Args, results
|
||||
name := "foo"
|
||||
mockResult := new(MockResourceProvider)
|
||||
|
||||
// Configure the mock
|
||||
mock.InitProviderProvider = mockResult
|
||||
|
||||
// Verify that it blocks until the real func is called
|
||||
var result ResourceProvider
|
||||
var err error
|
||||
doneCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
result, err = shadow.InitProvider(name)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-doneCh:
|
||||
t.Fatal("should block until finished")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
// Call the real func
|
||||
realResult, realErr := real.InitProvider(name)
|
||||
if realErr != nil {
|
||||
t.Fatalf("bad: %#v", realErr)
|
||||
}
|
||||
realResult.Configure(nil)
|
||||
if !mockResult.ConfigureCalled {
|
||||
t.Fatalf("bad: %#v", realResult)
|
||||
}
|
||||
mockResult.ConfigureCalled = false
|
||||
|
||||
// The shadow should finish now
|
||||
<-doneCh
|
||||
|
||||
// Verify the shadow returned the same values
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
|
||||
// Verify that the returned value is a shadow. Calling one function
|
||||
// shouldn't affect the other.
|
||||
result.Configure(nil)
|
||||
if mockResult.ConfigureCalled {
|
||||
t.Fatal("close should not be called")
|
||||
}
|
||||
|
||||
// And doing some work should result in that value
|
||||
mockErr := fmt.Errorf("yo")
|
||||
mockResult.ConfigureReturnError = mockErr
|
||||
realResult.Configure(nil)
|
||||
if err := result.Configure(nil); !reflect.DeepEqual(err, mockErr) {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue