github.com/mhlias/terraform@v0.6.12-0.20161118140322-a5d6410b912a/helper/schema/schema_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strconv"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/hil"
    12  	"github.com/hashicorp/hil/ast"
    13  	"github.com/hashicorp/terraform/config"
    14  	"github.com/hashicorp/terraform/helper/hashcode"
    15  	"github.com/hashicorp/terraform/terraform"
    16  )
    17  
    18  func TestEnvDefaultFunc(t *testing.T) {
    19  	key := "TF_TEST_ENV_DEFAULT_FUNC"
    20  	defer os.Unsetenv(key)
    21  
    22  	f := EnvDefaultFunc(key, "42")
    23  	if err := os.Setenv(key, "foo"); err != nil {
    24  		t.Fatalf("err: %s", err)
    25  	}
    26  
    27  	actual, err := f()
    28  	if err != nil {
    29  		t.Fatalf("err: %s", err)
    30  	}
    31  	if actual != "foo" {
    32  		t.Fatalf("bad: %#v", actual)
    33  	}
    34  
    35  	if err := os.Unsetenv(key); err != nil {
    36  		t.Fatalf("err: %s", err)
    37  	}
    38  
    39  	actual, err = f()
    40  	if err != nil {
    41  		t.Fatalf("err: %s", err)
    42  	}
    43  	if actual != "42" {
    44  		t.Fatalf("bad: %#v", actual)
    45  	}
    46  }
    47  
    48  func TestMultiEnvDefaultFunc(t *testing.T) {
    49  	keys := []string{
    50  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC1",
    51  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC2",
    52  	}
    53  	defer func() {
    54  		for _, k := range keys {
    55  			os.Unsetenv(k)
    56  		}
    57  	}()
    58  
    59  	// Test that the first key is returned first
    60  	f := MultiEnvDefaultFunc(keys, "42")
    61  	if err := os.Setenv(keys[0], "foo"); err != nil {
    62  		t.Fatalf("err: %s", err)
    63  	}
    64  
    65  	actual, err := f()
    66  	if err != nil {
    67  		t.Fatalf("err: %s", err)
    68  	}
    69  	if actual != "foo" {
    70  		t.Fatalf("bad: %#v", actual)
    71  	}
    72  
    73  	if err := os.Unsetenv(keys[0]); err != nil {
    74  		t.Fatalf("err: %s", err)
    75  	}
    76  
    77  	// Test that the second key is returned if the first one is empty
    78  	f = MultiEnvDefaultFunc(keys, "42")
    79  	if err := os.Setenv(keys[1], "foo"); err != nil {
    80  		t.Fatalf("err: %s", err)
    81  	}
    82  
    83  	actual, err = f()
    84  	if err != nil {
    85  		t.Fatalf("err: %s", err)
    86  	}
    87  	if actual != "foo" {
    88  		t.Fatalf("bad: %#v", actual)
    89  	}
    90  
    91  	if err := os.Unsetenv(keys[1]); err != nil {
    92  		t.Fatalf("err: %s", err)
    93  	}
    94  
    95  	// Test that the default value is returned when no keys are set
    96  	actual, err = f()
    97  	if err != nil {
    98  		t.Fatalf("err: %s", err)
    99  	}
   100  	if actual != "42" {
   101  		t.Fatalf("bad: %#v", actual)
   102  	}
   103  }
   104  
   105  func TestValueType_Zero(t *testing.T) {
   106  	cases := []struct {
   107  		Type  ValueType
   108  		Value interface{}
   109  	}{
   110  		{TypeBool, false},
   111  		{TypeInt, 0},
   112  		{TypeFloat, 0.0},
   113  		{TypeString, ""},
   114  		{TypeList, []interface{}{}},
   115  		{TypeMap, map[string]interface{}{}},
   116  		{TypeSet, new(Set)},
   117  	}
   118  
   119  	for i, tc := range cases {
   120  		actual := tc.Type.Zero()
   121  		if !reflect.DeepEqual(actual, tc.Value) {
   122  			t.Fatalf("%d: %#v != %#v", i, actual, tc.Value)
   123  		}
   124  	}
   125  }
   126  
   127  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
   128  	variable, _ := hil.InterfaceToVariable(input)
   129  	return variable
   130  }
   131  
   132  func TestSchemaMap_Diff(t *testing.T) {
   133  	cases := map[string]struct {
   134  		Schema          map[string]*Schema
   135  		State           *terraform.InstanceState
   136  		Config          map[string]interface{}
   137  		ConfigVariables map[string]ast.Variable
   138  		Diff            *terraform.InstanceDiff
   139  		Err             bool
   140  	}{
   141  		"#0": {
   142  			Schema: map[string]*Schema{
   143  				"availability_zone": &Schema{
   144  					Type:     TypeString,
   145  					Optional: true,
   146  					Computed: true,
   147  					ForceNew: true,
   148  				},
   149  			},
   150  
   151  			State: nil,
   152  
   153  			Config: map[string]interface{}{
   154  				"availability_zone": "foo",
   155  			},
   156  
   157  			Diff: &terraform.InstanceDiff{
   158  				Attributes: map[string]*terraform.ResourceAttrDiff{
   159  					"availability_zone": &terraform.ResourceAttrDiff{
   160  						Old:         "",
   161  						New:         "foo",
   162  						RequiresNew: true,
   163  					},
   164  				},
   165  			},
   166  
   167  			Err: false,
   168  		},
   169  
   170  		"#1": {
   171  			Schema: map[string]*Schema{
   172  				"availability_zone": &Schema{
   173  					Type:     TypeString,
   174  					Optional: true,
   175  					Computed: true,
   176  					ForceNew: true,
   177  				},
   178  			},
   179  
   180  			State: nil,
   181  
   182  			Config: map[string]interface{}{},
   183  
   184  			Diff: &terraform.InstanceDiff{
   185  				Attributes: map[string]*terraform.ResourceAttrDiff{
   186  					"availability_zone": &terraform.ResourceAttrDiff{
   187  						Old:         "",
   188  						NewComputed: true,
   189  						RequiresNew: true,
   190  					},
   191  				},
   192  			},
   193  
   194  			Err: false,
   195  		},
   196  
   197  		"#2": {
   198  			Schema: map[string]*Schema{
   199  				"availability_zone": &Schema{
   200  					Type:     TypeString,
   201  					Optional: true,
   202  					Computed: true,
   203  					ForceNew: true,
   204  				},
   205  			},
   206  
   207  			State: &terraform.InstanceState{
   208  				ID: "foo",
   209  			},
   210  
   211  			Config: map[string]interface{}{},
   212  
   213  			Diff: nil,
   214  
   215  			Err: false,
   216  		},
   217  
   218  		"#3 Computed, but set in config": {
   219  			Schema: map[string]*Schema{
   220  				"availability_zone": &Schema{
   221  					Type:     TypeString,
   222  					Optional: true,
   223  					Computed: true,
   224  				},
   225  			},
   226  
   227  			State: &terraform.InstanceState{
   228  				Attributes: map[string]string{
   229  					"availability_zone": "foo",
   230  				},
   231  			},
   232  
   233  			Config: map[string]interface{}{
   234  				"availability_zone": "bar",
   235  			},
   236  
   237  			Diff: &terraform.InstanceDiff{
   238  				Attributes: map[string]*terraform.ResourceAttrDiff{
   239  					"availability_zone": &terraform.ResourceAttrDiff{
   240  						Old: "foo",
   241  						New: "bar",
   242  					},
   243  				},
   244  			},
   245  
   246  			Err: false,
   247  		},
   248  
   249  		"#4 Default": {
   250  			Schema: map[string]*Schema{
   251  				"availability_zone": &Schema{
   252  					Type:     TypeString,
   253  					Optional: true,
   254  					Default:  "foo",
   255  				},
   256  			},
   257  
   258  			State: nil,
   259  
   260  			Config: nil,
   261  
   262  			Diff: &terraform.InstanceDiff{
   263  				Attributes: map[string]*terraform.ResourceAttrDiff{
   264  					"availability_zone": &terraform.ResourceAttrDiff{
   265  						Old: "",
   266  						New: "foo",
   267  					},
   268  				},
   269  			},
   270  
   271  			Err: false,
   272  		},
   273  
   274  		"#5 DefaultFunc, value": {
   275  			Schema: map[string]*Schema{
   276  				"availability_zone": &Schema{
   277  					Type:     TypeString,
   278  					Optional: true,
   279  					DefaultFunc: func() (interface{}, error) {
   280  						return "foo", nil
   281  					},
   282  				},
   283  			},
   284  
   285  			State: nil,
   286  
   287  			Config: nil,
   288  
   289  			Diff: &terraform.InstanceDiff{
   290  				Attributes: map[string]*terraform.ResourceAttrDiff{
   291  					"availability_zone": &terraform.ResourceAttrDiff{
   292  						Old: "",
   293  						New: "foo",
   294  					},
   295  				},
   296  			},
   297  
   298  			Err: false,
   299  		},
   300  
   301  		"#6 DefaultFunc, configuration set": {
   302  			Schema: map[string]*Schema{
   303  				"availability_zone": &Schema{
   304  					Type:     TypeString,
   305  					Optional: true,
   306  					DefaultFunc: func() (interface{}, error) {
   307  						return "foo", nil
   308  					},
   309  				},
   310  			},
   311  
   312  			State: nil,
   313  
   314  			Config: map[string]interface{}{
   315  				"availability_zone": "bar",
   316  			},
   317  
   318  			Diff: &terraform.InstanceDiff{
   319  				Attributes: map[string]*terraform.ResourceAttrDiff{
   320  					"availability_zone": &terraform.ResourceAttrDiff{
   321  						Old: "",
   322  						New: "bar",
   323  					},
   324  				},
   325  			},
   326  
   327  			Err: false,
   328  		},
   329  
   330  		"String with StateFunc": {
   331  			Schema: map[string]*Schema{
   332  				"availability_zone": &Schema{
   333  					Type:     TypeString,
   334  					Optional: true,
   335  					Computed: true,
   336  					StateFunc: func(a interface{}) string {
   337  						return a.(string) + "!"
   338  					},
   339  				},
   340  			},
   341  
   342  			State: nil,
   343  
   344  			Config: map[string]interface{}{
   345  				"availability_zone": "foo",
   346  			},
   347  
   348  			Diff: &terraform.InstanceDiff{
   349  				Attributes: map[string]*terraform.ResourceAttrDiff{
   350  					"availability_zone": &terraform.ResourceAttrDiff{
   351  						Old:      "",
   352  						New:      "foo!",
   353  						NewExtra: "foo",
   354  					},
   355  				},
   356  			},
   357  
   358  			Err: false,
   359  		},
   360  
   361  		"StateFunc not called with nil value": {
   362  			Schema: map[string]*Schema{
   363  				"availability_zone": &Schema{
   364  					Type:     TypeString,
   365  					Optional: true,
   366  					Computed: true,
   367  					StateFunc: func(a interface{}) string {
   368  						t.Fatalf("should not get here!")
   369  						return ""
   370  					},
   371  				},
   372  			},
   373  
   374  			State: nil,
   375  
   376  			Config: map[string]interface{}{},
   377  
   378  			Diff: &terraform.InstanceDiff{
   379  				Attributes: map[string]*terraform.ResourceAttrDiff{
   380  					"availability_zone": &terraform.ResourceAttrDiff{
   381  						Old:         "",
   382  						New:         "",
   383  						NewComputed: true,
   384  					},
   385  				},
   386  			},
   387  
   388  			Err: false,
   389  		},
   390  
   391  		"#8 Variable (just checking)": {
   392  			Schema: map[string]*Schema{
   393  				"availability_zone": &Schema{
   394  					Type:     TypeString,
   395  					Optional: true,
   396  				},
   397  			},
   398  
   399  			State: nil,
   400  
   401  			Config: map[string]interface{}{
   402  				"availability_zone": "${var.foo}",
   403  			},
   404  
   405  			ConfigVariables: map[string]ast.Variable{
   406  				"var.foo": interfaceToVariableSwallowError("bar"),
   407  			},
   408  
   409  			Diff: &terraform.InstanceDiff{
   410  				Attributes: map[string]*terraform.ResourceAttrDiff{
   411  					"availability_zone": &terraform.ResourceAttrDiff{
   412  						Old: "",
   413  						New: "bar",
   414  					},
   415  				},
   416  			},
   417  
   418  			Err: false,
   419  		},
   420  
   421  		"#9 Variable computed": {
   422  			Schema: map[string]*Schema{
   423  				"availability_zone": &Schema{
   424  					Type:     TypeString,
   425  					Optional: true,
   426  				},
   427  			},
   428  
   429  			State: nil,
   430  
   431  			Config: map[string]interface{}{
   432  				"availability_zone": "${var.foo}",
   433  			},
   434  
   435  			ConfigVariables: map[string]ast.Variable{
   436  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
   437  			},
   438  
   439  			Diff: &terraform.InstanceDiff{
   440  				Attributes: map[string]*terraform.ResourceAttrDiff{
   441  					"availability_zone": &terraform.ResourceAttrDiff{
   442  						Old: "",
   443  						New: "${var.foo}",
   444  					},
   445  				},
   446  			},
   447  
   448  			Err: false,
   449  		},
   450  
   451  		"#10 Int decode": {
   452  			Schema: map[string]*Schema{
   453  				"port": &Schema{
   454  					Type:     TypeInt,
   455  					Optional: true,
   456  					Computed: true,
   457  					ForceNew: true,
   458  				},
   459  			},
   460  
   461  			State: nil,
   462  
   463  			Config: map[string]interface{}{
   464  				"port": 27,
   465  			},
   466  
   467  			Diff: &terraform.InstanceDiff{
   468  				Attributes: map[string]*terraform.ResourceAttrDiff{
   469  					"port": &terraform.ResourceAttrDiff{
   470  						Old:         "",
   471  						New:         "27",
   472  						RequiresNew: true,
   473  					},
   474  				},
   475  			},
   476  
   477  			Err: false,
   478  		},
   479  
   480  		"#11 bool decode": {
   481  			Schema: map[string]*Schema{
   482  				"port": &Schema{
   483  					Type:     TypeBool,
   484  					Optional: true,
   485  					Computed: true,
   486  					ForceNew: true,
   487  				},
   488  			},
   489  
   490  			State: nil,
   491  
   492  			Config: map[string]interface{}{
   493  				"port": false,
   494  			},
   495  
   496  			Diff: &terraform.InstanceDiff{
   497  				Attributes: map[string]*terraform.ResourceAttrDiff{
   498  					"port": &terraform.ResourceAttrDiff{
   499  						Old:         "",
   500  						New:         "false",
   501  						RequiresNew: true,
   502  					},
   503  				},
   504  			},
   505  
   506  			Err: false,
   507  		},
   508  
   509  		"#12 Bool": {
   510  			Schema: map[string]*Schema{
   511  				"delete": &Schema{
   512  					Type:     TypeBool,
   513  					Optional: true,
   514  					Default:  false,
   515  				},
   516  			},
   517  
   518  			State: &terraform.InstanceState{
   519  				Attributes: map[string]string{
   520  					"delete": "false",
   521  				},
   522  			},
   523  
   524  			Config: nil,
   525  
   526  			Diff: nil,
   527  
   528  			Err: false,
   529  		},
   530  
   531  		"#13 List decode": {
   532  			Schema: map[string]*Schema{
   533  				"ports": &Schema{
   534  					Type:     TypeList,
   535  					Required: true,
   536  					Elem:     &Schema{Type: TypeInt},
   537  				},
   538  			},
   539  
   540  			State: nil,
   541  
   542  			Config: map[string]interface{}{
   543  				"ports": []interface{}{1, 2, 5},
   544  			},
   545  
   546  			Diff: &terraform.InstanceDiff{
   547  				Attributes: map[string]*terraform.ResourceAttrDiff{
   548  					"ports.#": &terraform.ResourceAttrDiff{
   549  						Old: "0",
   550  						New: "3",
   551  					},
   552  					"ports.0": &terraform.ResourceAttrDiff{
   553  						Old: "",
   554  						New: "1",
   555  					},
   556  					"ports.1": &terraform.ResourceAttrDiff{
   557  						Old: "",
   558  						New: "2",
   559  					},
   560  					"ports.2": &terraform.ResourceAttrDiff{
   561  						Old: "",
   562  						New: "5",
   563  					},
   564  				},
   565  			},
   566  
   567  			Err: false,
   568  		},
   569  
   570  		"#14": {
   571  			Schema: map[string]*Schema{
   572  				"ports": &Schema{
   573  					Type:     TypeList,
   574  					Required: true,
   575  					Elem:     &Schema{Type: TypeInt},
   576  				},
   577  			},
   578  
   579  			State: nil,
   580  
   581  			Config: map[string]interface{}{
   582  				"ports": []interface{}{1, "${var.foo}"},
   583  			},
   584  
   585  			ConfigVariables: map[string]ast.Variable{
   586  				"var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}),
   587  			},
   588  
   589  			Diff: &terraform.InstanceDiff{
   590  				Attributes: map[string]*terraform.ResourceAttrDiff{
   591  					"ports.#": &terraform.ResourceAttrDiff{
   592  						Old: "0",
   593  						New: "3",
   594  					},
   595  					"ports.0": &terraform.ResourceAttrDiff{
   596  						Old: "",
   597  						New: "1",
   598  					},
   599  					"ports.1": &terraform.ResourceAttrDiff{
   600  						Old: "",
   601  						New: "2",
   602  					},
   603  					"ports.2": &terraform.ResourceAttrDiff{
   604  						Old: "",
   605  						New: "5",
   606  					},
   607  				},
   608  			},
   609  
   610  			Err: false,
   611  		},
   612  
   613  		"#15": {
   614  			Schema: map[string]*Schema{
   615  				"ports": &Schema{
   616  					Type:     TypeList,
   617  					Required: true,
   618  					Elem:     &Schema{Type: TypeInt},
   619  				},
   620  			},
   621  
   622  			State: nil,
   623  
   624  			Config: map[string]interface{}{
   625  				"ports": []interface{}{1, "${var.foo}"},
   626  			},
   627  
   628  			ConfigVariables: map[string]ast.Variable{
   629  				"var.foo": interfaceToVariableSwallowError([]interface{}{
   630  					config.UnknownVariableValue, "5"}),
   631  			},
   632  
   633  			Diff: &terraform.InstanceDiff{
   634  				Attributes: map[string]*terraform.ResourceAttrDiff{
   635  					"ports.#": &terraform.ResourceAttrDiff{
   636  						Old:         "0",
   637  						New:         "",
   638  						NewComputed: true,
   639  					},
   640  				},
   641  			},
   642  
   643  			Err: false,
   644  		},
   645  
   646  		"#16": {
   647  			Schema: map[string]*Schema{
   648  				"ports": &Schema{
   649  					Type:     TypeList,
   650  					Required: true,
   651  					Elem:     &Schema{Type: TypeInt},
   652  				},
   653  			},
   654  
   655  			State: &terraform.InstanceState{
   656  				Attributes: map[string]string{
   657  					"ports.#": "3",
   658  					"ports.0": "1",
   659  					"ports.1": "2",
   660  					"ports.2": "5",
   661  				},
   662  			},
   663  
   664  			Config: map[string]interface{}{
   665  				"ports": []interface{}{1, 2, 5},
   666  			},
   667  
   668  			Diff: nil,
   669  
   670  			Err: false,
   671  		},
   672  
   673  		"#17": {
   674  			Schema: map[string]*Schema{
   675  				"ports": &Schema{
   676  					Type:     TypeList,
   677  					Required: true,
   678  					Elem:     &Schema{Type: TypeInt},
   679  				},
   680  			},
   681  
   682  			State: &terraform.InstanceState{
   683  				Attributes: map[string]string{
   684  					"ports.#": "2",
   685  					"ports.0": "1",
   686  					"ports.1": "2",
   687  				},
   688  			},
   689  
   690  			Config: map[string]interface{}{
   691  				"ports": []interface{}{1, 2, 5},
   692  			},
   693  
   694  			Diff: &terraform.InstanceDiff{
   695  				Attributes: map[string]*terraform.ResourceAttrDiff{
   696  					"ports.#": &terraform.ResourceAttrDiff{
   697  						Old: "2",
   698  						New: "3",
   699  					},
   700  					"ports.2": &terraform.ResourceAttrDiff{
   701  						Old: "",
   702  						New: "5",
   703  					},
   704  				},
   705  			},
   706  
   707  			Err: false,
   708  		},
   709  
   710  		"#18": {
   711  			Schema: map[string]*Schema{
   712  				"ports": &Schema{
   713  					Type:     TypeList,
   714  					Required: true,
   715  					Elem:     &Schema{Type: TypeInt},
   716  					ForceNew: true,
   717  				},
   718  			},
   719  
   720  			State: nil,
   721  
   722  			Config: map[string]interface{}{
   723  				"ports": []interface{}{1, 2, 5},
   724  			},
   725  
   726  			Diff: &terraform.InstanceDiff{
   727  				Attributes: map[string]*terraform.ResourceAttrDiff{
   728  					"ports.#": &terraform.ResourceAttrDiff{
   729  						Old:         "0",
   730  						New:         "3",
   731  						RequiresNew: true,
   732  					},
   733  					"ports.0": &terraform.ResourceAttrDiff{
   734  						Old:         "",
   735  						New:         "1",
   736  						RequiresNew: true,
   737  					},
   738  					"ports.1": &terraform.ResourceAttrDiff{
   739  						Old:         "",
   740  						New:         "2",
   741  						RequiresNew: true,
   742  					},
   743  					"ports.2": &terraform.ResourceAttrDiff{
   744  						Old:         "",
   745  						New:         "5",
   746  						RequiresNew: true,
   747  					},
   748  				},
   749  			},
   750  
   751  			Err: false,
   752  		},
   753  
   754  		"#19": {
   755  			Schema: map[string]*Schema{
   756  				"ports": &Schema{
   757  					Type:     TypeList,
   758  					Optional: true,
   759  					Computed: true,
   760  					Elem:     &Schema{Type: TypeInt},
   761  				},
   762  			},
   763  
   764  			State: nil,
   765  
   766  			Config: map[string]interface{}{},
   767  
   768  			Diff: &terraform.InstanceDiff{
   769  				Attributes: map[string]*terraform.ResourceAttrDiff{
   770  					"ports.#": &terraform.ResourceAttrDiff{
   771  						Old:         "",
   772  						NewComputed: true,
   773  					},
   774  				},
   775  			},
   776  
   777  			Err: false,
   778  		},
   779  
   780  		"#20 Set": {
   781  			Schema: map[string]*Schema{
   782  				"ports": &Schema{
   783  					Type:     TypeSet,
   784  					Required: true,
   785  					Elem:     &Schema{Type: TypeInt},
   786  					Set: func(a interface{}) int {
   787  						return a.(int)
   788  					},
   789  				},
   790  			},
   791  
   792  			State: nil,
   793  
   794  			Config: map[string]interface{}{
   795  				"ports": []interface{}{5, 2, 1},
   796  			},
   797  
   798  			Diff: &terraform.InstanceDiff{
   799  				Attributes: map[string]*terraform.ResourceAttrDiff{
   800  					"ports.#": &terraform.ResourceAttrDiff{
   801  						Old: "0",
   802  						New: "3",
   803  					},
   804  					"ports.1": &terraform.ResourceAttrDiff{
   805  						Old: "",
   806  						New: "1",
   807  					},
   808  					"ports.2": &terraform.ResourceAttrDiff{
   809  						Old: "",
   810  						New: "2",
   811  					},
   812  					"ports.5": &terraform.ResourceAttrDiff{
   813  						Old: "",
   814  						New: "5",
   815  					},
   816  				},
   817  			},
   818  
   819  			Err: false,
   820  		},
   821  
   822  		"#21 Set": {
   823  			Schema: map[string]*Schema{
   824  				"ports": &Schema{
   825  					Type:     TypeSet,
   826  					Computed: true,
   827  					Required: true,
   828  					Elem:     &Schema{Type: TypeInt},
   829  					Set: func(a interface{}) int {
   830  						return a.(int)
   831  					},
   832  				},
   833  			},
   834  
   835  			State: &terraform.InstanceState{
   836  				Attributes: map[string]string{
   837  					"ports.#": "0",
   838  				},
   839  			},
   840  
   841  			Config: nil,
   842  
   843  			Diff: nil,
   844  
   845  			Err: false,
   846  		},
   847  
   848  		"#22 Set": {
   849  			Schema: map[string]*Schema{
   850  				"ports": &Schema{
   851  					Type:     TypeSet,
   852  					Optional: true,
   853  					Computed: true,
   854  					Elem:     &Schema{Type: TypeInt},
   855  					Set: func(a interface{}) int {
   856  						return a.(int)
   857  					},
   858  				},
   859  			},
   860  
   861  			State: nil,
   862  
   863  			Config: nil,
   864  
   865  			Diff: &terraform.InstanceDiff{
   866  				Attributes: map[string]*terraform.ResourceAttrDiff{
   867  					"ports.#": &terraform.ResourceAttrDiff{
   868  						Old:         "",
   869  						NewComputed: true,
   870  					},
   871  				},
   872  			},
   873  
   874  			Err: false,
   875  		},
   876  
   877  		"#23 Set": {
   878  			Schema: map[string]*Schema{
   879  				"ports": &Schema{
   880  					Type:     TypeSet,
   881  					Required: true,
   882  					Elem:     &Schema{Type: TypeInt},
   883  					Set: func(a interface{}) int {
   884  						return a.(int)
   885  					},
   886  				},
   887  			},
   888  
   889  			State: nil,
   890  
   891  			Config: map[string]interface{}{
   892  				"ports": []interface{}{"${var.foo}", 1},
   893  			},
   894  
   895  			ConfigVariables: map[string]ast.Variable{
   896  				"var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}),
   897  			},
   898  
   899  			Diff: &terraform.InstanceDiff{
   900  				Attributes: map[string]*terraform.ResourceAttrDiff{
   901  					"ports.#": &terraform.ResourceAttrDiff{
   902  						Old: "0",
   903  						New: "3",
   904  					},
   905  					"ports.1": &terraform.ResourceAttrDiff{
   906  						Old: "",
   907  						New: "1",
   908  					},
   909  					"ports.2": &terraform.ResourceAttrDiff{
   910  						Old: "",
   911  						New: "2",
   912  					},
   913  					"ports.5": &terraform.ResourceAttrDiff{
   914  						Old: "",
   915  						New: "5",
   916  					},
   917  				},
   918  			},
   919  
   920  			Err: false,
   921  		},
   922  
   923  		"#24 Set": {
   924  			Schema: map[string]*Schema{
   925  				"ports": &Schema{
   926  					Type:     TypeSet,
   927  					Required: true,
   928  					Elem:     &Schema{Type: TypeInt},
   929  					Set: func(a interface{}) int {
   930  						return a.(int)
   931  					},
   932  				},
   933  			},
   934  
   935  			State: nil,
   936  
   937  			Config: map[string]interface{}{
   938  				"ports": []interface{}{1, "${var.foo}"},
   939  			},
   940  
   941  			ConfigVariables: map[string]ast.Variable{
   942  				"var.foo": interfaceToVariableSwallowError([]interface{}{
   943  					config.UnknownVariableValue, "5"}),
   944  			},
   945  
   946  			Diff: &terraform.InstanceDiff{
   947  				Attributes: map[string]*terraform.ResourceAttrDiff{
   948  					"ports.#": &terraform.ResourceAttrDiff{
   949  						Old:         "",
   950  						New:         "",
   951  						NewComputed: true,
   952  					},
   953  				},
   954  			},
   955  
   956  			Err: false,
   957  		},
   958  
   959  		"#25 Set": {
   960  			Schema: map[string]*Schema{
   961  				"ports": &Schema{
   962  					Type:     TypeSet,
   963  					Required: true,
   964  					Elem:     &Schema{Type: TypeInt},
   965  					Set: func(a interface{}) int {
   966  						return a.(int)
   967  					},
   968  				},
   969  			},
   970  
   971  			State: &terraform.InstanceState{
   972  				Attributes: map[string]string{
   973  					"ports.#": "2",
   974  					"ports.1": "1",
   975  					"ports.2": "2",
   976  				},
   977  			},
   978  
   979  			Config: map[string]interface{}{
   980  				"ports": []interface{}{5, 2, 1},
   981  			},
   982  
   983  			Diff: &terraform.InstanceDiff{
   984  				Attributes: map[string]*terraform.ResourceAttrDiff{
   985  					"ports.#": &terraform.ResourceAttrDiff{
   986  						Old: "2",
   987  						New: "3",
   988  					},
   989  					"ports.1": &terraform.ResourceAttrDiff{
   990  						Old: "1",
   991  						New: "1",
   992  					},
   993  					"ports.2": &terraform.ResourceAttrDiff{
   994  						Old: "2",
   995  						New: "2",
   996  					},
   997  					"ports.5": &terraform.ResourceAttrDiff{
   998  						Old: "",
   999  						New: "5",
  1000  					},
  1001  				},
  1002  			},
  1003  
  1004  			Err: false,
  1005  		},
  1006  
  1007  		"#26 Set": {
  1008  			Schema: map[string]*Schema{
  1009  				"ports": &Schema{
  1010  					Type:     TypeSet,
  1011  					Required: true,
  1012  					Elem:     &Schema{Type: TypeInt},
  1013  					Set: func(a interface{}) int {
  1014  						return a.(int)
  1015  					},
  1016  				},
  1017  			},
  1018  
  1019  			State: &terraform.InstanceState{
  1020  				Attributes: map[string]string{
  1021  					"ports.#": "2",
  1022  					"ports.1": "1",
  1023  					"ports.2": "2",
  1024  				},
  1025  			},
  1026  
  1027  			Config: map[string]interface{}{},
  1028  
  1029  			Diff: &terraform.InstanceDiff{
  1030  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1031  					"ports.#": &terraform.ResourceAttrDiff{
  1032  						Old: "2",
  1033  						New: "0",
  1034  					},
  1035  					"ports.1": &terraform.ResourceAttrDiff{
  1036  						Old:        "1",
  1037  						New:        "0",
  1038  						NewRemoved: true,
  1039  					},
  1040  					"ports.2": &terraform.ResourceAttrDiff{
  1041  						Old:        "2",
  1042  						New:        "0",
  1043  						NewRemoved: true,
  1044  					},
  1045  				},
  1046  			},
  1047  
  1048  			Err: false,
  1049  		},
  1050  
  1051  		"#27 Set": {
  1052  			Schema: map[string]*Schema{
  1053  				"ports": &Schema{
  1054  					Type:     TypeSet,
  1055  					Optional: true,
  1056  					Computed: true,
  1057  					Elem:     &Schema{Type: TypeInt},
  1058  					Set: func(a interface{}) int {
  1059  						return a.(int)
  1060  					},
  1061  				},
  1062  			},
  1063  
  1064  			State: &terraform.InstanceState{
  1065  				Attributes: map[string]string{
  1066  					"availability_zone": "bar",
  1067  					"ports.#":           "1",
  1068  					"ports.80":          "80",
  1069  				},
  1070  			},
  1071  
  1072  			Config: map[string]interface{}{},
  1073  
  1074  			Diff: nil,
  1075  
  1076  			Err: false,
  1077  		},
  1078  
  1079  		"#28 Set": {
  1080  			Schema: map[string]*Schema{
  1081  				"ingress": &Schema{
  1082  					Type:     TypeSet,
  1083  					Required: true,
  1084  					Elem: &Resource{
  1085  						Schema: map[string]*Schema{
  1086  							"ports": &Schema{
  1087  								Type:     TypeList,
  1088  								Optional: true,
  1089  								Elem:     &Schema{Type: TypeInt},
  1090  							},
  1091  						},
  1092  					},
  1093  					Set: func(v interface{}) int {
  1094  						m := v.(map[string]interface{})
  1095  						ps := m["ports"].([]interface{})
  1096  						result := 0
  1097  						for _, p := range ps {
  1098  							result += p.(int)
  1099  						}
  1100  						return result
  1101  					},
  1102  				},
  1103  			},
  1104  
  1105  			State: &terraform.InstanceState{
  1106  				Attributes: map[string]string{
  1107  					"ingress.#":           "2",
  1108  					"ingress.80.ports.#":  "1",
  1109  					"ingress.80.ports.0":  "80",
  1110  					"ingress.443.ports.#": "1",
  1111  					"ingress.443.ports.0": "443",
  1112  				},
  1113  			},
  1114  
  1115  			Config: map[string]interface{}{
  1116  				"ingress": []map[string]interface{}{
  1117  					map[string]interface{}{
  1118  						"ports": []interface{}{443},
  1119  					},
  1120  					map[string]interface{}{
  1121  						"ports": []interface{}{80},
  1122  					},
  1123  				},
  1124  			},
  1125  
  1126  			Diff: nil,
  1127  
  1128  			Err: false,
  1129  		},
  1130  
  1131  		"#29 List of structure decode": {
  1132  			Schema: map[string]*Schema{
  1133  				"ingress": &Schema{
  1134  					Type:     TypeList,
  1135  					Required: true,
  1136  					Elem: &Resource{
  1137  						Schema: map[string]*Schema{
  1138  							"from": &Schema{
  1139  								Type:     TypeInt,
  1140  								Required: true,
  1141  							},
  1142  						},
  1143  					},
  1144  				},
  1145  			},
  1146  
  1147  			State: nil,
  1148  
  1149  			Config: map[string]interface{}{
  1150  				"ingress": []interface{}{
  1151  					map[string]interface{}{
  1152  						"from": 8080,
  1153  					},
  1154  				},
  1155  			},
  1156  
  1157  			Diff: &terraform.InstanceDiff{
  1158  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1159  					"ingress.#": &terraform.ResourceAttrDiff{
  1160  						Old: "0",
  1161  						New: "1",
  1162  					},
  1163  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1164  						Old: "",
  1165  						New: "8080",
  1166  					},
  1167  				},
  1168  			},
  1169  
  1170  			Err: false,
  1171  		},
  1172  
  1173  		"#30 ComputedWhen": {
  1174  			Schema: map[string]*Schema{
  1175  				"availability_zone": &Schema{
  1176  					Type:         TypeString,
  1177  					Computed:     true,
  1178  					ComputedWhen: []string{"port"},
  1179  				},
  1180  
  1181  				"port": &Schema{
  1182  					Type:     TypeInt,
  1183  					Optional: true,
  1184  				},
  1185  			},
  1186  
  1187  			State: &terraform.InstanceState{
  1188  				Attributes: map[string]string{
  1189  					"availability_zone": "foo",
  1190  					"port":              "80",
  1191  				},
  1192  			},
  1193  
  1194  			Config: map[string]interface{}{
  1195  				"port": 80,
  1196  			},
  1197  
  1198  			Diff: nil,
  1199  
  1200  			Err: false,
  1201  		},
  1202  
  1203  		"#31": {
  1204  			Schema: map[string]*Schema{
  1205  				"availability_zone": &Schema{
  1206  					Type:         TypeString,
  1207  					Computed:     true,
  1208  					ComputedWhen: []string{"port"},
  1209  				},
  1210  
  1211  				"port": &Schema{
  1212  					Type:     TypeInt,
  1213  					Optional: true,
  1214  				},
  1215  			},
  1216  
  1217  			State: &terraform.InstanceState{
  1218  				Attributes: map[string]string{
  1219  					"port": "80",
  1220  				},
  1221  			},
  1222  
  1223  			Config: map[string]interface{}{
  1224  				"port": 80,
  1225  			},
  1226  
  1227  			Diff: &terraform.InstanceDiff{
  1228  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1229  					"availability_zone": &terraform.ResourceAttrDiff{
  1230  						NewComputed: true,
  1231  					},
  1232  				},
  1233  			},
  1234  
  1235  			Err: false,
  1236  		},
  1237  
  1238  		/* TODO
  1239  		{
  1240  			Schema: map[string]*Schema{
  1241  				"availability_zone": &Schema{
  1242  					Type:         TypeString,
  1243  					Computed:     true,
  1244  					ComputedWhen: []string{"port"},
  1245  				},
  1246  
  1247  				"port": &Schema{
  1248  					Type:     TypeInt,
  1249  					Optional: true,
  1250  				},
  1251  			},
  1252  
  1253  			State: &terraform.InstanceState{
  1254  				Attributes: map[string]string{
  1255  					"availability_zone": "foo",
  1256  					"port":              "80",
  1257  				},
  1258  			},
  1259  
  1260  			Config: map[string]interface{}{
  1261  				"port": 8080,
  1262  			},
  1263  
  1264  			Diff: &terraform.ResourceDiff{
  1265  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1266  					"availability_zone": &terraform.ResourceAttrDiff{
  1267  						Old:         "foo",
  1268  						NewComputed: true,
  1269  					},
  1270  					"port": &terraform.ResourceAttrDiff{
  1271  						Old: "80",
  1272  						New: "8080",
  1273  					},
  1274  				},
  1275  			},
  1276  
  1277  			Err: false,
  1278  		},
  1279  		*/
  1280  
  1281  		"#32 Maps": {
  1282  			Schema: map[string]*Schema{
  1283  				"config_vars": &Schema{
  1284  					Type: TypeMap,
  1285  				},
  1286  			},
  1287  
  1288  			State: nil,
  1289  
  1290  			Config: map[string]interface{}{
  1291  				"config_vars": []interface{}{
  1292  					map[string]interface{}{
  1293  						"bar": "baz",
  1294  					},
  1295  				},
  1296  			},
  1297  
  1298  			Diff: &terraform.InstanceDiff{
  1299  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1300  					"config_vars.%": &terraform.ResourceAttrDiff{
  1301  						Old: "0",
  1302  						New: "1",
  1303  					},
  1304  
  1305  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1306  						Old: "",
  1307  						New: "baz",
  1308  					},
  1309  				},
  1310  			},
  1311  
  1312  			Err: false,
  1313  		},
  1314  
  1315  		"#33 Maps": {
  1316  			Schema: map[string]*Schema{
  1317  				"config_vars": &Schema{
  1318  					Type: TypeMap,
  1319  				},
  1320  			},
  1321  
  1322  			State: &terraform.InstanceState{
  1323  				Attributes: map[string]string{
  1324  					"config_vars.foo": "bar",
  1325  				},
  1326  			},
  1327  
  1328  			Config: map[string]interface{}{
  1329  				"config_vars": []interface{}{
  1330  					map[string]interface{}{
  1331  						"bar": "baz",
  1332  					},
  1333  				},
  1334  			},
  1335  
  1336  			Diff: &terraform.InstanceDiff{
  1337  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1338  					"config_vars.foo": &terraform.ResourceAttrDiff{
  1339  						Old:        "bar",
  1340  						NewRemoved: true,
  1341  					},
  1342  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1343  						Old: "",
  1344  						New: "baz",
  1345  					},
  1346  				},
  1347  			},
  1348  
  1349  			Err: false,
  1350  		},
  1351  
  1352  		"#34 Maps": {
  1353  			Schema: map[string]*Schema{
  1354  				"vars": &Schema{
  1355  					Type:     TypeMap,
  1356  					Optional: true,
  1357  					Computed: true,
  1358  				},
  1359  			},
  1360  
  1361  			State: &terraform.InstanceState{
  1362  				Attributes: map[string]string{
  1363  					"vars.foo": "bar",
  1364  				},
  1365  			},
  1366  
  1367  			Config: map[string]interface{}{
  1368  				"vars": []interface{}{
  1369  					map[string]interface{}{
  1370  						"bar": "baz",
  1371  					},
  1372  				},
  1373  			},
  1374  
  1375  			Diff: &terraform.InstanceDiff{
  1376  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1377  					"vars.foo": &terraform.ResourceAttrDiff{
  1378  						Old:        "bar",
  1379  						New:        "",
  1380  						NewRemoved: true,
  1381  					},
  1382  					"vars.bar": &terraform.ResourceAttrDiff{
  1383  						Old: "",
  1384  						New: "baz",
  1385  					},
  1386  				},
  1387  			},
  1388  
  1389  			Err: false,
  1390  		},
  1391  
  1392  		"#35 Maps": {
  1393  			Schema: map[string]*Schema{
  1394  				"vars": &Schema{
  1395  					Type:     TypeMap,
  1396  					Computed: true,
  1397  				},
  1398  			},
  1399  
  1400  			State: &terraform.InstanceState{
  1401  				Attributes: map[string]string{
  1402  					"vars.foo": "bar",
  1403  				},
  1404  			},
  1405  
  1406  			Config: nil,
  1407  
  1408  			Diff: nil,
  1409  
  1410  			Err: false,
  1411  		},
  1412  
  1413  		"#36 Maps": {
  1414  			Schema: map[string]*Schema{
  1415  				"config_vars": &Schema{
  1416  					Type: TypeList,
  1417  					Elem: &Schema{Type: TypeMap},
  1418  				},
  1419  			},
  1420  
  1421  			State: &terraform.InstanceState{
  1422  				Attributes: map[string]string{
  1423  					"config_vars.#":     "1",
  1424  					"config_vars.0.foo": "bar",
  1425  				},
  1426  			},
  1427  
  1428  			Config: map[string]interface{}{
  1429  				"config_vars": []interface{}{
  1430  					map[string]interface{}{
  1431  						"bar": "baz",
  1432  					},
  1433  				},
  1434  			},
  1435  
  1436  			Diff: &terraform.InstanceDiff{
  1437  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1438  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1439  						Old:        "bar",
  1440  						NewRemoved: true,
  1441  					},
  1442  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1443  						Old: "",
  1444  						New: "baz",
  1445  					},
  1446  				},
  1447  			},
  1448  
  1449  			Err: false,
  1450  		},
  1451  
  1452  		"#37 Maps": {
  1453  			Schema: map[string]*Schema{
  1454  				"config_vars": &Schema{
  1455  					Type: TypeList,
  1456  					Elem: &Schema{Type: TypeMap},
  1457  				},
  1458  			},
  1459  
  1460  			State: &terraform.InstanceState{
  1461  				Attributes: map[string]string{
  1462  					"config_vars.#":     "1",
  1463  					"config_vars.0.foo": "bar",
  1464  					"config_vars.0.bar": "baz",
  1465  				},
  1466  			},
  1467  
  1468  			Config: map[string]interface{}{},
  1469  
  1470  			Diff: &terraform.InstanceDiff{
  1471  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1472  					"config_vars.#": &terraform.ResourceAttrDiff{
  1473  						Old: "1",
  1474  						New: "0",
  1475  					},
  1476  					"config_vars.0.%": &terraform.ResourceAttrDiff{
  1477  						Old: "2",
  1478  						New: "0",
  1479  					},
  1480  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1481  						Old:        "bar",
  1482  						NewRemoved: true,
  1483  					},
  1484  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1485  						Old:        "baz",
  1486  						NewRemoved: true,
  1487  					},
  1488  				},
  1489  			},
  1490  
  1491  			Err: false,
  1492  		},
  1493  
  1494  		"#38 ForceNews": {
  1495  			Schema: map[string]*Schema{
  1496  				"availability_zone": &Schema{
  1497  					Type:     TypeString,
  1498  					Optional: true,
  1499  					ForceNew: true,
  1500  				},
  1501  
  1502  				"address": &Schema{
  1503  					Type:     TypeString,
  1504  					Optional: true,
  1505  					Computed: true,
  1506  				},
  1507  			},
  1508  
  1509  			State: &terraform.InstanceState{
  1510  				Attributes: map[string]string{
  1511  					"availability_zone": "bar",
  1512  					"address":           "foo",
  1513  				},
  1514  			},
  1515  
  1516  			Config: map[string]interface{}{
  1517  				"availability_zone": "foo",
  1518  			},
  1519  
  1520  			Diff: &terraform.InstanceDiff{
  1521  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1522  					"availability_zone": &terraform.ResourceAttrDiff{
  1523  						Old:         "bar",
  1524  						New:         "foo",
  1525  						RequiresNew: true,
  1526  					},
  1527  
  1528  					"address": &terraform.ResourceAttrDiff{
  1529  						Old:         "foo",
  1530  						New:         "",
  1531  						NewComputed: true,
  1532  					},
  1533  				},
  1534  			},
  1535  
  1536  			Err: false,
  1537  		},
  1538  
  1539  		"#39 Set": {
  1540  			Schema: map[string]*Schema{
  1541  				"availability_zone": &Schema{
  1542  					Type:     TypeString,
  1543  					Optional: true,
  1544  					ForceNew: true,
  1545  				},
  1546  
  1547  				"ports": &Schema{
  1548  					Type:     TypeSet,
  1549  					Optional: true,
  1550  					Computed: true,
  1551  					Elem:     &Schema{Type: TypeInt},
  1552  					Set: func(a interface{}) int {
  1553  						return a.(int)
  1554  					},
  1555  				},
  1556  			},
  1557  
  1558  			State: &terraform.InstanceState{
  1559  				Attributes: map[string]string{
  1560  					"availability_zone": "bar",
  1561  					"ports.#":           "1",
  1562  					"ports.80":          "80",
  1563  				},
  1564  			},
  1565  
  1566  			Config: map[string]interface{}{
  1567  				"availability_zone": "foo",
  1568  			},
  1569  
  1570  			Diff: &terraform.InstanceDiff{
  1571  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1572  					"availability_zone": &terraform.ResourceAttrDiff{
  1573  						Old:         "bar",
  1574  						New:         "foo",
  1575  						RequiresNew: true,
  1576  					},
  1577  
  1578  					"ports.#": &terraform.ResourceAttrDiff{
  1579  						Old:         "1",
  1580  						New:         "",
  1581  						NewComputed: true,
  1582  					},
  1583  				},
  1584  			},
  1585  
  1586  			Err: false,
  1587  		},
  1588  
  1589  		"#40 Set": {
  1590  			Schema: map[string]*Schema{
  1591  				"instances": &Schema{
  1592  					Type:     TypeSet,
  1593  					Elem:     &Schema{Type: TypeString},
  1594  					Optional: true,
  1595  					Computed: true,
  1596  					Set: func(v interface{}) int {
  1597  						return len(v.(string))
  1598  					},
  1599  				},
  1600  			},
  1601  
  1602  			State: &terraform.InstanceState{
  1603  				Attributes: map[string]string{
  1604  					"instances.#": "0",
  1605  				},
  1606  			},
  1607  
  1608  			Config: map[string]interface{}{
  1609  				"instances": []interface{}{"${var.foo}"},
  1610  			},
  1611  
  1612  			ConfigVariables: map[string]ast.Variable{
  1613  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1614  			},
  1615  
  1616  			Diff: &terraform.InstanceDiff{
  1617  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1618  					"instances.#": &terraform.ResourceAttrDiff{
  1619  						NewComputed: true,
  1620  					},
  1621  				},
  1622  			},
  1623  
  1624  			Err: false,
  1625  		},
  1626  
  1627  		"#41 Set": {
  1628  			Schema: map[string]*Schema{
  1629  				"route": &Schema{
  1630  					Type:     TypeSet,
  1631  					Optional: true,
  1632  					Elem: &Resource{
  1633  						Schema: map[string]*Schema{
  1634  							"index": &Schema{
  1635  								Type:     TypeInt,
  1636  								Required: true,
  1637  							},
  1638  
  1639  							"gateway": &Schema{
  1640  								Type:     TypeString,
  1641  								Optional: true,
  1642  							},
  1643  						},
  1644  					},
  1645  					Set: func(v interface{}) int {
  1646  						m := v.(map[string]interface{})
  1647  						return m["index"].(int)
  1648  					},
  1649  				},
  1650  			},
  1651  
  1652  			State: nil,
  1653  
  1654  			Config: map[string]interface{}{
  1655  				"route": []map[string]interface{}{
  1656  					map[string]interface{}{
  1657  						"index":   "1",
  1658  						"gateway": "${var.foo}",
  1659  					},
  1660  				},
  1661  			},
  1662  
  1663  			ConfigVariables: map[string]ast.Variable{
  1664  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1665  			},
  1666  
  1667  			Diff: &terraform.InstanceDiff{
  1668  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1669  					"route.#": &terraform.ResourceAttrDiff{
  1670  						Old: "0",
  1671  						New: "1",
  1672  					},
  1673  					"route.~1.index": &terraform.ResourceAttrDiff{
  1674  						Old: "",
  1675  						New: "1",
  1676  					},
  1677  					"route.~1.gateway": &terraform.ResourceAttrDiff{
  1678  						Old: "",
  1679  						New: "${var.foo}",
  1680  					},
  1681  				},
  1682  			},
  1683  
  1684  			Err: false,
  1685  		},
  1686  
  1687  		"#42 Set": {
  1688  			Schema: map[string]*Schema{
  1689  				"route": &Schema{
  1690  					Type:     TypeSet,
  1691  					Optional: true,
  1692  					Elem: &Resource{
  1693  						Schema: map[string]*Schema{
  1694  							"index": &Schema{
  1695  								Type:     TypeInt,
  1696  								Required: true,
  1697  							},
  1698  
  1699  							"gateway": &Schema{
  1700  								Type:     TypeSet,
  1701  								Optional: true,
  1702  								Elem:     &Schema{Type: TypeInt},
  1703  								Set: func(a interface{}) int {
  1704  									return a.(int)
  1705  								},
  1706  							},
  1707  						},
  1708  					},
  1709  					Set: func(v interface{}) int {
  1710  						m := v.(map[string]interface{})
  1711  						return m["index"].(int)
  1712  					},
  1713  				},
  1714  			},
  1715  
  1716  			State: nil,
  1717  
  1718  			Config: map[string]interface{}{
  1719  				"route": []map[string]interface{}{
  1720  					map[string]interface{}{
  1721  						"index": "1",
  1722  						"gateway": []interface{}{
  1723  							"${var.foo}",
  1724  						},
  1725  					},
  1726  				},
  1727  			},
  1728  
  1729  			ConfigVariables: map[string]ast.Variable{
  1730  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1731  			},
  1732  
  1733  			Diff: &terraform.InstanceDiff{
  1734  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1735  					"route.#": &terraform.ResourceAttrDiff{
  1736  						Old: "0",
  1737  						New: "1",
  1738  					},
  1739  					"route.~1.index": &terraform.ResourceAttrDiff{
  1740  						Old: "",
  1741  						New: "1",
  1742  					},
  1743  					"route.~1.gateway.#": &terraform.ResourceAttrDiff{
  1744  						NewComputed: true,
  1745  					},
  1746  				},
  1747  			},
  1748  
  1749  			Err: false,
  1750  		},
  1751  
  1752  		"#43 - Computed maps": {
  1753  			Schema: map[string]*Schema{
  1754  				"vars": &Schema{
  1755  					Type:     TypeMap,
  1756  					Computed: true,
  1757  				},
  1758  			},
  1759  
  1760  			State: nil,
  1761  
  1762  			Config: nil,
  1763  
  1764  			Diff: &terraform.InstanceDiff{
  1765  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1766  					"vars.%": &terraform.ResourceAttrDiff{
  1767  						Old:         "",
  1768  						NewComputed: true,
  1769  					},
  1770  				},
  1771  			},
  1772  
  1773  			Err: false,
  1774  		},
  1775  
  1776  		"#44 - Computed maps": {
  1777  			Schema: map[string]*Schema{
  1778  				"vars": &Schema{
  1779  					Type:     TypeMap,
  1780  					Computed: true,
  1781  				},
  1782  			},
  1783  
  1784  			State: &terraform.InstanceState{
  1785  				Attributes: map[string]string{
  1786  					"vars.%": "0",
  1787  				},
  1788  			},
  1789  
  1790  			Config: map[string]interface{}{
  1791  				"vars": map[string]interface{}{
  1792  					"bar": "${var.foo}",
  1793  				},
  1794  			},
  1795  
  1796  			ConfigVariables: map[string]ast.Variable{
  1797  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1798  			},
  1799  
  1800  			Diff: &terraform.InstanceDiff{
  1801  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1802  					"vars.%": &terraform.ResourceAttrDiff{
  1803  						Old:         "",
  1804  						NewComputed: true,
  1805  					},
  1806  				},
  1807  			},
  1808  
  1809  			Err: false,
  1810  		},
  1811  
  1812  		"#45 - Empty": {
  1813  			Schema: map[string]*Schema{},
  1814  
  1815  			State: &terraform.InstanceState{},
  1816  
  1817  			Config: map[string]interface{}{},
  1818  
  1819  			Diff: nil,
  1820  
  1821  			Err: false,
  1822  		},
  1823  
  1824  		"#46 - Float": {
  1825  			Schema: map[string]*Schema{
  1826  				"some_threshold": &Schema{
  1827  					Type: TypeFloat,
  1828  				},
  1829  			},
  1830  
  1831  			State: &terraform.InstanceState{
  1832  				Attributes: map[string]string{
  1833  					"some_threshold": "567.8",
  1834  				},
  1835  			},
  1836  
  1837  			Config: map[string]interface{}{
  1838  				"some_threshold": 12.34,
  1839  			},
  1840  
  1841  			Diff: &terraform.InstanceDiff{
  1842  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1843  					"some_threshold": &terraform.ResourceAttrDiff{
  1844  						Old: "567.8",
  1845  						New: "12.34",
  1846  					},
  1847  				},
  1848  			},
  1849  
  1850  			Err: false,
  1851  		},
  1852  
  1853  		"#47 - https://github.com/hashicorp/terraform/issues/824": {
  1854  			Schema: map[string]*Schema{
  1855  				"block_device": &Schema{
  1856  					Type:     TypeSet,
  1857  					Optional: true,
  1858  					Computed: true,
  1859  					Elem: &Resource{
  1860  						Schema: map[string]*Schema{
  1861  							"device_name": &Schema{
  1862  								Type:     TypeString,
  1863  								Required: true,
  1864  							},
  1865  							"delete_on_termination": &Schema{
  1866  								Type:     TypeBool,
  1867  								Optional: true,
  1868  								Default:  true,
  1869  							},
  1870  						},
  1871  					},
  1872  					Set: func(v interface{}) int {
  1873  						var buf bytes.Buffer
  1874  						m := v.(map[string]interface{})
  1875  						buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
  1876  						buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
  1877  						return hashcode.String(buf.String())
  1878  					},
  1879  				},
  1880  			},
  1881  
  1882  			State: &terraform.InstanceState{
  1883  				Attributes: map[string]string{
  1884  					"block_device.#":                                "2",
  1885  					"block_device.616397234.delete_on_termination":  "true",
  1886  					"block_device.616397234.device_name":            "/dev/sda1",
  1887  					"block_device.2801811477.delete_on_termination": "true",
  1888  					"block_device.2801811477.device_name":           "/dev/sdx",
  1889  				},
  1890  			},
  1891  
  1892  			Config: map[string]interface{}{
  1893  				"block_device": []map[string]interface{}{
  1894  					map[string]interface{}{
  1895  						"device_name": "/dev/sda1",
  1896  					},
  1897  					map[string]interface{}{
  1898  						"device_name": "/dev/sdx",
  1899  					},
  1900  				},
  1901  			},
  1902  			Diff: nil,
  1903  			Err:  false,
  1904  		},
  1905  
  1906  		"#48 - Zero value in state shouldn't result in diff": {
  1907  			Schema: map[string]*Schema{
  1908  				"port": &Schema{
  1909  					Type:     TypeBool,
  1910  					Optional: true,
  1911  					ForceNew: true,
  1912  				},
  1913  			},
  1914  
  1915  			State: &terraform.InstanceState{
  1916  				Attributes: map[string]string{
  1917  					"port": "false",
  1918  				},
  1919  			},
  1920  
  1921  			Config: map[string]interface{}{},
  1922  
  1923  			Diff: nil,
  1924  
  1925  			Err: false,
  1926  		},
  1927  
  1928  		"#49 Set - Same as #48 but for sets": {
  1929  			Schema: map[string]*Schema{
  1930  				"route": &Schema{
  1931  					Type:     TypeSet,
  1932  					Optional: true,
  1933  					Elem: &Resource{
  1934  						Schema: map[string]*Schema{
  1935  							"index": &Schema{
  1936  								Type:     TypeInt,
  1937  								Required: true,
  1938  							},
  1939  
  1940  							"gateway": &Schema{
  1941  								Type:     TypeSet,
  1942  								Optional: true,
  1943  								Elem:     &Schema{Type: TypeInt},
  1944  								Set: func(a interface{}) int {
  1945  									return a.(int)
  1946  								},
  1947  							},
  1948  						},
  1949  					},
  1950  					Set: func(v interface{}) int {
  1951  						m := v.(map[string]interface{})
  1952  						return m["index"].(int)
  1953  					},
  1954  				},
  1955  			},
  1956  
  1957  			State: &terraform.InstanceState{
  1958  				Attributes: map[string]string{
  1959  					"route.#": "0",
  1960  				},
  1961  			},
  1962  
  1963  			Config: map[string]interface{}{},
  1964  
  1965  			Diff: nil,
  1966  
  1967  			Err: false,
  1968  		},
  1969  
  1970  		"#50 - A set computed element shouldn't cause a diff": {
  1971  			Schema: map[string]*Schema{
  1972  				"active": &Schema{
  1973  					Type:     TypeBool,
  1974  					Computed: true,
  1975  					ForceNew: true,
  1976  				},
  1977  			},
  1978  
  1979  			State: &terraform.InstanceState{
  1980  				Attributes: map[string]string{
  1981  					"active": "true",
  1982  				},
  1983  			},
  1984  
  1985  			Config: map[string]interface{}{},
  1986  
  1987  			Diff: nil,
  1988  
  1989  			Err: false,
  1990  		},
  1991  
  1992  		"#51 - An empty set should show up in the diff": {
  1993  			Schema: map[string]*Schema{
  1994  				"instances": &Schema{
  1995  					Type:     TypeSet,
  1996  					Elem:     &Schema{Type: TypeString},
  1997  					Optional: true,
  1998  					ForceNew: true,
  1999  					Set: func(v interface{}) int {
  2000  						return len(v.(string))
  2001  					},
  2002  				},
  2003  			},
  2004  
  2005  			State: &terraform.InstanceState{
  2006  				Attributes: map[string]string{
  2007  					"instances.#": "1",
  2008  					"instances.3": "foo",
  2009  				},
  2010  			},
  2011  
  2012  			Config: map[string]interface{}{},
  2013  
  2014  			Diff: &terraform.InstanceDiff{
  2015  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2016  					"instances.#": &terraform.ResourceAttrDiff{
  2017  						Old:         "1",
  2018  						New:         "0",
  2019  						RequiresNew: true,
  2020  					},
  2021  					"instances.3": &terraform.ResourceAttrDiff{
  2022  						Old:        "foo",
  2023  						New:        "",
  2024  						NewRemoved: true,
  2025  					},
  2026  				},
  2027  			},
  2028  
  2029  			Err: false,
  2030  		},
  2031  
  2032  		"#52 - Map with empty value": {
  2033  			Schema: map[string]*Schema{
  2034  				"vars": &Schema{
  2035  					Type: TypeMap,
  2036  				},
  2037  			},
  2038  
  2039  			State: nil,
  2040  
  2041  			Config: map[string]interface{}{
  2042  				"vars": map[string]interface{}{
  2043  					"foo": "",
  2044  				},
  2045  			},
  2046  
  2047  			Diff: &terraform.InstanceDiff{
  2048  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2049  					"vars.%": &terraform.ResourceAttrDiff{
  2050  						Old: "0",
  2051  						New: "1",
  2052  					},
  2053  					"vars.foo": &terraform.ResourceAttrDiff{
  2054  						Old: "",
  2055  						New: "",
  2056  					},
  2057  				},
  2058  			},
  2059  
  2060  			Err: false,
  2061  		},
  2062  
  2063  		"#53 - Unset bool, not in state": {
  2064  			Schema: map[string]*Schema{
  2065  				"force": &Schema{
  2066  					Type:     TypeBool,
  2067  					Optional: true,
  2068  					ForceNew: true,
  2069  				},
  2070  			},
  2071  
  2072  			State: nil,
  2073  
  2074  			Config: map[string]interface{}{},
  2075  
  2076  			Diff: nil,
  2077  
  2078  			Err: false,
  2079  		},
  2080  
  2081  		"#54 - Unset set, not in state": {
  2082  			Schema: map[string]*Schema{
  2083  				"metadata_keys": &Schema{
  2084  					Type:     TypeSet,
  2085  					Optional: true,
  2086  					ForceNew: true,
  2087  					Elem:     &Schema{Type: TypeInt},
  2088  					Set:      func(interface{}) int { return 0 },
  2089  				},
  2090  			},
  2091  
  2092  			State: nil,
  2093  
  2094  			Config: map[string]interface{}{},
  2095  
  2096  			Diff: nil,
  2097  
  2098  			Err: false,
  2099  		},
  2100  
  2101  		"#55 - Unset list in state, should not show up computed": {
  2102  			Schema: map[string]*Schema{
  2103  				"metadata_keys": &Schema{
  2104  					Type:     TypeList,
  2105  					Optional: true,
  2106  					Computed: true,
  2107  					ForceNew: true,
  2108  					Elem:     &Schema{Type: TypeInt},
  2109  				},
  2110  			},
  2111  
  2112  			State: &terraform.InstanceState{
  2113  				Attributes: map[string]string{
  2114  					"metadata_keys.#": "0",
  2115  				},
  2116  			},
  2117  
  2118  			Config: map[string]interface{}{},
  2119  
  2120  			Diff: nil,
  2121  
  2122  			Err: false,
  2123  		},
  2124  
  2125  		"#56 - Set element computed substring": {
  2126  			Schema: map[string]*Schema{
  2127  				"ports": &Schema{
  2128  					Type:     TypeSet,
  2129  					Required: true,
  2130  					Elem:     &Schema{Type: TypeInt},
  2131  					Set: func(a interface{}) int {
  2132  						return a.(int)
  2133  					},
  2134  				},
  2135  			},
  2136  
  2137  			State: nil,
  2138  
  2139  			Config: map[string]interface{}{
  2140  				"ports": []interface{}{1, "${var.foo}32"},
  2141  			},
  2142  
  2143  			ConfigVariables: map[string]ast.Variable{
  2144  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  2145  			},
  2146  
  2147  			Diff: &terraform.InstanceDiff{
  2148  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2149  					"ports.#": &terraform.ResourceAttrDiff{
  2150  						Old:         "",
  2151  						New:         "",
  2152  						NewComputed: true,
  2153  					},
  2154  				},
  2155  			},
  2156  
  2157  			Err: false,
  2158  		},
  2159  
  2160  		"#57 Computed map without config that's known to be empty does not generate diff": {
  2161  			Schema: map[string]*Schema{
  2162  				"tags": &Schema{
  2163  					Type:     TypeMap,
  2164  					Computed: true,
  2165  				},
  2166  			},
  2167  
  2168  			Config: nil,
  2169  
  2170  			State: &terraform.InstanceState{
  2171  				Attributes: map[string]string{
  2172  					"tags.%": "0",
  2173  				},
  2174  			},
  2175  
  2176  			Diff: nil,
  2177  
  2178  			Err: false,
  2179  		},
  2180  
  2181  		"#58 Set with hyphen keys": {
  2182  			Schema: map[string]*Schema{
  2183  				"route": &Schema{
  2184  					Type:     TypeSet,
  2185  					Optional: true,
  2186  					Elem: &Resource{
  2187  						Schema: map[string]*Schema{
  2188  							"index": &Schema{
  2189  								Type:     TypeInt,
  2190  								Required: true,
  2191  							},
  2192  
  2193  							"gateway-name": &Schema{
  2194  								Type:     TypeString,
  2195  								Optional: true,
  2196  							},
  2197  						},
  2198  					},
  2199  					Set: func(v interface{}) int {
  2200  						m := v.(map[string]interface{})
  2201  						return m["index"].(int)
  2202  					},
  2203  				},
  2204  			},
  2205  
  2206  			State: nil,
  2207  
  2208  			Config: map[string]interface{}{
  2209  				"route": []map[string]interface{}{
  2210  					map[string]interface{}{
  2211  						"index":        "1",
  2212  						"gateway-name": "hello",
  2213  					},
  2214  				},
  2215  			},
  2216  
  2217  			Diff: &terraform.InstanceDiff{
  2218  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2219  					"route.#": &terraform.ResourceAttrDiff{
  2220  						Old: "0",
  2221  						New: "1",
  2222  					},
  2223  					"route.1.index": &terraform.ResourceAttrDiff{
  2224  						Old: "",
  2225  						New: "1",
  2226  					},
  2227  					"route.1.gateway-name": &terraform.ResourceAttrDiff{
  2228  						Old: "",
  2229  						New: "hello",
  2230  					},
  2231  				},
  2232  			},
  2233  
  2234  			Err: false,
  2235  		},
  2236  
  2237  		"#59: StateFunc in nested set (#1759)": {
  2238  			Schema: map[string]*Schema{
  2239  				"service_account": &Schema{
  2240  					Type:     TypeList,
  2241  					Optional: true,
  2242  					ForceNew: true,
  2243  					Elem: &Resource{
  2244  						Schema: map[string]*Schema{
  2245  							"scopes": &Schema{
  2246  								Type:     TypeSet,
  2247  								Required: true,
  2248  								ForceNew: true,
  2249  								Elem: &Schema{
  2250  									Type: TypeString,
  2251  									StateFunc: func(v interface{}) string {
  2252  										return v.(string) + "!"
  2253  									},
  2254  								},
  2255  								Set: func(v interface{}) int {
  2256  									i, err := strconv.Atoi(v.(string))
  2257  									if err != nil {
  2258  										t.Fatalf("err: %s", err)
  2259  									}
  2260  									return i
  2261  								},
  2262  							},
  2263  						},
  2264  					},
  2265  				},
  2266  			},
  2267  
  2268  			State: nil,
  2269  
  2270  			Config: map[string]interface{}{
  2271  				"service_account": []map[string]interface{}{
  2272  					{
  2273  						"scopes": []interface{}{"123"},
  2274  					},
  2275  				},
  2276  			},
  2277  
  2278  			Diff: &terraform.InstanceDiff{
  2279  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2280  					"service_account.#": &terraform.ResourceAttrDiff{
  2281  						Old:         "0",
  2282  						New:         "1",
  2283  						RequiresNew: true,
  2284  					},
  2285  					"service_account.0.scopes.#": &terraform.ResourceAttrDiff{
  2286  						Old:         "0",
  2287  						New:         "1",
  2288  						RequiresNew: true,
  2289  					},
  2290  					"service_account.0.scopes.123": &terraform.ResourceAttrDiff{
  2291  						Old:         "",
  2292  						New:         "123!",
  2293  						NewExtra:    "123",
  2294  						RequiresNew: true,
  2295  					},
  2296  				},
  2297  			},
  2298  
  2299  			Err: false,
  2300  		},
  2301  
  2302  		"#60 - Removing set elements": {
  2303  			Schema: map[string]*Schema{
  2304  				"instances": &Schema{
  2305  					Type:     TypeSet,
  2306  					Elem:     &Schema{Type: TypeString},
  2307  					Optional: true,
  2308  					ForceNew: true,
  2309  					Set: func(v interface{}) int {
  2310  						return len(v.(string))
  2311  					},
  2312  				},
  2313  			},
  2314  
  2315  			State: &terraform.InstanceState{
  2316  				Attributes: map[string]string{
  2317  					"instances.#": "2",
  2318  					"instances.3": "333",
  2319  					"instances.2": "22",
  2320  				},
  2321  			},
  2322  
  2323  			Config: map[string]interface{}{
  2324  				"instances": []interface{}{"333", "4444"},
  2325  			},
  2326  
  2327  			Diff: &terraform.InstanceDiff{
  2328  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2329  					"instances.#": &terraform.ResourceAttrDiff{
  2330  						Old: "2",
  2331  						New: "2",
  2332  					},
  2333  					"instances.2": &terraform.ResourceAttrDiff{
  2334  						Old:        "22",
  2335  						New:        "",
  2336  						NewRemoved: true,
  2337  					},
  2338  					"instances.3": &terraform.ResourceAttrDiff{
  2339  						Old:         "333",
  2340  						New:         "333",
  2341  						RequiresNew: true,
  2342  					},
  2343  					"instances.4": &terraform.ResourceAttrDiff{
  2344  						Old:         "",
  2345  						New:         "4444",
  2346  						RequiresNew: true,
  2347  					},
  2348  				},
  2349  			},
  2350  
  2351  			Err: false,
  2352  		},
  2353  
  2354  		"Bools can be set with 0/1 in config, still get true/false": {
  2355  			Schema: map[string]*Schema{
  2356  				"one": &Schema{
  2357  					Type:     TypeBool,
  2358  					Optional: true,
  2359  				},
  2360  				"two": &Schema{
  2361  					Type:     TypeBool,
  2362  					Optional: true,
  2363  				},
  2364  				"three": &Schema{
  2365  					Type:     TypeBool,
  2366  					Optional: true,
  2367  				},
  2368  			},
  2369  
  2370  			State: &terraform.InstanceState{
  2371  				Attributes: map[string]string{
  2372  					"one":   "false",
  2373  					"two":   "true",
  2374  					"three": "true",
  2375  				},
  2376  			},
  2377  
  2378  			Config: map[string]interface{}{
  2379  				"one": "1",
  2380  				"two": "0",
  2381  			},
  2382  
  2383  			Diff: &terraform.InstanceDiff{
  2384  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2385  					"one": &terraform.ResourceAttrDiff{
  2386  						Old: "false",
  2387  						New: "true",
  2388  					},
  2389  					"two": &terraform.ResourceAttrDiff{
  2390  						Old: "true",
  2391  						New: "false",
  2392  					},
  2393  					"three": &terraform.ResourceAttrDiff{
  2394  						Old:        "true",
  2395  						New:        "false",
  2396  						NewRemoved: true,
  2397  					},
  2398  				},
  2399  			},
  2400  
  2401  			Err: false,
  2402  		},
  2403  
  2404  		"tainted in state w/ no attr changes is still a replacement": {
  2405  			Schema: map[string]*Schema{},
  2406  
  2407  			State: &terraform.InstanceState{
  2408  				Attributes: map[string]string{
  2409  					"id": "someid",
  2410  				},
  2411  				Tainted: true,
  2412  			},
  2413  
  2414  			Config: map[string]interface{}{},
  2415  
  2416  			Diff: &terraform.InstanceDiff{
  2417  				Attributes:     map[string]*terraform.ResourceAttrDiff{},
  2418  				DestroyTainted: true,
  2419  			},
  2420  
  2421  			Err: false,
  2422  		},
  2423  	}
  2424  
  2425  	for tn, tc := range cases {
  2426  		c, err := config.NewRawConfig(tc.Config)
  2427  		if err != nil {
  2428  			t.Fatalf("#%q err: %s", tn, err)
  2429  		}
  2430  
  2431  		if len(tc.ConfigVariables) > 0 {
  2432  			if err := c.Interpolate(tc.ConfigVariables); err != nil {
  2433  				t.Fatalf("#%q err: %s", tn, err)
  2434  			}
  2435  		}
  2436  
  2437  		d, err := schemaMap(tc.Schema).Diff(
  2438  			tc.State, terraform.NewResourceConfig(c))
  2439  		if err != nil != tc.Err {
  2440  			t.Fatalf("#%q err: %s", tn, err)
  2441  		}
  2442  
  2443  		if !reflect.DeepEqual(tc.Diff, d) {
  2444  			t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Diff, d)
  2445  		}
  2446  	}
  2447  }
  2448  
  2449  func TestSchemaMap_Input(t *testing.T) {
  2450  	cases := map[string]struct {
  2451  		Schema map[string]*Schema
  2452  		Config map[string]interface{}
  2453  		Input  map[string]string
  2454  		Result map[string]interface{}
  2455  		Err    bool
  2456  	}{
  2457  		/*
  2458  		 * String decode
  2459  		 */
  2460  
  2461  		"uses input on optional field with no config": {
  2462  			Schema: map[string]*Schema{
  2463  				"availability_zone": &Schema{
  2464  					Type:     TypeString,
  2465  					Optional: true,
  2466  				},
  2467  			},
  2468  
  2469  			Input: map[string]string{
  2470  				"availability_zone": "foo",
  2471  			},
  2472  
  2473  			Result: map[string]interface{}{
  2474  				"availability_zone": "foo",
  2475  			},
  2476  
  2477  			Err: false,
  2478  		},
  2479  
  2480  		"input ignored when config has a value": {
  2481  			Schema: map[string]*Schema{
  2482  				"availability_zone": &Schema{
  2483  					Type:     TypeString,
  2484  					Optional: true,
  2485  				},
  2486  			},
  2487  
  2488  			Config: map[string]interface{}{
  2489  				"availability_zone": "bar",
  2490  			},
  2491  
  2492  			Input: map[string]string{
  2493  				"availability_zone": "foo",
  2494  			},
  2495  
  2496  			Result: map[string]interface{}{},
  2497  
  2498  			Err: false,
  2499  		},
  2500  
  2501  		"input ignored when schema has a default": {
  2502  			Schema: map[string]*Schema{
  2503  				"availability_zone": &Schema{
  2504  					Type:     TypeString,
  2505  					Default:  "foo",
  2506  					Optional: true,
  2507  				},
  2508  			},
  2509  
  2510  			Input: map[string]string{
  2511  				"availability_zone": "bar",
  2512  			},
  2513  
  2514  			Result: map[string]interface{}{},
  2515  
  2516  			Err: false,
  2517  		},
  2518  
  2519  		"input ignored when default function returns a value": {
  2520  			Schema: map[string]*Schema{
  2521  				"availability_zone": &Schema{
  2522  					Type: TypeString,
  2523  					DefaultFunc: func() (interface{}, error) {
  2524  						return "foo", nil
  2525  					},
  2526  					Optional: true,
  2527  				},
  2528  			},
  2529  
  2530  			Input: map[string]string{
  2531  				"availability_zone": "bar",
  2532  			},
  2533  
  2534  			Result: map[string]interface{}{},
  2535  
  2536  			Err: false,
  2537  		},
  2538  
  2539  		"input ignored when default function returns an empty string": {
  2540  			Schema: map[string]*Schema{
  2541  				"availability_zone": &Schema{
  2542  					Type:     TypeString,
  2543  					Default:  "",
  2544  					Optional: true,
  2545  				},
  2546  			},
  2547  
  2548  			Input: map[string]string{
  2549  				"availability_zone": "bar",
  2550  			},
  2551  
  2552  			Result: map[string]interface{}{},
  2553  
  2554  			Err: false,
  2555  		},
  2556  
  2557  		"input used when default function returns nil": {
  2558  			Schema: map[string]*Schema{
  2559  				"availability_zone": &Schema{
  2560  					Type: TypeString,
  2561  					DefaultFunc: func() (interface{}, error) {
  2562  						return nil, nil
  2563  					},
  2564  					Optional: true,
  2565  				},
  2566  			},
  2567  
  2568  			Input: map[string]string{
  2569  				"availability_zone": "bar",
  2570  			},
  2571  
  2572  			Result: map[string]interface{}{
  2573  				"availability_zone": "bar",
  2574  			},
  2575  
  2576  			Err: false,
  2577  		},
  2578  	}
  2579  
  2580  	for i, tc := range cases {
  2581  		if tc.Config == nil {
  2582  			tc.Config = make(map[string]interface{})
  2583  		}
  2584  
  2585  		c, err := config.NewRawConfig(tc.Config)
  2586  		if err != nil {
  2587  			t.Fatalf("err: %s", err)
  2588  		}
  2589  
  2590  		input := new(terraform.MockUIInput)
  2591  		input.InputReturnMap = tc.Input
  2592  
  2593  		rc := terraform.NewResourceConfig(c)
  2594  		rc.Config = make(map[string]interface{})
  2595  
  2596  		actual, err := schemaMap(tc.Schema).Input(input, rc)
  2597  		if err != nil != tc.Err {
  2598  			t.Fatalf("#%v err: %s", i, err)
  2599  		}
  2600  
  2601  		if !reflect.DeepEqual(tc.Result, actual.Config) {
  2602  			t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result)
  2603  		}
  2604  	}
  2605  }
  2606  
  2607  func TestSchemaMap_InputDefault(t *testing.T) {
  2608  	emptyConfig := make(map[string]interface{})
  2609  	c, err := config.NewRawConfig(emptyConfig)
  2610  	if err != nil {
  2611  		t.Fatalf("err: %s", err)
  2612  	}
  2613  	rc := terraform.NewResourceConfig(c)
  2614  	rc.Config = make(map[string]interface{})
  2615  
  2616  	input := new(terraform.MockUIInput)
  2617  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  2618  		t.Fatalf("InputFn should not be called on: %#v", opts)
  2619  		return "", nil
  2620  	}
  2621  
  2622  	schema := map[string]*Schema{
  2623  		"availability_zone": &Schema{
  2624  			Type:     TypeString,
  2625  			Default:  "foo",
  2626  			Optional: true,
  2627  		},
  2628  	}
  2629  	actual, err := schemaMap(schema).Input(input, rc)
  2630  	if err != nil {
  2631  		t.Fatalf("err: %s", err)
  2632  	}
  2633  
  2634  	expected := map[string]interface{}{}
  2635  
  2636  	if !reflect.DeepEqual(expected, actual.Config) {
  2637  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  2638  	}
  2639  }
  2640  
  2641  func TestSchemaMap_InputDeprecated(t *testing.T) {
  2642  	emptyConfig := make(map[string]interface{})
  2643  	c, err := config.NewRawConfig(emptyConfig)
  2644  	if err != nil {
  2645  		t.Fatalf("err: %s", err)
  2646  	}
  2647  	rc := terraform.NewResourceConfig(c)
  2648  	rc.Config = make(map[string]interface{})
  2649  
  2650  	input := new(terraform.MockUIInput)
  2651  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  2652  		t.Fatalf("InputFn should not be called on: %#v", opts)
  2653  		return "", nil
  2654  	}
  2655  
  2656  	schema := map[string]*Schema{
  2657  		"availability_zone": &Schema{
  2658  			Type:       TypeString,
  2659  			Deprecated: "long gone",
  2660  			Optional:   true,
  2661  		},
  2662  	}
  2663  	actual, err := schemaMap(schema).Input(input, rc)
  2664  	if err != nil {
  2665  		t.Fatalf("err: %s", err)
  2666  	}
  2667  
  2668  	expected := map[string]interface{}{}
  2669  
  2670  	if !reflect.DeepEqual(expected, actual.Config) {
  2671  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  2672  	}
  2673  }
  2674  
  2675  func TestSchemaMap_InternalValidate(t *testing.T) {
  2676  	cases := map[string]struct {
  2677  		In  map[string]*Schema
  2678  		Err bool
  2679  	}{
  2680  		"nothing": {
  2681  			nil,
  2682  			false,
  2683  		},
  2684  
  2685  		"Both optional and required": {
  2686  			map[string]*Schema{
  2687  				"foo": &Schema{
  2688  					Type:     TypeInt,
  2689  					Optional: true,
  2690  					Required: true,
  2691  				},
  2692  			},
  2693  			true,
  2694  		},
  2695  
  2696  		"No optional and no required": {
  2697  			map[string]*Schema{
  2698  				"foo": &Schema{
  2699  					Type: TypeInt,
  2700  				},
  2701  			},
  2702  			true,
  2703  		},
  2704  
  2705  		"Missing Type": {
  2706  			map[string]*Schema{
  2707  				"foo": &Schema{
  2708  					Required: true,
  2709  				},
  2710  			},
  2711  			true,
  2712  		},
  2713  
  2714  		"Required but computed": {
  2715  			map[string]*Schema{
  2716  				"foo": &Schema{
  2717  					Type:     TypeInt,
  2718  					Required: true,
  2719  					Computed: true,
  2720  				},
  2721  			},
  2722  			true,
  2723  		},
  2724  
  2725  		"Looks good": {
  2726  			map[string]*Schema{
  2727  				"foo": &Schema{
  2728  					Type:     TypeString,
  2729  					Required: true,
  2730  				},
  2731  			},
  2732  			false,
  2733  		},
  2734  
  2735  		"Computed but has default": {
  2736  			map[string]*Schema{
  2737  				"foo": &Schema{
  2738  					Type:     TypeInt,
  2739  					Optional: true,
  2740  					Computed: true,
  2741  					Default:  "foo",
  2742  				},
  2743  			},
  2744  			true,
  2745  		},
  2746  
  2747  		"Required but has default": {
  2748  			map[string]*Schema{
  2749  				"foo": &Schema{
  2750  					Type:     TypeInt,
  2751  					Optional: true,
  2752  					Required: true,
  2753  					Default:  "foo",
  2754  				},
  2755  			},
  2756  			true,
  2757  		},
  2758  
  2759  		"List element not set": {
  2760  			map[string]*Schema{
  2761  				"foo": &Schema{
  2762  					Type: TypeList,
  2763  				},
  2764  			},
  2765  			true,
  2766  		},
  2767  
  2768  		"List default": {
  2769  			map[string]*Schema{
  2770  				"foo": &Schema{
  2771  					Type:    TypeList,
  2772  					Elem:    &Schema{Type: TypeInt},
  2773  					Default: "foo",
  2774  				},
  2775  			},
  2776  			true,
  2777  		},
  2778  
  2779  		"List element computed": {
  2780  			map[string]*Schema{
  2781  				"foo": &Schema{
  2782  					Type:     TypeList,
  2783  					Optional: true,
  2784  					Elem: &Schema{
  2785  						Type:     TypeInt,
  2786  						Computed: true,
  2787  					},
  2788  				},
  2789  			},
  2790  			true,
  2791  		},
  2792  
  2793  		"List element with Set set": {
  2794  			map[string]*Schema{
  2795  				"foo": &Schema{
  2796  					Type:     TypeList,
  2797  					Elem:     &Schema{Type: TypeInt},
  2798  					Set:      func(interface{}) int { return 0 },
  2799  					Optional: true,
  2800  				},
  2801  			},
  2802  			true,
  2803  		},
  2804  
  2805  		"Set element with no Set set": {
  2806  			map[string]*Schema{
  2807  				"foo": &Schema{
  2808  					Type:     TypeSet,
  2809  					Elem:     &Schema{Type: TypeInt},
  2810  					Optional: true,
  2811  				},
  2812  			},
  2813  			false,
  2814  		},
  2815  
  2816  		"Required but computedWhen": {
  2817  			map[string]*Schema{
  2818  				"foo": &Schema{
  2819  					Type:         TypeInt,
  2820  					Required:     true,
  2821  					ComputedWhen: []string{"foo"},
  2822  				},
  2823  			},
  2824  			true,
  2825  		},
  2826  
  2827  		"Conflicting attributes cannot be required": {
  2828  			map[string]*Schema{
  2829  				"blacklist": &Schema{
  2830  					Type:     TypeBool,
  2831  					Required: true,
  2832  				},
  2833  				"whitelist": &Schema{
  2834  					Type:          TypeBool,
  2835  					Optional:      true,
  2836  					ConflictsWith: []string{"blacklist"},
  2837  				},
  2838  			},
  2839  			true,
  2840  		},
  2841  
  2842  		"Attribute with conflicts cannot be required": {
  2843  			map[string]*Schema{
  2844  				"whitelist": &Schema{
  2845  					Type:          TypeBool,
  2846  					Required:      true,
  2847  					ConflictsWith: []string{"blacklist"},
  2848  				},
  2849  			},
  2850  			true,
  2851  		},
  2852  
  2853  		"ConflictsWith cannot be used w/ Computed": {
  2854  			map[string]*Schema{
  2855  				"blacklist": &Schema{
  2856  					Type:     TypeBool,
  2857  					Computed: true,
  2858  				},
  2859  				"whitelist": &Schema{
  2860  					Type:          TypeBool,
  2861  					Optional:      true,
  2862  					ConflictsWith: []string{"blacklist"},
  2863  				},
  2864  			},
  2865  			true,
  2866  		},
  2867  
  2868  		"ConflictsWith cannot be used w/ ComputedWhen": {
  2869  			map[string]*Schema{
  2870  				"blacklist": &Schema{
  2871  					Type:         TypeBool,
  2872  					ComputedWhen: []string{"foor"},
  2873  				},
  2874  				"whitelist": &Schema{
  2875  					Type:          TypeBool,
  2876  					Required:      true,
  2877  					ConflictsWith: []string{"blacklist"},
  2878  				},
  2879  			},
  2880  			true,
  2881  		},
  2882  
  2883  		"Sub-resource invalid": {
  2884  			map[string]*Schema{
  2885  				"foo": &Schema{
  2886  					Type:     TypeList,
  2887  					Optional: true,
  2888  					Elem: &Resource{
  2889  						Schema: map[string]*Schema{
  2890  							"foo": new(Schema),
  2891  						},
  2892  					},
  2893  				},
  2894  			},
  2895  			true,
  2896  		},
  2897  
  2898  		"Sub-resource valid": {
  2899  			map[string]*Schema{
  2900  				"foo": &Schema{
  2901  					Type:     TypeList,
  2902  					Optional: true,
  2903  					Elem: &Resource{
  2904  						Schema: map[string]*Schema{
  2905  							"foo": &Schema{
  2906  								Type:     TypeInt,
  2907  								Optional: true,
  2908  							},
  2909  						},
  2910  					},
  2911  				},
  2912  			},
  2913  			false,
  2914  		},
  2915  
  2916  		"ValidateFunc on non-primitive": {
  2917  			map[string]*Schema{
  2918  				"foo": &Schema{
  2919  					Type:     TypeSet,
  2920  					Required: true,
  2921  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  2922  						return
  2923  					},
  2924  				},
  2925  			},
  2926  			true,
  2927  		},
  2928  	}
  2929  
  2930  	for tn, tc := range cases {
  2931  		err := schemaMap(tc.In).InternalValidate(schemaMap{})
  2932  		if err != nil != tc.Err {
  2933  			if tc.Err {
  2934  				t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In)
  2935  			}
  2936  			t.Fatalf("%q: Unexpected error occurred:\n\n%#v", tn, tc.In)
  2937  		}
  2938  	}
  2939  
  2940  }
  2941  
  2942  func TestSchemaMap_DiffSuppress(t *testing.T) {
  2943  	cases := map[string]struct {
  2944  		Schema          map[string]*Schema
  2945  		State           *terraform.InstanceState
  2946  		Config          map[string]interface{}
  2947  		ConfigVariables map[string]ast.Variable
  2948  		ExpectedDiff    *terraform.InstanceDiff
  2949  		Err             bool
  2950  	}{
  2951  		"#0 - Suppress otherwise valid diff by returning true": {
  2952  			Schema: map[string]*Schema{
  2953  				"availability_zone": {
  2954  					Type:     TypeString,
  2955  					Optional: true,
  2956  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  2957  						// Always suppress any diff
  2958  						return true
  2959  					},
  2960  				},
  2961  			},
  2962  
  2963  			State: nil,
  2964  
  2965  			Config: map[string]interface{}{
  2966  				"availability_zone": "foo",
  2967  			},
  2968  
  2969  			ExpectedDiff: nil,
  2970  
  2971  			Err: false,
  2972  		},
  2973  
  2974  		"#1 - Don't suppress diff by returning false": {
  2975  			Schema: map[string]*Schema{
  2976  				"availability_zone": {
  2977  					Type:     TypeString,
  2978  					Optional: true,
  2979  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  2980  						// Always suppress any diff
  2981  						return false
  2982  					},
  2983  				},
  2984  			},
  2985  
  2986  			State: nil,
  2987  
  2988  			Config: map[string]interface{}{
  2989  				"availability_zone": "foo",
  2990  			},
  2991  
  2992  			ExpectedDiff: &terraform.InstanceDiff{
  2993  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2994  					"availability_zone": {
  2995  						Old: "",
  2996  						New: "foo",
  2997  					},
  2998  				},
  2999  			},
  3000  
  3001  			Err: false,
  3002  		},
  3003  
  3004  		"Default with suppress makes no diff": {
  3005  			Schema: map[string]*Schema{
  3006  				"availability_zone": {
  3007  					Type:     TypeString,
  3008  					Optional: true,
  3009  					Default:  "foo",
  3010  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  3011  						return true
  3012  					},
  3013  				},
  3014  			},
  3015  
  3016  			State: nil,
  3017  
  3018  			Config: map[string]interface{}{},
  3019  
  3020  			ExpectedDiff: nil,
  3021  
  3022  			Err: false,
  3023  		},
  3024  
  3025  		"Default with false suppress makes diff": {
  3026  			Schema: map[string]*Schema{
  3027  				"availability_zone": {
  3028  					Type:     TypeString,
  3029  					Optional: true,
  3030  					Default:  "foo",
  3031  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  3032  						return false
  3033  					},
  3034  				},
  3035  			},
  3036  
  3037  			State: nil,
  3038  
  3039  			Config: map[string]interface{}{},
  3040  
  3041  			ExpectedDiff: &terraform.InstanceDiff{
  3042  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3043  					"availability_zone": {
  3044  						Old: "",
  3045  						New: "foo",
  3046  					},
  3047  				},
  3048  			},
  3049  
  3050  			Err: false,
  3051  		},
  3052  	}
  3053  
  3054  	for tn, tc := range cases {
  3055  		t.Run(tn, func(t *testing.T) {
  3056  			c, err := config.NewRawConfig(tc.Config)
  3057  			if err != nil {
  3058  				t.Fatalf("#%q err: %s", tn, err)
  3059  			}
  3060  
  3061  			if len(tc.ConfigVariables) > 0 {
  3062  				if err := c.Interpolate(tc.ConfigVariables); err != nil {
  3063  					t.Fatalf("#%q err: %s", tn, err)
  3064  				}
  3065  			}
  3066  
  3067  			d, err := schemaMap(tc.Schema).Diff(
  3068  				tc.State, terraform.NewResourceConfig(c))
  3069  			if err != nil != tc.Err {
  3070  				t.Fatalf("#%q err: %s", tn, err)
  3071  			}
  3072  
  3073  			if !reflect.DeepEqual(tc.ExpectedDiff, d) {
  3074  				t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d)
  3075  			}
  3076  		})
  3077  	}
  3078  }
  3079  
  3080  func TestSchemaMap_Validate(t *testing.T) {
  3081  	cases := map[string]struct {
  3082  		Schema   map[string]*Schema
  3083  		Config   map[string]interface{}
  3084  		Vars     map[string]string
  3085  		Err      bool
  3086  		Errors   []error
  3087  		Warnings []string
  3088  	}{
  3089  		"Good": {
  3090  			Schema: map[string]*Schema{
  3091  				"availability_zone": &Schema{
  3092  					Type:     TypeString,
  3093  					Optional: true,
  3094  					Computed: true,
  3095  					ForceNew: true,
  3096  				},
  3097  			},
  3098  
  3099  			Config: map[string]interface{}{
  3100  				"availability_zone": "foo",
  3101  			},
  3102  		},
  3103  
  3104  		"Good, because the var is not set and that error will come elsewhere": {
  3105  			Schema: map[string]*Schema{
  3106  				"size": &Schema{
  3107  					Type:     TypeInt,
  3108  					Required: true,
  3109  				},
  3110  			},
  3111  
  3112  			Config: map[string]interface{}{
  3113  				"size": "${var.foo}",
  3114  			},
  3115  
  3116  			Vars: map[string]string{
  3117  				"var.foo": config.UnknownVariableValue,
  3118  			},
  3119  		},
  3120  
  3121  		"Required field not set": {
  3122  			Schema: map[string]*Schema{
  3123  				"availability_zone": &Schema{
  3124  					Type:     TypeString,
  3125  					Required: true,
  3126  				},
  3127  			},
  3128  
  3129  			Config: map[string]interface{}{},
  3130  
  3131  			Err: true,
  3132  		},
  3133  
  3134  		"Invalid basic type": {
  3135  			Schema: map[string]*Schema{
  3136  				"port": &Schema{
  3137  					Type:     TypeInt,
  3138  					Required: true,
  3139  				},
  3140  			},
  3141  
  3142  			Config: map[string]interface{}{
  3143  				"port": "I am invalid",
  3144  			},
  3145  
  3146  			Err: true,
  3147  		},
  3148  
  3149  		"Invalid complex type": {
  3150  			Schema: map[string]*Schema{
  3151  				"user_data": &Schema{
  3152  					Type:     TypeString,
  3153  					Optional: true,
  3154  				},
  3155  			},
  3156  
  3157  			Config: map[string]interface{}{
  3158  				"user_data": []interface{}{
  3159  					map[string]interface{}{
  3160  						"foo": "bar",
  3161  					},
  3162  				},
  3163  			},
  3164  
  3165  			Err: true,
  3166  		},
  3167  
  3168  		"Bad type, interpolated": {
  3169  			Schema: map[string]*Schema{
  3170  				"size": &Schema{
  3171  					Type:     TypeInt,
  3172  					Required: true,
  3173  				},
  3174  			},
  3175  
  3176  			Config: map[string]interface{}{
  3177  				"size": "${var.foo}",
  3178  			},
  3179  
  3180  			Vars: map[string]string{
  3181  				"var.foo": "nope",
  3182  			},
  3183  
  3184  			Err: true,
  3185  		},
  3186  
  3187  		"Required but has DefaultFunc": {
  3188  			Schema: map[string]*Schema{
  3189  				"availability_zone": &Schema{
  3190  					Type:     TypeString,
  3191  					Required: true,
  3192  					DefaultFunc: func() (interface{}, error) {
  3193  						return "foo", nil
  3194  					},
  3195  				},
  3196  			},
  3197  
  3198  			Config: nil,
  3199  		},
  3200  
  3201  		"Required but has DefaultFunc return nil": {
  3202  			Schema: map[string]*Schema{
  3203  				"availability_zone": &Schema{
  3204  					Type:     TypeString,
  3205  					Required: true,
  3206  					DefaultFunc: func() (interface{}, error) {
  3207  						return nil, nil
  3208  					},
  3209  				},
  3210  			},
  3211  
  3212  			Config: nil,
  3213  
  3214  			Err: true,
  3215  		},
  3216  
  3217  		"Optional sub-resource": {
  3218  			Schema: map[string]*Schema{
  3219  				"ingress": &Schema{
  3220  					Type: TypeList,
  3221  					Elem: &Resource{
  3222  						Schema: map[string]*Schema{
  3223  							"from": &Schema{
  3224  								Type:     TypeInt,
  3225  								Required: true,
  3226  							},
  3227  						},
  3228  					},
  3229  				},
  3230  			},
  3231  
  3232  			Config: map[string]interface{}{},
  3233  
  3234  			Err: false,
  3235  		},
  3236  
  3237  		"Sub-resource is the wrong type": {
  3238  			Schema: map[string]*Schema{
  3239  				"ingress": &Schema{
  3240  					Type:     TypeList,
  3241  					Required: true,
  3242  					Elem: &Resource{
  3243  						Schema: map[string]*Schema{
  3244  							"from": &Schema{
  3245  								Type:     TypeInt,
  3246  								Required: true,
  3247  							},
  3248  						},
  3249  					},
  3250  				},
  3251  			},
  3252  
  3253  			Config: map[string]interface{}{
  3254  				"ingress": []interface{}{"foo"},
  3255  			},
  3256  
  3257  			Err: true,
  3258  		},
  3259  
  3260  		"Not a list": {
  3261  			Schema: map[string]*Schema{
  3262  				"ingress": &Schema{
  3263  					Type: TypeList,
  3264  					Elem: &Resource{
  3265  						Schema: map[string]*Schema{
  3266  							"from": &Schema{
  3267  								Type:     TypeInt,
  3268  								Required: true,
  3269  							},
  3270  						},
  3271  					},
  3272  				},
  3273  			},
  3274  
  3275  			Config: map[string]interface{}{
  3276  				"ingress": "foo",
  3277  			},
  3278  
  3279  			Err: true,
  3280  		},
  3281  
  3282  		"Required sub-resource field": {
  3283  			Schema: map[string]*Schema{
  3284  				"ingress": &Schema{
  3285  					Type: TypeList,
  3286  					Elem: &Resource{
  3287  						Schema: map[string]*Schema{
  3288  							"from": &Schema{
  3289  								Type:     TypeInt,
  3290  								Required: true,
  3291  							},
  3292  						},
  3293  					},
  3294  				},
  3295  			},
  3296  
  3297  			Config: map[string]interface{}{
  3298  				"ingress": []interface{}{
  3299  					map[string]interface{}{},
  3300  				},
  3301  			},
  3302  
  3303  			Err: true,
  3304  		},
  3305  
  3306  		"Good sub-resource": {
  3307  			Schema: map[string]*Schema{
  3308  				"ingress": &Schema{
  3309  					Type:     TypeList,
  3310  					Optional: true,
  3311  					Elem: &Resource{
  3312  						Schema: map[string]*Schema{
  3313  							"from": &Schema{
  3314  								Type:     TypeInt,
  3315  								Required: true,
  3316  							},
  3317  						},
  3318  					},
  3319  				},
  3320  			},
  3321  
  3322  			Config: map[string]interface{}{
  3323  				"ingress": []interface{}{
  3324  					map[string]interface{}{
  3325  						"from": 80,
  3326  					},
  3327  				},
  3328  			},
  3329  
  3330  			Err: false,
  3331  		},
  3332  
  3333  		"Invalid/unknown field": {
  3334  			Schema: map[string]*Schema{
  3335  				"availability_zone": &Schema{
  3336  					Type:     TypeString,
  3337  					Optional: true,
  3338  					Computed: true,
  3339  					ForceNew: true,
  3340  				},
  3341  			},
  3342  
  3343  			Config: map[string]interface{}{
  3344  				"foo": "bar",
  3345  			},
  3346  
  3347  			Err: true,
  3348  		},
  3349  
  3350  		"Invalid/unknown field with computed value": {
  3351  			Schema: map[string]*Schema{
  3352  				"availability_zone": &Schema{
  3353  					Type:     TypeString,
  3354  					Optional: true,
  3355  					Computed: true,
  3356  					ForceNew: true,
  3357  				},
  3358  			},
  3359  
  3360  			Config: map[string]interface{}{
  3361  				"foo": "${var.foo}",
  3362  			},
  3363  
  3364  			Vars: map[string]string{
  3365  				"var.foo": config.UnknownVariableValue,
  3366  			},
  3367  
  3368  			Err: true,
  3369  		},
  3370  
  3371  		"Computed field set": {
  3372  			Schema: map[string]*Schema{
  3373  				"availability_zone": &Schema{
  3374  					Type:     TypeString,
  3375  					Computed: true,
  3376  				},
  3377  			},
  3378  
  3379  			Config: map[string]interface{}{
  3380  				"availability_zone": "bar",
  3381  			},
  3382  
  3383  			Err: true,
  3384  		},
  3385  
  3386  		"Not a set": {
  3387  			Schema: map[string]*Schema{
  3388  				"ports": &Schema{
  3389  					Type:     TypeSet,
  3390  					Required: true,
  3391  					Elem:     &Schema{Type: TypeInt},
  3392  					Set: func(a interface{}) int {
  3393  						return a.(int)
  3394  					},
  3395  				},
  3396  			},
  3397  
  3398  			Config: map[string]interface{}{
  3399  				"ports": "foo",
  3400  			},
  3401  
  3402  			Err: true,
  3403  		},
  3404  
  3405  		"Maps": {
  3406  			Schema: map[string]*Schema{
  3407  				"user_data": &Schema{
  3408  					Type:     TypeMap,
  3409  					Optional: true,
  3410  				},
  3411  			},
  3412  
  3413  			Config: map[string]interface{}{
  3414  				"user_data": "foo",
  3415  			},
  3416  
  3417  			Err: true,
  3418  		},
  3419  
  3420  		"Good map: data surrounded by extra slice": {
  3421  			Schema: map[string]*Schema{
  3422  				"user_data": &Schema{
  3423  					Type:     TypeMap,
  3424  					Optional: true,
  3425  				},
  3426  			},
  3427  
  3428  			Config: map[string]interface{}{
  3429  				"user_data": []interface{}{
  3430  					map[string]interface{}{
  3431  						"foo": "bar",
  3432  					},
  3433  				},
  3434  			},
  3435  		},
  3436  
  3437  		"Good map": {
  3438  			Schema: map[string]*Schema{
  3439  				"user_data": &Schema{
  3440  					Type:     TypeMap,
  3441  					Optional: true,
  3442  				},
  3443  			},
  3444  
  3445  			Config: map[string]interface{}{
  3446  				"user_data": map[string]interface{}{
  3447  					"foo": "bar",
  3448  				},
  3449  			},
  3450  		},
  3451  
  3452  		"Bad map: just a slice": {
  3453  			Schema: map[string]*Schema{
  3454  				"user_data": &Schema{
  3455  					Type:     TypeMap,
  3456  					Optional: true,
  3457  				},
  3458  			},
  3459  
  3460  			Config: map[string]interface{}{
  3461  				"user_data": []interface{}{
  3462  					"foo",
  3463  				},
  3464  			},
  3465  
  3466  			Err: true,
  3467  		},
  3468  
  3469  		"Good set: config has slice with single interpolated value": {
  3470  			Schema: map[string]*Schema{
  3471  				"security_groups": &Schema{
  3472  					Type:     TypeSet,
  3473  					Optional: true,
  3474  					Computed: true,
  3475  					ForceNew: true,
  3476  					Elem:     &Schema{Type: TypeString},
  3477  					Set: func(v interface{}) int {
  3478  						return len(v.(string))
  3479  					},
  3480  				},
  3481  			},
  3482  
  3483  			Config: map[string]interface{}{
  3484  				"security_groups": []interface{}{"${var.foo}"},
  3485  			},
  3486  
  3487  			Err: false,
  3488  		},
  3489  
  3490  		"Bad set: config has single interpolated value": {
  3491  			Schema: map[string]*Schema{
  3492  				"security_groups": &Schema{
  3493  					Type:     TypeSet,
  3494  					Optional: true,
  3495  					Computed: true,
  3496  					ForceNew: true,
  3497  					Elem:     &Schema{Type: TypeString},
  3498  				},
  3499  			},
  3500  
  3501  			Config: map[string]interface{}{
  3502  				"security_groups": "${var.foo}",
  3503  			},
  3504  
  3505  			Err: true,
  3506  		},
  3507  
  3508  		"Bad, subresource should not allow unknown elements": {
  3509  			Schema: map[string]*Schema{
  3510  				"ingress": &Schema{
  3511  					Type:     TypeList,
  3512  					Optional: true,
  3513  					Elem: &Resource{
  3514  						Schema: map[string]*Schema{
  3515  							"port": &Schema{
  3516  								Type:     TypeInt,
  3517  								Required: true,
  3518  							},
  3519  						},
  3520  					},
  3521  				},
  3522  			},
  3523  
  3524  			Config: map[string]interface{}{
  3525  				"ingress": []interface{}{
  3526  					map[string]interface{}{
  3527  						"port":  80,
  3528  						"other": "yes",
  3529  					},
  3530  				},
  3531  			},
  3532  
  3533  			Err: true,
  3534  		},
  3535  
  3536  		"Bad, subresource should not allow invalid types": {
  3537  			Schema: map[string]*Schema{
  3538  				"ingress": &Schema{
  3539  					Type:     TypeList,
  3540  					Optional: true,
  3541  					Elem: &Resource{
  3542  						Schema: map[string]*Schema{
  3543  							"port": &Schema{
  3544  								Type:     TypeInt,
  3545  								Required: true,
  3546  							},
  3547  						},
  3548  					},
  3549  				},
  3550  			},
  3551  
  3552  			Config: map[string]interface{}{
  3553  				"ingress": []interface{}{
  3554  					map[string]interface{}{
  3555  						"port": "bad",
  3556  					},
  3557  				},
  3558  			},
  3559  
  3560  			Err: true,
  3561  		},
  3562  
  3563  		"Bad, should not allow lists to be assigned to string attributes": {
  3564  			Schema: map[string]*Schema{
  3565  				"availability_zone": &Schema{
  3566  					Type:     TypeString,
  3567  					Required: true,
  3568  				},
  3569  			},
  3570  
  3571  			Config: map[string]interface{}{
  3572  				"availability_zone": []interface{}{"foo", "bar", "baz"},
  3573  			},
  3574  
  3575  			Err: true,
  3576  		},
  3577  
  3578  		"Bad, should not allow maps to be assigned to string attributes": {
  3579  			Schema: map[string]*Schema{
  3580  				"availability_zone": &Schema{
  3581  					Type:     TypeString,
  3582  					Required: true,
  3583  				},
  3584  			},
  3585  
  3586  			Config: map[string]interface{}{
  3587  				"availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"},
  3588  			},
  3589  
  3590  			Err: true,
  3591  		},
  3592  
  3593  		"Deprecated attribute usage generates warning, but not error": {
  3594  			Schema: map[string]*Schema{
  3595  				"old_news": &Schema{
  3596  					Type:       TypeString,
  3597  					Optional:   true,
  3598  					Deprecated: "please use 'new_news' instead",
  3599  				},
  3600  			},
  3601  
  3602  			Config: map[string]interface{}{
  3603  				"old_news": "extra extra!",
  3604  			},
  3605  
  3606  			Err: false,
  3607  
  3608  			Warnings: []string{
  3609  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  3610  			},
  3611  		},
  3612  
  3613  		"Deprecated generates no warnings if attr not used": {
  3614  			Schema: map[string]*Schema{
  3615  				"old_news": &Schema{
  3616  					Type:       TypeString,
  3617  					Optional:   true,
  3618  					Deprecated: "please use 'new_news' instead",
  3619  				},
  3620  			},
  3621  
  3622  			Err: false,
  3623  
  3624  			Warnings: nil,
  3625  		},
  3626  
  3627  		"Removed attribute usage generates error": {
  3628  			Schema: map[string]*Schema{
  3629  				"long_gone": &Schema{
  3630  					Type:     TypeString,
  3631  					Optional: true,
  3632  					Removed:  "no longer supported by Cloud API",
  3633  				},
  3634  			},
  3635  
  3636  			Config: map[string]interface{}{
  3637  				"long_gone": "still here!",
  3638  			},
  3639  
  3640  			Err: true,
  3641  			Errors: []error{
  3642  				fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"),
  3643  			},
  3644  		},
  3645  
  3646  		"Removed generates no errors if attr not used": {
  3647  			Schema: map[string]*Schema{
  3648  				"long_gone": &Schema{
  3649  					Type:     TypeString,
  3650  					Optional: true,
  3651  					Removed:  "no longer supported by Cloud API",
  3652  				},
  3653  			},
  3654  
  3655  			Err: false,
  3656  		},
  3657  
  3658  		"Conflicting attributes generate error": {
  3659  			Schema: map[string]*Schema{
  3660  				"whitelist": &Schema{
  3661  					Type:     TypeString,
  3662  					Optional: true,
  3663  				},
  3664  				"blacklist": &Schema{
  3665  					Type:          TypeString,
  3666  					Optional:      true,
  3667  					ConflictsWith: []string{"whitelist"},
  3668  				},
  3669  			},
  3670  
  3671  			Config: map[string]interface{}{
  3672  				"whitelist": "white-val",
  3673  				"blacklist": "black-val",
  3674  			},
  3675  
  3676  			Err: true,
  3677  			Errors: []error{
  3678  				fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"),
  3679  			},
  3680  		},
  3681  
  3682  		"Required attribute & undefined conflicting optional are good": {
  3683  			Schema: map[string]*Schema{
  3684  				"required_att": &Schema{
  3685  					Type:     TypeString,
  3686  					Required: true,
  3687  				},
  3688  				"optional_att": &Schema{
  3689  					Type:          TypeString,
  3690  					Optional:      true,
  3691  					ConflictsWith: []string{"required_att"},
  3692  				},
  3693  			},
  3694  
  3695  			Config: map[string]interface{}{
  3696  				"required_att": "required-val",
  3697  			},
  3698  
  3699  			Err: false,
  3700  		},
  3701  
  3702  		"Required conflicting attribute & defined optional generate error": {
  3703  			Schema: map[string]*Schema{
  3704  				"required_att": &Schema{
  3705  					Type:     TypeString,
  3706  					Required: true,
  3707  				},
  3708  				"optional_att": &Schema{
  3709  					Type:          TypeString,
  3710  					Optional:      true,
  3711  					ConflictsWith: []string{"required_att"},
  3712  				},
  3713  			},
  3714  
  3715  			Config: map[string]interface{}{
  3716  				"required_att": "required-val",
  3717  				"optional_att": "optional-val",
  3718  			},
  3719  
  3720  			Err: true,
  3721  			Errors: []error{
  3722  				fmt.Errorf(`"optional_att": conflicts with required_att ("required-val")`),
  3723  			},
  3724  		},
  3725  
  3726  		"Good with ValidateFunc": {
  3727  			Schema: map[string]*Schema{
  3728  				"validate_me": &Schema{
  3729  					Type:     TypeString,
  3730  					Required: true,
  3731  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3732  						return
  3733  					},
  3734  				},
  3735  			},
  3736  			Config: map[string]interface{}{
  3737  				"validate_me": "valid",
  3738  			},
  3739  			Err: false,
  3740  		},
  3741  
  3742  		"Bad with ValidateFunc": {
  3743  			Schema: map[string]*Schema{
  3744  				"validate_me": &Schema{
  3745  					Type:     TypeString,
  3746  					Required: true,
  3747  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3748  						es = append(es, fmt.Errorf("something is not right here"))
  3749  						return
  3750  					},
  3751  				},
  3752  			},
  3753  			Config: map[string]interface{}{
  3754  				"validate_me": "invalid",
  3755  			},
  3756  			Err: true,
  3757  			Errors: []error{
  3758  				fmt.Errorf(`something is not right here`),
  3759  			},
  3760  		},
  3761  
  3762  		"ValidateFunc not called when type does not match": {
  3763  			Schema: map[string]*Schema{
  3764  				"number": &Schema{
  3765  					Type:     TypeInt,
  3766  					Required: true,
  3767  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3768  						t.Fatalf("Should not have gotten validate call")
  3769  						return
  3770  					},
  3771  				},
  3772  			},
  3773  			Config: map[string]interface{}{
  3774  				"number": "NaN",
  3775  			},
  3776  			Err: true,
  3777  		},
  3778  
  3779  		"ValidateFunc gets decoded type": {
  3780  			Schema: map[string]*Schema{
  3781  				"maybe": &Schema{
  3782  					Type:     TypeBool,
  3783  					Required: true,
  3784  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3785  						if _, ok := v.(bool); !ok {
  3786  							t.Fatalf("Expected bool, got: %#v", v)
  3787  						}
  3788  						return
  3789  					},
  3790  				},
  3791  			},
  3792  			Config: map[string]interface{}{
  3793  				"maybe": "true",
  3794  			},
  3795  		},
  3796  
  3797  		"ValidateFunc is not called with a computed value": {
  3798  			Schema: map[string]*Schema{
  3799  				"validate_me": &Schema{
  3800  					Type:     TypeString,
  3801  					Required: true,
  3802  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3803  						es = append(es, fmt.Errorf("something is not right here"))
  3804  						return
  3805  					},
  3806  				},
  3807  			},
  3808  			Config: map[string]interface{}{
  3809  				"validate_me": "${var.foo}",
  3810  			},
  3811  			Vars: map[string]string{
  3812  				"var.foo": config.UnknownVariableValue,
  3813  			},
  3814  
  3815  			Err: false,
  3816  		},
  3817  	}
  3818  
  3819  	for tn, tc := range cases {
  3820  		c, err := config.NewRawConfig(tc.Config)
  3821  		if err != nil {
  3822  			t.Fatalf("err: %s", err)
  3823  		}
  3824  		if tc.Vars != nil {
  3825  			vars := make(map[string]ast.Variable)
  3826  			for k, v := range tc.Vars {
  3827  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  3828  			}
  3829  
  3830  			if err := c.Interpolate(vars); err != nil {
  3831  				t.Fatalf("err: %s", err)
  3832  			}
  3833  		}
  3834  
  3835  		ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3836  		if len(es) > 0 != tc.Err {
  3837  			if len(es) == 0 {
  3838  				t.Errorf("%q: no errors", tn)
  3839  			}
  3840  
  3841  			for _, e := range es {
  3842  				t.Errorf("%q: err: %s", tn, e)
  3843  			}
  3844  
  3845  			t.FailNow()
  3846  		}
  3847  
  3848  		if !reflect.DeepEqual(ws, tc.Warnings) {
  3849  			t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws)
  3850  		}
  3851  
  3852  		if tc.Errors != nil {
  3853  			if !reflect.DeepEqual(es, tc.Errors) {
  3854  				t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es)
  3855  			}
  3856  		}
  3857  	}
  3858  }
  3859  
  3860  func TestSchemaSet_ValidateMaxItems(t *testing.T) {
  3861  	cases := map[string]struct {
  3862  		Schema          map[string]*Schema
  3863  		State           *terraform.InstanceState
  3864  		Config          map[string]interface{}
  3865  		ConfigVariables map[string]string
  3866  		Diff            *terraform.InstanceDiff
  3867  		Err             bool
  3868  		Errors          []error
  3869  	}{
  3870  		"#0": {
  3871  			Schema: map[string]*Schema{
  3872  				"aliases": &Schema{
  3873  					Type:     TypeSet,
  3874  					Optional: true,
  3875  					MaxItems: 1,
  3876  					Elem:     &Schema{Type: TypeString},
  3877  				},
  3878  			},
  3879  			State: nil,
  3880  			Config: map[string]interface{}{
  3881  				"aliases": []interface{}{"foo", "bar"},
  3882  			},
  3883  			Diff: nil,
  3884  			Err:  true,
  3885  			Errors: []error{
  3886  				fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"),
  3887  			},
  3888  		},
  3889  		"#1": {
  3890  			Schema: map[string]*Schema{
  3891  				"aliases": &Schema{
  3892  					Type:     TypeSet,
  3893  					Optional: true,
  3894  					Elem:     &Schema{Type: TypeString},
  3895  				},
  3896  			},
  3897  			State: nil,
  3898  			Config: map[string]interface{}{
  3899  				"aliases": []interface{}{"foo", "bar"},
  3900  			},
  3901  			Diff:   nil,
  3902  			Err:    false,
  3903  			Errors: nil,
  3904  		},
  3905  		"#2": {
  3906  			Schema: map[string]*Schema{
  3907  				"aliases": &Schema{
  3908  					Type:     TypeSet,
  3909  					Optional: true,
  3910  					MaxItems: 1,
  3911  					Elem:     &Schema{Type: TypeString},
  3912  				},
  3913  			},
  3914  			State: nil,
  3915  			Config: map[string]interface{}{
  3916  				"aliases": []interface{}{"foo"},
  3917  			},
  3918  			Diff:   nil,
  3919  			Err:    false,
  3920  			Errors: nil,
  3921  		},
  3922  	}
  3923  
  3924  	for tn, tc := range cases {
  3925  		c, err := config.NewRawConfig(tc.Config)
  3926  		if err != nil {
  3927  			t.Fatalf("%q: err: %s", tn, err)
  3928  		}
  3929  		_, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3930  
  3931  		if len(es) > 0 != tc.Err {
  3932  			if len(es) == 0 {
  3933  				t.Errorf("%q: no errors", tn)
  3934  			}
  3935  
  3936  			for _, e := range es {
  3937  				t.Errorf("%q: err: %s", tn, e)
  3938  			}
  3939  
  3940  			t.FailNow()
  3941  		}
  3942  
  3943  		if tc.Errors != nil {
  3944  			if !reflect.DeepEqual(es, tc.Errors) {
  3945  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  3946  			}
  3947  		}
  3948  	}
  3949  }
  3950  
  3951  func TestSchemaSet_ValidateMinItems(t *testing.T) {
  3952  	cases := map[string]struct {
  3953  		Schema          map[string]*Schema
  3954  		State           *terraform.InstanceState
  3955  		Config          map[string]interface{}
  3956  		ConfigVariables map[string]string
  3957  		Diff            *terraform.InstanceDiff
  3958  		Err             bool
  3959  		Errors          []error
  3960  	}{
  3961  		"#0": {
  3962  			Schema: map[string]*Schema{
  3963  				"aliases": &Schema{
  3964  					Type:     TypeSet,
  3965  					Optional: true,
  3966  					MinItems: 2,
  3967  					Elem:     &Schema{Type: TypeString},
  3968  				},
  3969  			},
  3970  			State: nil,
  3971  			Config: map[string]interface{}{
  3972  				"aliases": []interface{}{"foo", "bar"},
  3973  			},
  3974  			Diff:   nil,
  3975  			Err:    false,
  3976  			Errors: nil,
  3977  		},
  3978  		"#1": {
  3979  			Schema: map[string]*Schema{
  3980  				"aliases": &Schema{
  3981  					Type:     TypeSet,
  3982  					Optional: true,
  3983  					Elem:     &Schema{Type: TypeString},
  3984  				},
  3985  			},
  3986  			State: nil,
  3987  			Config: map[string]interface{}{
  3988  				"aliases": []interface{}{"foo", "bar"},
  3989  			},
  3990  			Diff:   nil,
  3991  			Err:    false,
  3992  			Errors: nil,
  3993  		},
  3994  		"#2": {
  3995  			Schema: map[string]*Schema{
  3996  				"aliases": &Schema{
  3997  					Type:     TypeSet,
  3998  					Optional: true,
  3999  					MinItems: 2,
  4000  					Elem:     &Schema{Type: TypeString},
  4001  				},
  4002  			},
  4003  			State: nil,
  4004  			Config: map[string]interface{}{
  4005  				"aliases": []interface{}{"foo"},
  4006  			},
  4007  			Diff: nil,
  4008  			Err:  true,
  4009  			Errors: []error{
  4010  				fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"),
  4011  			},
  4012  		},
  4013  	}
  4014  
  4015  	for tn, tc := range cases {
  4016  		c, err := config.NewRawConfig(tc.Config)
  4017  		if err != nil {
  4018  			t.Fatalf("%q: err: %s", tn, err)
  4019  		}
  4020  		_, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  4021  
  4022  		if len(es) > 0 != tc.Err {
  4023  			if len(es) == 0 {
  4024  				t.Errorf("%q: no errors", tn)
  4025  			}
  4026  
  4027  			for _, e := range es {
  4028  				t.Errorf("%q: err: %s", tn, e)
  4029  			}
  4030  
  4031  			t.FailNow()
  4032  		}
  4033  
  4034  		if tc.Errors != nil {
  4035  			if !reflect.DeepEqual(es, tc.Errors) {
  4036  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  4037  			}
  4038  		}
  4039  	}
  4040  }