github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/resource/testing_test.go (about)

     1  package resource
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"os"
     8  	"reflect"
     9  	"regexp"
    10  	"sort"
    11  	"strings"
    12  	"sync"
    13  	"sync/atomic"
    14  	"testing"
    15  
    16  	"github.com/hashicorp/go-multierror"
    17  	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
    18  	"github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery"
    19  	"github.com/hashicorp/terraform-plugin-sdk/terraform"
    20  )
    21  
    22  func init() {
    23  	testTesting = true
    24  
    25  	// TODO: Remove when we remove the guard on id checks
    26  	if err := os.Setenv("TF_ACC_IDONLY", "1"); err != nil {
    27  		panic(err)
    28  	}
    29  
    30  	if err := os.Setenv(TestEnvVar, "1"); err != nil {
    31  		panic(err)
    32  	}
    33  }
    34  
    35  // wrap the mock provider to implement TestProvider
    36  type resetProvider struct {
    37  	*terraform.MockResourceProvider
    38  	mu              sync.Mutex
    39  	TestResetCalled bool
    40  	TestResetError  error
    41  }
    42  
    43  func (p *resetProvider) TestReset() error {
    44  	p.mu.Lock()
    45  	defer p.mu.Unlock()
    46  	p.TestResetCalled = true
    47  	return p.TestResetError
    48  }
    49  
    50  func TestParallelTest(t *testing.T) {
    51  	mt := new(mockT)
    52  	ParallelTest(mt, TestCase{})
    53  
    54  	if !mt.ParallelCalled {
    55  		t.Fatal("Parallel() not called")
    56  	}
    57  }
    58  
    59  func TestTest(t *testing.T) {
    60  	t.Skip("test requires new provider implementation")
    61  
    62  	mp := &resetProvider{
    63  		MockResourceProvider: testProvider(),
    64  	}
    65  
    66  	mp.DiffReturn = nil
    67  
    68  	mp.ApplyFn = func(
    69  		info *terraform.InstanceInfo,
    70  		state *terraform.InstanceState,
    71  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
    72  		if !diff.Destroy {
    73  			return &terraform.InstanceState{
    74  				ID: "foo",
    75  			}, nil
    76  		}
    77  
    78  		return nil, nil
    79  	}
    80  
    81  	var refreshCount int32
    82  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
    83  		atomic.AddInt32(&refreshCount, 1)
    84  		return &terraform.InstanceState{ID: "foo"}, nil
    85  	}
    86  
    87  	checkDestroy := false
    88  	checkStep := false
    89  
    90  	checkDestroyFn := func(*terraform.State) error {
    91  		checkDestroy = true
    92  		return nil
    93  	}
    94  
    95  	checkStepFn := func(s *terraform.State) error {
    96  		checkStep = true
    97  
    98  		rs, ok := s.RootModule().Resources["test_instance.foo"]
    99  		if !ok {
   100  			t.Error("test_instance.foo is not present")
   101  			return nil
   102  		}
   103  		is := rs.Primary
   104  		if is.ID != "foo" {
   105  			t.Errorf("bad check ID: %s", is.ID)
   106  		}
   107  
   108  		return nil
   109  	}
   110  
   111  	mt := new(mockT)
   112  	Test(mt, TestCase{
   113  		Providers: map[string]terraform.ResourceProvider{
   114  			"test": mp,
   115  		},
   116  		CheckDestroy: checkDestroyFn,
   117  		Steps: []TestStep{
   118  			{
   119  				Config: testConfigStr,
   120  				Check:  checkStepFn,
   121  			},
   122  		},
   123  	})
   124  
   125  	if mt.failed() {
   126  		t.Fatalf("test failed: %s", mt.failMessage())
   127  	}
   128  	if mt.ParallelCalled {
   129  		t.Fatal("Parallel() called")
   130  	}
   131  	if !checkStep {
   132  		t.Fatal("didn't call check for step")
   133  	}
   134  	if !checkDestroy {
   135  		t.Fatal("didn't call check for destroy")
   136  	}
   137  	if !mp.TestResetCalled {
   138  		t.Fatal("didn't call TestReset")
   139  	}
   140  }
   141  
   142  func TestTest_plan_only(t *testing.T) {
   143  	t.Skip("test requires new provider implementation")
   144  
   145  	mp := testProvider()
   146  	mp.ApplyReturn = &terraform.InstanceState{
   147  		ID: "foo",
   148  	}
   149  
   150  	checkDestroy := false
   151  
   152  	checkDestroyFn := func(*terraform.State) error {
   153  		checkDestroy = true
   154  		return nil
   155  	}
   156  
   157  	mt := new(mockT)
   158  	Test(mt, TestCase{
   159  		Providers: map[string]terraform.ResourceProvider{
   160  			"test": mp,
   161  		},
   162  		CheckDestroy: checkDestroyFn,
   163  		Steps: []TestStep{
   164  			{
   165  				Config:             testConfigStr,
   166  				PlanOnly:           true,
   167  				ExpectNonEmptyPlan: false,
   168  			},
   169  		},
   170  	})
   171  
   172  	if !mt.failed() {
   173  		t.Fatal("test should've failed")
   174  	}
   175  
   176  	expected := `Step 0 error: After applying this step, the plan was not empty:
   177  
   178  DIFF:
   179  
   180  CREATE: test_instance.foo
   181    foo: "" => "bar"
   182  
   183  STATE:
   184  
   185  <no state>`
   186  
   187  	if mt.failMessage() != expected {
   188  		t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage())
   189  	}
   190  
   191  	if !checkDestroy {
   192  		t.Fatal("didn't call check for destroy")
   193  	}
   194  }
   195  
   196  func TestTest_idRefresh(t *testing.T) {
   197  	t.Skip("test requires new provider implementation")
   198  
   199  	// Refresh count should be 3:
   200  	//   1.) initial Ref/Plan/Apply
   201  	//   2.) post Ref/Plan/Apply for plan-check
   202  	//   3.) id refresh check
   203  	var expectedRefresh int32 = 3
   204  
   205  	mp := testProvider()
   206  	mp.DiffReturn = nil
   207  
   208  	mp.ApplyFn = func(
   209  		info *terraform.InstanceInfo,
   210  		state *terraform.InstanceState,
   211  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   212  		if !diff.Destroy {
   213  			return &terraform.InstanceState{
   214  				ID: "foo",
   215  			}, nil
   216  		}
   217  
   218  		return nil, nil
   219  	}
   220  
   221  	var refreshCount int32
   222  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   223  		atomic.AddInt32(&refreshCount, 1)
   224  		return &terraform.InstanceState{ID: "foo"}, nil
   225  	}
   226  
   227  	mt := new(mockT)
   228  	Test(mt, TestCase{
   229  		IDRefreshName: "test_instance.foo",
   230  		Providers: map[string]terraform.ResourceProvider{
   231  			"test": mp,
   232  		},
   233  		Steps: []TestStep{
   234  			{
   235  				Config: testConfigStr,
   236  			},
   237  		},
   238  	})
   239  
   240  	if mt.failed() {
   241  		t.Fatalf("test failed: %s", mt.failMessage())
   242  	}
   243  
   244  	// See declaration of expectedRefresh for why that number
   245  	if refreshCount != expectedRefresh {
   246  		t.Fatalf("bad refresh count: %d", refreshCount)
   247  	}
   248  }
   249  
   250  func TestTest_idRefreshCustomName(t *testing.T) {
   251  	t.Skip("test requires new provider implementation")
   252  
   253  	// Refresh count should be 3:
   254  	//   1.) initial Ref/Plan/Apply
   255  	//   2.) post Ref/Plan/Apply for plan-check
   256  	//   3.) id refresh check
   257  	var expectedRefresh int32 = 3
   258  
   259  	mp := testProvider()
   260  	mp.DiffReturn = nil
   261  
   262  	mp.ApplyFn = func(
   263  		info *terraform.InstanceInfo,
   264  		state *terraform.InstanceState,
   265  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   266  		if !diff.Destroy {
   267  			return &terraform.InstanceState{
   268  				ID: "foo",
   269  			}, nil
   270  		}
   271  
   272  		return nil, nil
   273  	}
   274  
   275  	var refreshCount int32
   276  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   277  		atomic.AddInt32(&refreshCount, 1)
   278  		return &terraform.InstanceState{ID: "foo"}, nil
   279  	}
   280  
   281  	mt := new(mockT)
   282  	Test(mt, TestCase{
   283  		IDRefreshName: "test_instance.foo",
   284  		Providers: map[string]terraform.ResourceProvider{
   285  			"test": mp,
   286  		},
   287  		Steps: []TestStep{
   288  			{
   289  				Config: testConfigStr,
   290  			},
   291  		},
   292  	})
   293  
   294  	if mt.failed() {
   295  		t.Fatalf("test failed: %s", mt.failMessage())
   296  	}
   297  
   298  	// See declaration of expectedRefresh for why that number
   299  	if refreshCount != expectedRefresh {
   300  		t.Fatalf("bad refresh count: %d", refreshCount)
   301  	}
   302  }
   303  
   304  func TestTest_idRefreshFail(t *testing.T) {
   305  	t.Skip("test requires new provider implementation")
   306  
   307  	// Refresh count should be 3:
   308  	//   1.) initial Ref/Plan/Apply
   309  	//   2.) post Ref/Plan/Apply for plan-check
   310  	//   3.) id refresh check
   311  	var expectedRefresh int32 = 3
   312  
   313  	mp := testProvider()
   314  	mp.DiffReturn = nil
   315  
   316  	mp.ApplyFn = func(
   317  		info *terraform.InstanceInfo,
   318  		state *terraform.InstanceState,
   319  		diff *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   320  		if !diff.Destroy {
   321  			return &terraform.InstanceState{
   322  				ID: "foo",
   323  			}, nil
   324  		}
   325  
   326  		return nil, nil
   327  	}
   328  
   329  	var refreshCount int32
   330  	mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) {
   331  		atomic.AddInt32(&refreshCount, 1)
   332  		if atomic.LoadInt32(&refreshCount) == expectedRefresh-1 {
   333  			return &terraform.InstanceState{
   334  				ID:         "foo",
   335  				Attributes: map[string]string{"foo": "bar"},
   336  			}, nil
   337  		} else if atomic.LoadInt32(&refreshCount) < expectedRefresh {
   338  			return &terraform.InstanceState{ID: "foo"}, nil
   339  		} else {
   340  			return nil, nil
   341  		}
   342  	}
   343  
   344  	mt := new(mockT)
   345  	Test(mt, TestCase{
   346  		IDRefreshName: "test_instance.foo",
   347  		Providers: map[string]terraform.ResourceProvider{
   348  			"test": mp,
   349  		},
   350  		Steps: []TestStep{
   351  			{
   352  				Config: testConfigStr,
   353  			},
   354  		},
   355  	})
   356  
   357  	if !mt.failed() {
   358  		t.Fatal("test didn't fail")
   359  	}
   360  	t.Logf("failure reason: %s", mt.failMessage())
   361  
   362  	// See declaration of expectedRefresh for why that number
   363  	if refreshCount != expectedRefresh {
   364  		t.Fatalf("bad refresh count: %d", refreshCount)
   365  	}
   366  }
   367  
   368  func TestTest_empty(t *testing.T) {
   369  	t.Skip("test requires new provider implementation")
   370  
   371  	destroyCalled := false
   372  	checkDestroyFn := func(*terraform.State) error {
   373  		destroyCalled = true
   374  		return nil
   375  	}
   376  
   377  	mt := new(mockT)
   378  	Test(mt, TestCase{
   379  		CheckDestroy: checkDestroyFn,
   380  	})
   381  
   382  	if mt.failed() {
   383  		t.Fatal("test failed")
   384  	}
   385  	if destroyCalled {
   386  		t.Fatal("should not call check destroy if there is no steps")
   387  	}
   388  }
   389  
   390  func TestTest_noEnv(t *testing.T) {
   391  	t.Skip("test requires new provider implementation")
   392  
   393  	// Unset the variable
   394  	if err := os.Setenv(TestEnvVar, ""); err != nil {
   395  		t.Fatalf("err: %s", err)
   396  	}
   397  	defer os.Setenv(TestEnvVar, "1")
   398  
   399  	mt := new(mockT)
   400  	Test(mt, TestCase{})
   401  
   402  	if !mt.SkipCalled {
   403  		t.Fatal("skip not called")
   404  	}
   405  }
   406  
   407  func TestTest_preCheck(t *testing.T) {
   408  	t.Skip("test requires new provider implementation")
   409  
   410  	called := false
   411  
   412  	mt := new(mockT)
   413  	Test(mt, TestCase{
   414  		PreCheck: func() { called = true },
   415  	})
   416  
   417  	if !called {
   418  		t.Fatal("precheck should be called")
   419  	}
   420  }
   421  
   422  func TestTest_skipFunc(t *testing.T) {
   423  	t.Skip("test requires new provider implementation")
   424  
   425  	preCheckCalled := false
   426  	skipped := false
   427  
   428  	mp := testProvider()
   429  	mp.ApplyReturn = &terraform.InstanceState{
   430  		ID: "foo",
   431  	}
   432  
   433  	checkStepFn := func(*terraform.State) error {
   434  		return fmt.Errorf("error")
   435  	}
   436  
   437  	mt := new(mockT)
   438  	Test(mt, TestCase{
   439  		Providers: map[string]terraform.ResourceProvider{
   440  			"test": mp,
   441  		},
   442  		PreCheck: func() { preCheckCalled = true },
   443  		Steps: []TestStep{
   444  			{
   445  				Config:   testConfigStr,
   446  				Check:    checkStepFn,
   447  				SkipFunc: func() (bool, error) { skipped = true; return true, nil },
   448  			},
   449  		},
   450  	})
   451  
   452  	if mt.failed() {
   453  		t.Fatal("Expected check to be skipped")
   454  	}
   455  
   456  	if !preCheckCalled {
   457  		t.Fatal("precheck should be called")
   458  	}
   459  	if !skipped {
   460  		t.Fatal("SkipFunc should be called")
   461  	}
   462  }
   463  
   464  func TestTest_stepError(t *testing.T) {
   465  	t.Skip("test requires new provider implementation")
   466  
   467  	mp := testProvider()
   468  	mp.ApplyReturn = &terraform.InstanceState{
   469  		ID: "foo",
   470  	}
   471  
   472  	checkDestroy := false
   473  
   474  	checkDestroyFn := func(*terraform.State) error {
   475  		checkDestroy = true
   476  		return nil
   477  	}
   478  
   479  	checkStepFn := func(*terraform.State) error {
   480  		return fmt.Errorf("error")
   481  	}
   482  
   483  	mt := new(mockT)
   484  	Test(mt, TestCase{
   485  		Providers: map[string]terraform.ResourceProvider{
   486  			"test": mp,
   487  		},
   488  		CheckDestroy: checkDestroyFn,
   489  		Steps: []TestStep{
   490  			{
   491  				Config: testConfigStr,
   492  				Check:  checkStepFn,
   493  			},
   494  		},
   495  	})
   496  
   497  	if !mt.failed() {
   498  		t.Fatal("test should've failed")
   499  	}
   500  	expected := "Step 0 error: Check failed: error"
   501  	if mt.failMessage() != expected {
   502  		t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage())
   503  	}
   504  
   505  	if !checkDestroy {
   506  		t.Fatal("didn't call check for destroy")
   507  	}
   508  }
   509  
   510  func TestTest_factoryError(t *testing.T) {
   511  	resourceFactoryError := fmt.Errorf("resource factory error")
   512  	factory := func() (terraform.ResourceProvider, error) {
   513  		return nil, resourceFactoryError
   514  	}
   515  	mt := new(mockT)
   516  
   517  	func() {
   518  		defer func() {
   519  			if r := recover(); r != nil {
   520  				if !strings.HasPrefix(r.(string), "mockT") {
   521  					panic(r)
   522  				}
   523  			}
   524  		}()
   525  		Test(mt, TestCase{
   526  			ProviderFactories: map[string]terraform.ResourceProviderFactory{
   527  				"test": factory,
   528  			},
   529  			IsUnitTest: true,
   530  		})
   531  	}()
   532  
   533  	if !mt.failed() {
   534  		t.Fatal("test should've failed")
   535  	}
   536  }
   537  
   538  func TestTest_resetError(t *testing.T) {
   539  	t.Skip("test requires new provider implementation")
   540  
   541  	mp := &resetProvider{
   542  		MockResourceProvider: testProvider(),
   543  		TestResetError:       fmt.Errorf("provider reset error"),
   544  	}
   545  
   546  	mt := new(mockT)
   547  	Test(mt, TestCase{
   548  		Providers: map[string]terraform.ResourceProvider{
   549  			"test": mp,
   550  		},
   551  		Steps: []TestStep{
   552  			{
   553  				ExpectError: regexp.MustCompile("provider reset error"),
   554  			},
   555  		},
   556  	})
   557  
   558  	if !mt.failed() {
   559  		t.Fatal("test should've failed")
   560  	}
   561  }
   562  
   563  func TestTest_expectError(t *testing.T) {
   564  	t.Skip("test requires new provider implementation")
   565  
   566  	cases := []struct {
   567  		name     string
   568  		planErr  bool
   569  		applyErr bool
   570  		badErr   bool
   571  	}{
   572  		{
   573  			name:     "successful apply",
   574  			planErr:  false,
   575  			applyErr: false,
   576  		},
   577  		{
   578  			name:     "bad plan",
   579  			planErr:  true,
   580  			applyErr: false,
   581  		},
   582  		{
   583  			name:     "bad apply",
   584  			planErr:  false,
   585  			applyErr: true,
   586  		},
   587  		{
   588  			name:     "bad plan, bad err",
   589  			planErr:  true,
   590  			applyErr: false,
   591  			badErr:   true,
   592  		},
   593  		{
   594  			name:     "bad apply, bad err",
   595  			planErr:  false,
   596  			applyErr: true,
   597  			badErr:   true,
   598  		},
   599  	}
   600  
   601  	for _, tc := range cases {
   602  		t.Run(tc.name, func(t *testing.T) {
   603  			mp := testProvider()
   604  			expectedText := "test provider error"
   605  			var errText string
   606  			if tc.badErr {
   607  				errText = "wrong provider error"
   608  			} else {
   609  				errText = expectedText
   610  			}
   611  			noErrText := "no error received, but expected a match to"
   612  			if tc.planErr {
   613  				mp.DiffReturnError = errors.New(errText)
   614  			}
   615  			if tc.applyErr {
   616  				mp.ApplyReturnError = errors.New(errText)
   617  			}
   618  			mt := new(mockT)
   619  			Test(mt, TestCase{
   620  				Providers: map[string]terraform.ResourceProvider{
   621  					"test": mp,
   622  				},
   623  				Steps: []TestStep{
   624  					{
   625  						Config:             testConfigStr,
   626  						ExpectError:        regexp.MustCompile(expectedText),
   627  						Check:              func(*terraform.State) error { return nil },
   628  						ExpectNonEmptyPlan: true,
   629  					},
   630  				},
   631  			},
   632  			)
   633  			if mt.FatalCalled {
   634  				t.Fatalf("fatal: %+v", mt.FatalArgs)
   635  			}
   636  			switch {
   637  			case len(mt.ErrorArgs) < 1 && !tc.planErr && !tc.applyErr:
   638  				t.Fatalf("expected error, got none")
   639  			case !tc.planErr && !tc.applyErr:
   640  				for _, e := range mt.ErrorArgs {
   641  					if regexp.MustCompile(noErrText).MatchString(fmt.Sprintf("%v", e)) {
   642  						return
   643  					}
   644  				}
   645  				t.Fatalf("expected error to match %s, got %+v", noErrText, mt.ErrorArgs)
   646  			case tc.badErr:
   647  				for _, e := range mt.ErrorArgs {
   648  					if regexp.MustCompile(expectedText).MatchString(fmt.Sprintf("%v", e)) {
   649  						return
   650  					}
   651  				}
   652  				t.Fatalf("expected error to match %s, got %+v", expectedText, mt.ErrorArgs)
   653  			}
   654  		})
   655  	}
   656  }
   657  
   658  func TestComposeAggregateTestCheckFunc(t *testing.T) {
   659  	check1 := func(s *terraform.State) error {
   660  		return errors.New("Error 1")
   661  	}
   662  
   663  	check2 := func(s *terraform.State) error {
   664  		return errors.New("Error 2")
   665  	}
   666  
   667  	f := ComposeAggregateTestCheckFunc(check1, check2)
   668  	err := f(nil)
   669  	if err == nil {
   670  		t.Fatalf("Expected errors")
   671  	}
   672  
   673  	multi := err.(*multierror.Error)
   674  	if !strings.Contains(multi.Errors[0].Error(), "Error 1") {
   675  		t.Fatalf("Expected Error 1, Got %s", multi.Errors[0])
   676  	}
   677  	if !strings.Contains(multi.Errors[1].Error(), "Error 2") {
   678  		t.Fatalf("Expected Error 2, Got %s", multi.Errors[1])
   679  	}
   680  }
   681  
   682  func TestComposeTestCheckFunc(t *testing.T) {
   683  	cases := []struct {
   684  		F      []TestCheckFunc
   685  		Result string
   686  	}{
   687  		{
   688  			F: []TestCheckFunc{
   689  				func(*terraform.State) error { return nil },
   690  			},
   691  			Result: "",
   692  		},
   693  
   694  		{
   695  			F: []TestCheckFunc{
   696  				func(*terraform.State) error {
   697  					return fmt.Errorf("error")
   698  				},
   699  				func(*terraform.State) error { return nil },
   700  			},
   701  			Result: "Check 1/2 error: error",
   702  		},
   703  
   704  		{
   705  			F: []TestCheckFunc{
   706  				func(*terraform.State) error { return nil },
   707  				func(*terraform.State) error {
   708  					return fmt.Errorf("error")
   709  				},
   710  			},
   711  			Result: "Check 2/2 error: error",
   712  		},
   713  
   714  		{
   715  			F: []TestCheckFunc{
   716  				func(*terraform.State) error { return nil },
   717  				func(*terraform.State) error { return nil },
   718  			},
   719  			Result: "",
   720  		},
   721  	}
   722  
   723  	for i, tc := range cases {
   724  		f := ComposeTestCheckFunc(tc.F...)
   725  		err := f(nil)
   726  		if err == nil {
   727  			err = fmt.Errorf("")
   728  		}
   729  		if tc.Result != err.Error() {
   730  			t.Fatalf("Case %d bad: %s", i, err)
   731  		}
   732  	}
   733  }
   734  
   735  // mockT implements TestT for testing
   736  type mockT struct {
   737  	ErrorCalled    bool
   738  	ErrorArgs      []interface{}
   739  	FatalCalled    bool
   740  	FatalArgs      []interface{}
   741  	ParallelCalled bool
   742  	SkipCalled     bool
   743  	SkipArgs       []interface{}
   744  
   745  	f bool
   746  }
   747  
   748  func (t *mockT) Error(args ...interface{}) {
   749  	t.ErrorCalled = true
   750  	t.ErrorArgs = args
   751  	t.f = true
   752  }
   753  
   754  func (t *mockT) Fatal(args ...interface{}) {
   755  	t.FatalCalled = true
   756  	t.FatalArgs = args
   757  	t.f = true
   758  
   759  	panic("mockT.Fatal")
   760  }
   761  
   762  func (t *mockT) Parallel() {
   763  	t.ParallelCalled = true
   764  }
   765  
   766  func (t *mockT) Skip(args ...interface{}) {
   767  	t.SkipCalled = true
   768  	t.SkipArgs = args
   769  	t.f = true
   770  }
   771  
   772  func (t *mockT) Name() string {
   773  	return "MockedName"
   774  }
   775  
   776  func (t *mockT) failed() bool {
   777  	return t.f
   778  }
   779  
   780  func (t *mockT) failMessage() string {
   781  	if t.FatalCalled {
   782  		return t.FatalArgs[0].(string)
   783  	} else if t.ErrorCalled {
   784  		return t.ErrorArgs[0].(string)
   785  	} else if t.SkipCalled {
   786  		return t.SkipArgs[0].(string)
   787  	}
   788  
   789  	return "unknown"
   790  }
   791  
   792  func testProvider() *terraform.MockResourceProvider {
   793  	mp := new(terraform.MockResourceProvider)
   794  	mp.DiffReturn = &terraform.InstanceDiff{
   795  		Attributes: map[string]*terraform.ResourceAttrDiff{
   796  			"foo": {
   797  				New: "bar",
   798  			},
   799  		},
   800  	}
   801  	mp.ResourcesReturn = []terraform.ResourceType{
   802  		{Name: "test_instance"},
   803  	}
   804  
   805  	return mp
   806  }
   807  
   808  func TestTest_Main(t *testing.T) {
   809  	flag.Parse()
   810  	if *flagSweep == "" {
   811  		// Tests for the TestMain method used for Sweepers will panic without the -sweep
   812  		// flag specified. Mock the value for now
   813  		*flagSweep = "us-east-1"
   814  	}
   815  
   816  	cases := []struct {
   817  		Name            string
   818  		Sweepers        map[string]*Sweeper
   819  		ExpectedRunList []string
   820  		SweepRun        string
   821  	}{
   822  		{
   823  			Name: "basic passing",
   824  			Sweepers: map[string]*Sweeper{
   825  				"aws_dummy": {
   826  					Name: "aws_dummy",
   827  					F:    mockSweeperFunc,
   828  				},
   829  			},
   830  			ExpectedRunList: []string{"aws_dummy"},
   831  		},
   832  	}
   833  
   834  	for _, tc := range cases {
   835  		// reset sweepers
   836  		sweeperFuncs = map[string]*Sweeper{}
   837  
   838  		t.Run(tc.Name, func(t *testing.T) {
   839  			for n, s := range tc.Sweepers {
   840  				AddTestSweepers(n, s)
   841  			}
   842  			*flagSweepRun = tc.SweepRun
   843  
   844  			// Verify it does not exit/panic
   845  			TestMain(&testing.M{})
   846  		})
   847  	}
   848  }
   849  
   850  func TestFilterSweepers(t *testing.T) {
   851  	cases := []struct {
   852  		Name             string
   853  		Sweepers         map[string]*Sweeper
   854  		Filter           string
   855  		ExpectedSweepers []string
   856  	}{
   857  		{
   858  			Name: "normal",
   859  			Sweepers: map[string]*Sweeper{
   860  				"aws_dummy": {
   861  					Name: "aws_dummy",
   862  					F:    mockSweeperFunc,
   863  				},
   864  			},
   865  			ExpectedSweepers: []string{"aws_dummy"},
   866  		},
   867  		{
   868  			Name: "with dep",
   869  			Sweepers: map[string]*Sweeper{
   870  				"aws_dummy": {
   871  					Name: "aws_dummy",
   872  					F:    mockSweeperFunc,
   873  				},
   874  				"aws_top": {
   875  					Name:         "aws_top",
   876  					Dependencies: []string{"aws_sub"},
   877  					F:            mockSweeperFunc,
   878  				},
   879  				"aws_sub": {
   880  					Name: "aws_sub",
   881  					F:    mockSweeperFunc,
   882  				},
   883  			},
   884  			ExpectedSweepers: []string{"aws_dummy", "aws_sub", "aws_top"},
   885  		},
   886  		{
   887  			Name: "with filter",
   888  			Sweepers: map[string]*Sweeper{
   889  				"aws_dummy": {
   890  					Name: "aws_dummy",
   891  					F:    mockSweeperFunc,
   892  				},
   893  				"aws_top": {
   894  					Name:         "aws_top",
   895  					Dependencies: []string{"aws_sub"},
   896  					F:            mockSweeperFunc,
   897  				},
   898  				"aws_sub": {
   899  					Name: "aws_sub",
   900  					F:    mockSweeperFunc,
   901  				},
   902  			},
   903  			ExpectedSweepers: []string{"aws_dummy"},
   904  			Filter:           "aws_dummy",
   905  		},
   906  		{
   907  			Name: "with two filters",
   908  			Sweepers: map[string]*Sweeper{
   909  				"aws_dummy": {
   910  					Name: "aws_dummy",
   911  					F:    mockSweeperFunc,
   912  				},
   913  				"aws_top": {
   914  					Name:         "aws_top",
   915  					Dependencies: []string{"aws_sub"},
   916  					F:            mockSweeperFunc,
   917  				},
   918  				"aws_sub": {
   919  					Name: "aws_sub",
   920  					F:    mockSweeperFunc,
   921  				},
   922  			},
   923  			ExpectedSweepers: []string{"aws_dummy", "aws_sub"},
   924  			Filter:           "aws_dummy,aws_sub",
   925  		},
   926  		{
   927  			Name: "with dep and filter",
   928  			Sweepers: map[string]*Sweeper{
   929  				"aws_dummy": {
   930  					Name: "aws_dummy",
   931  					F:    mockSweeperFunc,
   932  				},
   933  				"aws_top": {
   934  					Name:         "aws_top",
   935  					Dependencies: []string{"aws_sub"},
   936  					F:            mockSweeperFunc,
   937  				},
   938  				"aws_sub": {
   939  					Name: "aws_sub",
   940  					F:    mockSweeperFunc,
   941  				},
   942  			},
   943  			ExpectedSweepers: []string{"aws_sub", "aws_top"},
   944  			Filter:           "aws_top",
   945  		},
   946  		{
   947  			Name: "with non-matching filter",
   948  			Sweepers: map[string]*Sweeper{
   949  				"aws_dummy": {
   950  					Name: "aws_dummy",
   951  					F:    mockSweeperFunc,
   952  				},
   953  				"aws_top": {
   954  					Name:         "aws_top",
   955  					Dependencies: []string{"aws_sub"},
   956  					F:            mockSweeperFunc,
   957  				},
   958  				"aws_sub": {
   959  					Name: "aws_sub",
   960  					F:    mockSweeperFunc,
   961  				},
   962  			},
   963  			Filter: "none",
   964  		},
   965  		{
   966  			Name: "with nested depenencies and top level filter",
   967  			Sweepers: map[string]*Sweeper{
   968  				"not_matching": {
   969  					Name: "not_matching",
   970  					F:    mockSweeperFunc,
   971  				},
   972  				"matching_level1": {
   973  					Name:         "matching_level1",
   974  					Dependencies: []string{"matching_level2"},
   975  					F:            mockSweeperFunc,
   976  				},
   977  				"matching_level2": {
   978  					Name:         "matching_level2",
   979  					Dependencies: []string{"matching_level3"},
   980  					F:            mockSweeperFunc,
   981  				},
   982  				"matching_level3": {
   983  					Name: "matching_level3",
   984  					F:    mockSweeperFunc,
   985  				},
   986  			},
   987  			ExpectedSweepers: []string{"matching_level1", "matching_level2", "matching_level3"},
   988  			Filter:           "matching_level1",
   989  		},
   990  		{
   991  			Name: "with nested depenencies and middle level filter",
   992  			Sweepers: map[string]*Sweeper{
   993  				"not_matching": {
   994  					Name: "not_matching",
   995  					F:    mockSweeperFunc,
   996  				},
   997  				"matching_level1": {
   998  					Name:         "matching_level1",
   999  					Dependencies: []string{"matching_level2"},
  1000  					F:            mockSweeperFunc,
  1001  				},
  1002  				"matching_level2": {
  1003  					Name:         "matching_level2",
  1004  					Dependencies: []string{"matching_level3"},
  1005  					F:            mockSweeperFunc,
  1006  				},
  1007  				"matching_level3": {
  1008  					Name: "matching_level3",
  1009  					F:    mockSweeperFunc,
  1010  				},
  1011  			},
  1012  			ExpectedSweepers: []string{"matching_level2", "matching_level3"},
  1013  			Filter:           "matching_level2",
  1014  		},
  1015  		{
  1016  			Name: "with nested depenencies and bottom level filter",
  1017  			Sweepers: map[string]*Sweeper{
  1018  				"not_matching": {
  1019  					Name: "not_matching",
  1020  					F:    mockSweeperFunc,
  1021  				},
  1022  				"matching_level1": {
  1023  					Name:         "matching_level1",
  1024  					Dependencies: []string{"matching_level2"},
  1025  					F:            mockSweeperFunc,
  1026  				},
  1027  				"matching_level2": {
  1028  					Name:         "matching_level2",
  1029  					Dependencies: []string{"matching_level3"},
  1030  					F:            mockSweeperFunc,
  1031  				},
  1032  				"matching_level3": {
  1033  					Name: "matching_level3",
  1034  					F:    mockSweeperFunc,
  1035  				},
  1036  			},
  1037  			ExpectedSweepers: []string{"matching_level3"},
  1038  			Filter:           "matching_level3",
  1039  		},
  1040  	}
  1041  
  1042  	for _, tc := range cases {
  1043  		// reset sweepers
  1044  		sweeperFuncs = map[string]*Sweeper{}
  1045  
  1046  		t.Run(tc.Name, func(t *testing.T) {
  1047  			actualSweepers := filterSweepers(tc.Filter, tc.Sweepers)
  1048  
  1049  			var keys []string
  1050  			for k := range actualSweepers {
  1051  				keys = append(keys, k)
  1052  			}
  1053  
  1054  			sort.Strings(keys)
  1055  			sort.Strings(tc.ExpectedSweepers)
  1056  			if !reflect.DeepEqual(keys, tc.ExpectedSweepers) {
  1057  				t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedSweepers, keys)
  1058  			}
  1059  		})
  1060  	}
  1061  }
  1062  
  1063  func TestFilterSweeperWithDependencies(t *testing.T) {
  1064  	cases := []struct {
  1065  		Name             string
  1066  		Sweepers         map[string]*Sweeper
  1067  		StartingSweeper  string
  1068  		ExpectedSweepers []string
  1069  	}{
  1070  		{
  1071  			Name: "no dependencies",
  1072  			Sweepers: map[string]*Sweeper{
  1073  				"matching_level1": {
  1074  					Name: "matching_level1",
  1075  					F:    mockSweeperFunc,
  1076  				},
  1077  				"non_matching": {
  1078  					Name: "non_matching",
  1079  					F:    mockSweeperFunc,
  1080  				},
  1081  			},
  1082  			StartingSweeper:  "matching_level1",
  1083  			ExpectedSweepers: []string{"matching_level1"},
  1084  		},
  1085  		{
  1086  			Name: "one level one dependency",
  1087  			Sweepers: map[string]*Sweeper{
  1088  				"matching_level1": {
  1089  					Name:         "matching_level1",
  1090  					Dependencies: []string{"matching_level2"},
  1091  					F:            mockSweeperFunc,
  1092  				},
  1093  				"matching_level2": {
  1094  					Name: "matching_level2",
  1095  					F:    mockSweeperFunc,
  1096  				},
  1097  			},
  1098  			StartingSweeper:  "matching_level1",
  1099  			ExpectedSweepers: []string{"matching_level1", "matching_level2"},
  1100  		},
  1101  		{
  1102  			Name: "one level multiple dependencies",
  1103  			Sweepers: map[string]*Sweeper{
  1104  				"matching_level1": {
  1105  					Name:         "matching_level1",
  1106  					Dependencies: []string{"matching_level2a", "matching_level2b"},
  1107  					F:            mockSweeperFunc,
  1108  				},
  1109  				"matching_level2a": {
  1110  					Name: "matching_level2a",
  1111  					F:    mockSweeperFunc,
  1112  				},
  1113  				"matching_level2b": {
  1114  					Name: "matching_level2b",
  1115  					F:    mockSweeperFunc,
  1116  				},
  1117  			},
  1118  			StartingSweeper:  "matching_level1",
  1119  			ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b"},
  1120  		},
  1121  		{
  1122  			Name: "multiple level one dependency",
  1123  			Sweepers: map[string]*Sweeper{
  1124  				"matching_level1": {
  1125  					Name:         "matching_level1",
  1126  					Dependencies: []string{"matching_level2"},
  1127  					F:            mockSweeperFunc,
  1128  				},
  1129  				"matching_level2": {
  1130  					Name:         "matching_level2",
  1131  					Dependencies: []string{"matching_level3"},
  1132  					F:            mockSweeperFunc,
  1133  				},
  1134  				"matching_level3": {
  1135  					Name: "matching_level3",
  1136  					F:    mockSweeperFunc,
  1137  				},
  1138  			},
  1139  			StartingSweeper:  "matching_level1",
  1140  			ExpectedSweepers: []string{"matching_level1", "matching_level2", "matching_level3"},
  1141  		},
  1142  		{
  1143  			Name: "multiple level multiple dependencies",
  1144  			Sweepers: map[string]*Sweeper{
  1145  				"matching_level1": {
  1146  					Name:         "matching_level1",
  1147  					Dependencies: []string{"matching_level2a", "matching_level2b"},
  1148  					F:            mockSweeperFunc,
  1149  				},
  1150  				"matching_level2a": {
  1151  					Name:         "matching_level2a",
  1152  					Dependencies: []string{"matching_level3a", "matching_level3b"},
  1153  					F:            mockSweeperFunc,
  1154  				},
  1155  				"matching_level2b": {
  1156  					Name:         "matching_level2b",
  1157  					Dependencies: []string{"matching_level3c", "matching_level3d"},
  1158  					F:            mockSweeperFunc,
  1159  				},
  1160  				"matching_level3a": {
  1161  					Name: "matching_level3a",
  1162  					F:    mockSweeperFunc,
  1163  				},
  1164  				"matching_level3b": {
  1165  					Name: "matching_level3b",
  1166  					F:    mockSweeperFunc,
  1167  				},
  1168  				"matching_level3c": {
  1169  					Name: "matching_level3c",
  1170  					F:    mockSweeperFunc,
  1171  				},
  1172  				"matching_level3d": {
  1173  					Name: "matching_level3d",
  1174  					F:    mockSweeperFunc,
  1175  				},
  1176  			},
  1177  			StartingSweeper:  "matching_level1",
  1178  			ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b", "matching_level3a", "matching_level3b", "matching_level3c", "matching_level3d"},
  1179  		},
  1180  		{
  1181  			Name: "no parents one level",
  1182  			Sweepers: map[string]*Sweeper{
  1183  				"matching_level1": {
  1184  					Name:         "matching_level1",
  1185  					Dependencies: []string{"matching_level2"},
  1186  					F:            mockSweeperFunc,
  1187  				},
  1188  				"matching_level2": {
  1189  					Name:         "matching_level2",
  1190  					Dependencies: []string{"matching_level3"},
  1191  					F:            mockSweeperFunc,
  1192  				},
  1193  				"matching_level3": {
  1194  					Name: "matching_level3",
  1195  					F:    mockSweeperFunc,
  1196  				},
  1197  			},
  1198  			StartingSweeper:  "matching_level2",
  1199  			ExpectedSweepers: []string{"matching_level2", "matching_level3"},
  1200  		},
  1201  		{
  1202  			Name: "no parents multiple level",
  1203  			Sweepers: map[string]*Sweeper{
  1204  				"matching_level1": {
  1205  					Name:         "matching_level1",
  1206  					Dependencies: []string{"matching_level2"},
  1207  					F:            mockSweeperFunc,
  1208  				},
  1209  				"matching_level2": {
  1210  					Name:         "matching_level2",
  1211  					Dependencies: []string{"matching_level3"},
  1212  					F:            mockSweeperFunc,
  1213  				},
  1214  				"matching_level3": {
  1215  					Name: "matching_level3",
  1216  					F:    mockSweeperFunc,
  1217  				},
  1218  			},
  1219  			StartingSweeper:  "matching_level3",
  1220  			ExpectedSweepers: []string{"matching_level3"},
  1221  		},
  1222  		{
  1223  			Name: "one level missing dependency",
  1224  			Sweepers: map[string]*Sweeper{
  1225  				"matching_level1": {
  1226  					Name:         "matching_level1",
  1227  					Dependencies: []string{"matching_level2a", "matching_level2c"},
  1228  					F:            mockSweeperFunc,
  1229  				},
  1230  				"matching_level2a": {
  1231  					Name: "matching_level2a",
  1232  					F:    mockSweeperFunc,
  1233  				},
  1234  				"matching_level2b": {
  1235  					Name: "matching_level2b",
  1236  					F:    mockSweeperFunc,
  1237  				},
  1238  			},
  1239  			StartingSweeper:  "matching_level1",
  1240  			ExpectedSweepers: []string{"matching_level1", "matching_level2a"},
  1241  		},
  1242  		{
  1243  			Name: "multiple level missing dependencies",
  1244  			Sweepers: map[string]*Sweeper{
  1245  				"matching_level1": {
  1246  					Name:         "matching_level1",
  1247  					Dependencies: []string{"matching_level2a", "matching_level2b", "matching_level2c"},
  1248  					F:            mockSweeperFunc,
  1249  				},
  1250  				"matching_level2a": {
  1251  					Name:         "matching_level2a",
  1252  					Dependencies: []string{"matching_level3a", "matching_level3e"},
  1253  					F:            mockSweeperFunc,
  1254  				},
  1255  				"matching_level2b": {
  1256  					Name:         "matching_level2b",
  1257  					Dependencies: []string{"matching_level3c", "matching_level3f"},
  1258  					F:            mockSweeperFunc,
  1259  				},
  1260  				"matching_level3a": {
  1261  					Name: "matching_level3a",
  1262  					F:    mockSweeperFunc,
  1263  				},
  1264  				"matching_level3b": {
  1265  					Name: "matching_level3b",
  1266  					F:    mockSweeperFunc,
  1267  				},
  1268  				"matching_level3c": {
  1269  					Name: "matching_level3c",
  1270  					F:    mockSweeperFunc,
  1271  				},
  1272  				"matching_level3d": {
  1273  					Name: "matching_level3d",
  1274  					F:    mockSweeperFunc,
  1275  				},
  1276  			},
  1277  			StartingSweeper:  "matching_level1",
  1278  			ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b", "matching_level3a", "matching_level3c"},
  1279  		},
  1280  	}
  1281  
  1282  	for _, tc := range cases {
  1283  		// reset sweepers
  1284  		sweeperFuncs = map[string]*Sweeper{}
  1285  
  1286  		t.Run(tc.Name, func(t *testing.T) {
  1287  			actualSweepers := filterSweeperWithDependencies(tc.StartingSweeper, tc.Sweepers)
  1288  
  1289  			var keys []string
  1290  			for k := range actualSweepers {
  1291  				keys = append(keys, k)
  1292  			}
  1293  
  1294  			sort.Strings(keys)
  1295  			sort.Strings(tc.ExpectedSweepers)
  1296  			if !reflect.DeepEqual(keys, tc.ExpectedSweepers) {
  1297  				t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedSweepers, keys)
  1298  			}
  1299  		})
  1300  	}
  1301  }
  1302  
  1303  func TestRunSweepers(t *testing.T) {
  1304  	cases := []struct {
  1305  		Name            string
  1306  		Sweepers        map[string]*Sweeper
  1307  		ExpectedRunList []string
  1308  		SweepRun        string
  1309  		AllowFailures   bool
  1310  		ExpectError     bool
  1311  	}{
  1312  		{
  1313  			Name: "single",
  1314  			Sweepers: map[string]*Sweeper{
  1315  				"aws_dummy": {
  1316  					Name: "aws_dummy",
  1317  					F:    mockSweeperFunc,
  1318  				},
  1319  			},
  1320  			ExpectedRunList: []string{"aws_dummy"},
  1321  		},
  1322  		{
  1323  			Name: "multiple",
  1324  			Sweepers: map[string]*Sweeper{
  1325  				"aws_one": {
  1326  					Name: "aws_one",
  1327  					F:    mockSweeperFunc,
  1328  				},
  1329  				"aws_two": {
  1330  					Name: "aws_two",
  1331  					F:    mockSweeperFunc,
  1332  				},
  1333  			},
  1334  			ExpectedRunList: []string{"aws_one", "aws_two"},
  1335  		},
  1336  		{
  1337  			Name: "multiple with dep",
  1338  			Sweepers: map[string]*Sweeper{
  1339  				"aws_dummy": {
  1340  					Name: "aws_dummy",
  1341  					F:    mockSweeperFunc,
  1342  				},
  1343  				"aws_top": {
  1344  					Name:         "aws_top",
  1345  					Dependencies: []string{"aws_sub"},
  1346  					F:            mockSweeperFunc,
  1347  				},
  1348  				"aws_sub": {
  1349  					Name: "aws_sub",
  1350  					F:    mockSweeperFunc,
  1351  				},
  1352  			},
  1353  			ExpectedRunList: []string{"aws_dummy", "aws_sub", "aws_top"},
  1354  		},
  1355  		{
  1356  			Name: "failing dep",
  1357  			Sweepers: map[string]*Sweeper{
  1358  				"aws_top": {
  1359  					Name:         "aws_top",
  1360  					Dependencies: []string{"aws_sub"},
  1361  					F:            mockSweeperFunc,
  1362  				},
  1363  				"aws_sub": {
  1364  					Name: "aws_sub",
  1365  					F:    mockFailingSweeperFunc,
  1366  				},
  1367  			},
  1368  			ExpectedRunList: []string{"aws_sub"},
  1369  			ExpectError:     true,
  1370  		},
  1371  		{
  1372  			Name: "failing dep allow failures",
  1373  			Sweepers: map[string]*Sweeper{
  1374  				"aws_top": {
  1375  					Name:         "aws_top",
  1376  					Dependencies: []string{"aws_sub"},
  1377  					F:            mockSweeperFunc,
  1378  				},
  1379  				"aws_sub": {
  1380  					Name: "aws_sub",
  1381  					F:    mockFailingSweeperFunc,
  1382  				},
  1383  			},
  1384  			ExpectedRunList: []string{"aws_sub", "aws_top"},
  1385  			AllowFailures:   true,
  1386  			ExpectError:     true,
  1387  		},
  1388  		{
  1389  			Name: "failing top",
  1390  			Sweepers: map[string]*Sweeper{
  1391  				"aws_top": {
  1392  					Name:         "aws_top",
  1393  					Dependencies: []string{"aws_sub"},
  1394  					F:            mockFailingSweeperFunc,
  1395  				},
  1396  				"aws_sub": {
  1397  					Name: "aws_sub",
  1398  					F:    mockSweeperFunc,
  1399  				},
  1400  			},
  1401  			ExpectedRunList: []string{"aws_sub", "aws_top"},
  1402  			ExpectError:     true,
  1403  		},
  1404  		{
  1405  			Name: "failing top allow failures",
  1406  			Sweepers: map[string]*Sweeper{
  1407  				"aws_top": {
  1408  					Name:         "aws_top",
  1409  					Dependencies: []string{"aws_sub"},
  1410  					F:            mockFailingSweeperFunc,
  1411  				},
  1412  				"aws_sub": {
  1413  					Name: "aws_sub",
  1414  					F:    mockSweeperFunc,
  1415  				},
  1416  			},
  1417  			ExpectedRunList: []string{"aws_sub", "aws_top"},
  1418  			AllowFailures:   true,
  1419  			ExpectError:     true,
  1420  		},
  1421  		{
  1422  			Name: "failing top and dep",
  1423  			Sweepers: map[string]*Sweeper{
  1424  				"aws_top": {
  1425  					Name:         "aws_top",
  1426  					Dependencies: []string{"aws_sub"},
  1427  					F:            mockFailingSweeperFunc,
  1428  				},
  1429  				"aws_sub": {
  1430  					Name: "aws_sub",
  1431  					F:    mockFailingSweeperFunc,
  1432  				},
  1433  			},
  1434  			ExpectedRunList: []string{"aws_sub"},
  1435  			ExpectError:     true,
  1436  		},
  1437  		{
  1438  			Name: "failing top and dep allow failues",
  1439  			Sweepers: map[string]*Sweeper{
  1440  				"aws_top": {
  1441  					Name:         "aws_top",
  1442  					Dependencies: []string{"aws_sub"},
  1443  					F:            mockFailingSweeperFunc,
  1444  				},
  1445  				"aws_sub": {
  1446  					Name: "aws_sub",
  1447  					F:    mockFailingSweeperFunc,
  1448  				},
  1449  			},
  1450  			ExpectedRunList: []string{"aws_sub", "aws_top"},
  1451  			AllowFailures:   true,
  1452  			ExpectError:     true,
  1453  		},
  1454  	}
  1455  
  1456  	for _, tc := range cases {
  1457  		// reset sweepers
  1458  		sweeperFuncs = map[string]*Sweeper{}
  1459  
  1460  		t.Run(tc.Name, func(t *testing.T) {
  1461  			sweeperRunList, err := runSweepers([]string{"test"}, tc.Sweepers, tc.AllowFailures)
  1462  			fmt.Printf("sweeperRunList: %#v\n", sweeperRunList)
  1463  
  1464  			if err == nil && tc.ExpectError {
  1465  				t.Fatalf("expected error, did not receive error")
  1466  			}
  1467  
  1468  			if err != nil && !tc.ExpectError {
  1469  				t.Fatalf("did not expect error, received error: %s", err)
  1470  			}
  1471  
  1472  			// get list of tests ran from sweeperRunList keys
  1473  			var keys []string
  1474  			for k := range sweeperRunList["test"] {
  1475  				keys = append(keys, k)
  1476  			}
  1477  
  1478  			sort.Strings(keys)
  1479  			sort.Strings(tc.ExpectedRunList)
  1480  			if !reflect.DeepEqual(keys, tc.ExpectedRunList) {
  1481  				t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedRunList, keys)
  1482  			}
  1483  		})
  1484  	}
  1485  }
  1486  
  1487  func mockFailingSweeperFunc(s string) error {
  1488  	return errors.New("failing sweeper")
  1489  }
  1490  
  1491  func mockSweeperFunc(s string) error {
  1492  	return nil
  1493  }
  1494  
  1495  func TestTest_Taint(t *testing.T) {
  1496  	t.Skip("test requires new provider implementation")
  1497  
  1498  	mp := testProvider()
  1499  	mp.DiffFn = func(
  1500  		_ *terraform.InstanceInfo,
  1501  		state *terraform.InstanceState,
  1502  		_ *terraform.ResourceConfig,
  1503  	) (*terraform.InstanceDiff, error) {
  1504  		return &terraform.InstanceDiff{
  1505  			DestroyTainted: state.Tainted,
  1506  		}, nil
  1507  	}
  1508  
  1509  	mp.ApplyFn = func(
  1510  		info *terraform.InstanceInfo,
  1511  		state *terraform.InstanceState,
  1512  		diff *terraform.InstanceDiff,
  1513  	) (*terraform.InstanceState, error) {
  1514  		var id string
  1515  		switch {
  1516  		case diff.Destroy && !diff.DestroyTainted:
  1517  			return nil, nil
  1518  		case diff.DestroyTainted:
  1519  			id = "tainted"
  1520  		default:
  1521  			id = "not_tainted"
  1522  		}
  1523  
  1524  		return &terraform.InstanceState{
  1525  			ID: id,
  1526  		}, nil
  1527  	}
  1528  
  1529  	mp.RefreshFn = func(
  1530  		_ *terraform.InstanceInfo,
  1531  		state *terraform.InstanceState,
  1532  	) (*terraform.InstanceState, error) {
  1533  		return state, nil
  1534  	}
  1535  
  1536  	mt := new(mockT)
  1537  	Test(mt, TestCase{
  1538  		Providers: map[string]terraform.ResourceProvider{
  1539  			"test": mp,
  1540  		},
  1541  		Steps: []TestStep{
  1542  			{
  1543  				Config: testConfigStr,
  1544  				Check: func(s *terraform.State) error {
  1545  					rs := s.RootModule().Resources["test_instance.foo"]
  1546  					if rs.Primary.ID != "not_tainted" {
  1547  						return fmt.Errorf("expected not_tainted, got %s", rs.Primary.ID)
  1548  					}
  1549  					return nil
  1550  				},
  1551  			},
  1552  			{
  1553  				Taint:  []string{"test_instance.foo"},
  1554  				Config: testConfigStr,
  1555  				Check: func(s *terraform.State) error {
  1556  					rs := s.RootModule().Resources["test_instance.foo"]
  1557  					if rs.Primary.ID != "tainted" {
  1558  						return fmt.Errorf("expected tainted, got %s", rs.Primary.ID)
  1559  					}
  1560  					return nil
  1561  				},
  1562  			},
  1563  			{
  1564  				Taint:       []string{"test_instance.fooo"},
  1565  				Config:      testConfigStr,
  1566  				ExpectError: regexp.MustCompile("resource \"test_instance.fooo\" not found in state"),
  1567  			},
  1568  		},
  1569  	})
  1570  
  1571  	if mt.failed() {
  1572  		t.Fatalf("test failure: %s", mt.failMessage())
  1573  	}
  1574  }
  1575  
  1576  func TestTestProviderResolver(t *testing.T) {
  1577  	stubProvider := func(name string) terraform.ResourceProvider {
  1578  		return &schema.Provider{
  1579  			Schema: map[string]*schema.Schema{
  1580  				name: {
  1581  					Type:     schema.TypeString,
  1582  					Required: true,
  1583  				},
  1584  			},
  1585  		}
  1586  	}
  1587  
  1588  	c := TestCase{
  1589  		ProviderFactories: map[string]terraform.ResourceProviderFactory{
  1590  			"foo": terraform.ResourceProviderFactoryFixed(stubProvider("foo")),
  1591  			"bar": terraform.ResourceProviderFactoryFixed(stubProvider("bar")),
  1592  		},
  1593  		Providers: map[string]terraform.ResourceProvider{
  1594  			"baz": stubProvider("baz"),
  1595  			"bop": stubProvider("bop"),
  1596  		},
  1597  	}
  1598  
  1599  	resolver, err := testProviderResolver(c)
  1600  	if err != nil {
  1601  		t.Fatal(err)
  1602  	}
  1603  
  1604  	reqd := discovery.PluginRequirements{
  1605  		"foo": &discovery.PluginConstraints{},
  1606  		"bar": &discovery.PluginConstraints{},
  1607  		"baz": &discovery.PluginConstraints{},
  1608  		"bop": &discovery.PluginConstraints{},
  1609  	}
  1610  
  1611  	factories, errs := resolver.ResolveProviders(reqd)
  1612  	if len(errs) != 0 {
  1613  		for _, err := range errs {
  1614  			t.Error(err)
  1615  		}
  1616  		t.Fatal("unexpected errors")
  1617  	}
  1618  
  1619  	for name := range reqd {
  1620  		t.Run(name, func(t *testing.T) {
  1621  			pf, ok := factories[name]
  1622  			if !ok {
  1623  				t.Fatalf("no factory for %q", name)
  1624  			}
  1625  			p, err := pf()
  1626  			if err != nil {
  1627  				t.Fatal(err)
  1628  			}
  1629  			resp := p.GetSchema()
  1630  			_, ok = resp.Provider.Block.Attributes[name]
  1631  			if !ok {
  1632  				var has string
  1633  				for k := range resp.Provider.Block.Attributes {
  1634  					has = k
  1635  					break
  1636  				}
  1637  				if has != "" {
  1638  					t.Errorf("provider %q does not have the expected schema attribute %q (but has %q)", name, name, has)
  1639  				} else {
  1640  					t.Errorf("provider %q does not have the expected schema attribute %q", name, name)
  1641  				}
  1642  			}
  1643  		})
  1644  	}
  1645  }
  1646  
  1647  const testConfigStr = `
  1648  resource "test_instance" "foo" {}
  1649  `
  1650  
  1651  const testConfigStrProvider = `
  1652  provider "test" {}
  1653  `
  1654  
  1655  func TestCheckResourceAttr_empty(t *testing.T) {
  1656  	s := terraform.NewState()
  1657  	s.AddModuleState(&terraform.ModuleState{
  1658  		Path: []string{"root"},
  1659  		Resources: map[string]*terraform.ResourceState{
  1660  			"test_resource": {
  1661  				Primary: &terraform.InstanceState{
  1662  					Attributes: map[string]string{
  1663  						"empty_list.#": "0",
  1664  						"empty_map.%":  "0",
  1665  					},
  1666  				},
  1667  			},
  1668  		},
  1669  	})
  1670  
  1671  	for _, key := range []string{
  1672  		"empty_list.#",
  1673  		"empty_map.%",
  1674  		"missing_list.#",
  1675  		"missing_map.%",
  1676  	} {
  1677  		t.Run(key, func(t *testing.T) {
  1678  			check := TestCheckResourceAttr("test_resource", key, "0")
  1679  			if err := check(s); err != nil {
  1680  				t.Fatal(err)
  1681  			}
  1682  		})
  1683  	}
  1684  }
  1685  
  1686  func TestCheckNoResourceAttr_empty(t *testing.T) {
  1687  	s := terraform.NewState()
  1688  	s.AddModuleState(&terraform.ModuleState{
  1689  		Path: []string{"root"},
  1690  		Resources: map[string]*terraform.ResourceState{
  1691  			"test_resource": {
  1692  				Primary: &terraform.InstanceState{
  1693  					Attributes: map[string]string{
  1694  						"empty_list.#": "0",
  1695  						"empty_map.%":  "0",
  1696  					},
  1697  				},
  1698  			},
  1699  		},
  1700  	})
  1701  
  1702  	for _, key := range []string{
  1703  		"empty_list.#",
  1704  		"empty_map.%",
  1705  		"missing_list.#",
  1706  		"missing_map.%",
  1707  	} {
  1708  		t.Run(key, func(t *testing.T) {
  1709  			check := TestCheckNoResourceAttr("test_resource", key)
  1710  			if err := check(s); err != nil {
  1711  				t.Fatal(err)
  1712  			}
  1713  		})
  1714  	}
  1715  }
  1716  
  1717  func TestTestCheckResourceAttrPair(t *testing.T) {
  1718  	tests := map[string]struct {
  1719  		state   *terraform.State
  1720  		wantErr string
  1721  	}{
  1722  		"exist match": {
  1723  			&terraform.State{
  1724  				Modules: []*terraform.ModuleState{
  1725  					{
  1726  						Path: []string{"root"},
  1727  						Resources: map[string]*terraform.ResourceState{
  1728  							"test.a": {
  1729  								Primary: &terraform.InstanceState{
  1730  									Attributes: map[string]string{
  1731  										"a": "boop",
  1732  									},
  1733  								},
  1734  							},
  1735  							"test.b": {
  1736  								Primary: &terraform.InstanceState{
  1737  									Attributes: map[string]string{
  1738  										"b": "boop",
  1739  									},
  1740  								},
  1741  							},
  1742  						},
  1743  					},
  1744  				},
  1745  			},
  1746  			``,
  1747  		},
  1748  		"nonexist match": {
  1749  			&terraform.State{
  1750  				Modules: []*terraform.ModuleState{
  1751  					{
  1752  						Path: []string{"root"},
  1753  						Resources: map[string]*terraform.ResourceState{
  1754  							"test.a": {
  1755  								Primary: &terraform.InstanceState{
  1756  									Attributes: map[string]string{},
  1757  								},
  1758  							},
  1759  							"test.b": {
  1760  								Primary: &terraform.InstanceState{
  1761  									Attributes: map[string]string{},
  1762  								},
  1763  							},
  1764  						},
  1765  					},
  1766  				},
  1767  			},
  1768  			``,
  1769  		},
  1770  		"exist nonmatch": {
  1771  			&terraform.State{
  1772  				Modules: []*terraform.ModuleState{
  1773  					{
  1774  						Path: []string{"root"},
  1775  						Resources: map[string]*terraform.ResourceState{
  1776  							"test.a": {
  1777  								Primary: &terraform.InstanceState{
  1778  									Attributes: map[string]string{
  1779  										"a": "beep",
  1780  									},
  1781  								},
  1782  							},
  1783  							"test.b": {
  1784  								Primary: &terraform.InstanceState{
  1785  									Attributes: map[string]string{
  1786  										"b": "boop",
  1787  									},
  1788  								},
  1789  							},
  1790  						},
  1791  					},
  1792  				},
  1793  			},
  1794  			`test.a: Attribute 'a' expected "boop", got "beep"`,
  1795  		},
  1796  		"inconsistent exist a": {
  1797  			&terraform.State{
  1798  				Modules: []*terraform.ModuleState{
  1799  					{
  1800  						Path: []string{"root"},
  1801  						Resources: map[string]*terraform.ResourceState{
  1802  							"test.a": {
  1803  								Primary: &terraform.InstanceState{
  1804  									Attributes: map[string]string{
  1805  										"a": "beep",
  1806  									},
  1807  								},
  1808  							},
  1809  							"test.b": {
  1810  								Primary: &terraform.InstanceState{
  1811  									Attributes: map[string]string{},
  1812  								},
  1813  							},
  1814  						},
  1815  					},
  1816  				},
  1817  			},
  1818  			`test.a: Attribute "a" is "beep", but "b" is not set in test.b`,
  1819  		},
  1820  		"inconsistent exist b": {
  1821  			&terraform.State{
  1822  				Modules: []*terraform.ModuleState{
  1823  					{
  1824  						Path: []string{"root"},
  1825  						Resources: map[string]*terraform.ResourceState{
  1826  							"test.a": {
  1827  								Primary: &terraform.InstanceState{
  1828  									Attributes: map[string]string{},
  1829  								},
  1830  							},
  1831  							"test.b": {
  1832  								Primary: &terraform.InstanceState{
  1833  									Attributes: map[string]string{
  1834  										"b": "boop",
  1835  									},
  1836  								},
  1837  							},
  1838  						},
  1839  					},
  1840  				},
  1841  			},
  1842  			`test.a: Attribute "a" not set, but "b" is set in test.b as "boop"`,
  1843  		},
  1844  	}
  1845  
  1846  	for name, test := range tests {
  1847  		t.Run(name, func(t *testing.T) {
  1848  			fn := TestCheckResourceAttrPair("test.a", "a", "test.b", "b")
  1849  			err := fn(test.state)
  1850  
  1851  			if test.wantErr != "" {
  1852  				if err == nil {
  1853  					t.Fatalf("succeeded; want error\nwant: %s", test.wantErr)
  1854  				}
  1855  				if got, want := err.Error(), test.wantErr; got != want {
  1856  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
  1857  				}
  1858  				return
  1859  			}
  1860  
  1861  			if err != nil {
  1862  				t.Fatalf("failed; want success\ngot: %s", err.Error())
  1863  			}
  1864  		})
  1865  	}
  1866  }
  1867  
  1868  func TestTestCheckResourceAttrPairCount(t *testing.T) {
  1869  	tests := map[string]struct {
  1870  		state   *terraform.State
  1871  		attr    string
  1872  		wantErr string
  1873  	}{
  1874  		"unset and 0 equal list": {
  1875  			&terraform.State{
  1876  				Modules: []*terraform.ModuleState{
  1877  					{
  1878  						Path: []string{"root"},
  1879  						Resources: map[string]*terraform.ResourceState{
  1880  							"test.a": {
  1881  								Primary: &terraform.InstanceState{
  1882  									Attributes: map[string]string{
  1883  										"a.#": "0",
  1884  									},
  1885  								},
  1886  							},
  1887  							"test.b": {
  1888  								Primary: &terraform.InstanceState{
  1889  									Attributes: map[string]string{},
  1890  								},
  1891  							},
  1892  						},
  1893  					},
  1894  				},
  1895  			},
  1896  			"a.#",
  1897  			``,
  1898  		},
  1899  		"unset and 0 equal map": {
  1900  			&terraform.State{
  1901  				Modules: []*terraform.ModuleState{
  1902  					{
  1903  						Path: []string{"root"},
  1904  						Resources: map[string]*terraform.ResourceState{
  1905  							"test.a": {
  1906  								Primary: &terraform.InstanceState{
  1907  									Attributes: map[string]string{
  1908  										"a.%": "0",
  1909  									},
  1910  								},
  1911  							},
  1912  							"test.b": {
  1913  								Primary: &terraform.InstanceState{
  1914  									Attributes: map[string]string{},
  1915  								},
  1916  							},
  1917  						},
  1918  					},
  1919  				},
  1920  			},
  1921  			"a.%",
  1922  			``,
  1923  		},
  1924  		"count equal": {
  1925  			&terraform.State{
  1926  				Modules: []*terraform.ModuleState{
  1927  					{
  1928  						Path: []string{"root"},
  1929  						Resources: map[string]*terraform.ResourceState{
  1930  							"test.a": {
  1931  								Primary: &terraform.InstanceState{
  1932  									Attributes: map[string]string{
  1933  										"a.%": "1",
  1934  									},
  1935  								},
  1936  							},
  1937  							"test.b": {
  1938  								Primary: &terraform.InstanceState{
  1939  									Attributes: map[string]string{
  1940  										"a.%": "1",
  1941  									}},
  1942  							},
  1943  						},
  1944  					},
  1945  				},
  1946  			},
  1947  			"a.%",
  1948  			``,
  1949  		},
  1950  	}
  1951  
  1952  	for name, test := range tests {
  1953  		t.Run(name, func(t *testing.T) {
  1954  			fn := TestCheckResourceAttrPair("test.a", test.attr, "test.b", test.attr)
  1955  			err := fn(test.state)
  1956  
  1957  			if test.wantErr != "" {
  1958  				if err == nil {
  1959  					t.Fatalf("succeeded; want error\nwant: %s", test.wantErr)
  1960  				}
  1961  				if got, want := err.Error(), test.wantErr; got != want {
  1962  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
  1963  				}
  1964  				return
  1965  			}
  1966  
  1967  			if err != nil {
  1968  				t.Fatalf("failed; want success\ngot: %s", err.Error())
  1969  			}
  1970  		})
  1971  	}
  1972  }