terraform: EvalContext.InitProvider (shadow) tests

This commit is contained in:
Mitchell Hashimoto 2016-09-30 21:56:07 -07:00
parent fb96b0c422
commit 792a9f1de4
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
2 changed files with 82 additions and 8 deletions

View File

@ -43,17 +43,23 @@ 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()
lock.RLock()
diffCopy = diff
// TODO: diffCopy = diff.DeepCopy()
lock.RUnlock()
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
@ -61,9 +67,11 @@ func NewShadowEvalContext(ctx EvalContext) (EvalContext, ShadowEvalContext) {
var stateCopy *State
{
state, lock := ctx.State()
lock.RLock()
stateCopy = state.DeepCopy()
lock.RUnlock()
if lock != nil {
lock.RLock()
stateCopy = state.DeepCopy()
lock.RUnlock()
}
}
// Build the shadow copy. For safety, we don't even give the shadow
@ -71,6 +79,8 @@ func NewShadowEvalContext(ctx EvalContext) (EvalContext, ShadowEvalContext) {
// 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),

View File

@ -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)
}
}