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
|
// This should be called before the ctx is ever used in order to ensure
|
||||||
// a consistent shadow state.
|
// a consistent shadow state.
|
||||||
func NewShadowEvalContext(ctx EvalContext) (EvalContext, ShadowEvalContext) {
|
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
|
// Copy the diff. We do this using some weird scoping so that the
|
||||||
// "diff" (real) value never leaks out and can be used.
|
// "diff" (real) value never leaks out and can be used.
|
||||||
var diffCopy *Diff
|
var diffCopy *Diff
|
||||||
{
|
{
|
||||||
diff, lock := ctx.Diff()
|
diff, lock := ctx.Diff()
|
||||||
|
if lock != nil {
|
||||||
lock.RLock()
|
lock.RLock()
|
||||||
diffCopy = diff
|
diffCopy = diff
|
||||||
// TODO: diffCopy = diff.DeepCopy()
|
// TODO: diffCopy = diff.DeepCopy()
|
||||||
lock.RUnlock()
|
lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the state. We do this using some weird scoping so that the
|
// Copy the state. We do this using some weird scoping so that the
|
||||||
// "state" (real) value never leaks out and can be used.
|
// "state" (real) value never leaks out and can be used.
|
||||||
var stateCopy *State
|
var stateCopy *State
|
||||||
{
|
{
|
||||||
state, lock := ctx.State()
|
state, lock := ctx.State()
|
||||||
|
if lock != nil {
|
||||||
lock.RLock()
|
lock.RLock()
|
||||||
stateCopy = state.DeepCopy()
|
stateCopy = state.DeepCopy()
|
||||||
lock.RUnlock()
|
lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build the shadow copy. For safety, we don't even give the shadow
|
// 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
|
// copy a reference to the real context. This means that it would be
|
||||||
// very difficult (impossible without some real obvious mistakes) for
|
// very difficult (impossible without some real obvious mistakes) for
|
||||||
// the shadow context to do "real" work.
|
// the shadow context to do "real" work.
|
||||||
shadow := &shadowEvalContextShadow{
|
shadow := &shadowEvalContextShadow{
|
||||||
|
Shared: &shared,
|
||||||
|
|
||||||
PathValue: ctx.Path(),
|
PathValue: ctx.Path(),
|
||||||
StateValue: stateCopy,
|
StateValue: stateCopy,
|
||||||
StateLock: new(sync.RWMutex),
|
StateLock: new(sync.RWMutex),
|
||||||
|
|
|
@ -1,10 +1,74 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShadowEvalContext_impl(t *testing.T) {
|
func TestShadowEvalContext_impl(t *testing.T) {
|
||||||
var _ EvalContext = new(shadowEvalContextReal)
|
var _ EvalContext = new(shadowEvalContextReal)
|
||||||
var _ EvalContext = new(shadowEvalContextShadow)
|
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