github.com/hashicorp/hcl/v2@v2.20.0/hclwrite/ast_body_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclwrite
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/davecgh/go-spew/spew"
    13  	"github.com/google/go-cmp/cmp"
    14  	"github.com/hashicorp/hcl/v2"
    15  	"github.com/hashicorp/hcl/v2/hclsyntax"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  func TestBodyGetAttribute(t *testing.T) {
    20  	tests := []struct {
    21  		src  string
    22  		name string
    23  		want Tokens
    24  	}{
    25  		{
    26  			"",
    27  			"a",
    28  			nil,
    29  		},
    30  		{
    31  			"a = 1\n",
    32  			"a",
    33  			Tokens{
    34  				{
    35  					Type:         hclsyntax.TokenIdent,
    36  					Bytes:        []byte{'a'},
    37  					SpacesBefore: 0,
    38  				},
    39  				{
    40  					Type:         hclsyntax.TokenEqual,
    41  					Bytes:        []byte{'='},
    42  					SpacesBefore: 1,
    43  				},
    44  				{
    45  					Type:         hclsyntax.TokenNumberLit,
    46  					Bytes:        []byte{'1'},
    47  					SpacesBefore: 1,
    48  				},
    49  				{
    50  					Type:         hclsyntax.TokenNewline,
    51  					Bytes:        []byte{'\n'},
    52  					SpacesBefore: 0,
    53  				},
    54  			},
    55  		},
    56  		{
    57  			"a = 1\nb = 1\nc = 1\n",
    58  			"a",
    59  			Tokens{
    60  				{
    61  					Type:         hclsyntax.TokenIdent,
    62  					Bytes:        []byte{'a'},
    63  					SpacesBefore: 0,
    64  				},
    65  				{
    66  					Type:         hclsyntax.TokenEqual,
    67  					Bytes:        []byte{'='},
    68  					SpacesBefore: 1,
    69  				},
    70  				{
    71  					Type:         hclsyntax.TokenNumberLit,
    72  					Bytes:        []byte{'1'},
    73  					SpacesBefore: 1,
    74  				},
    75  				{
    76  					Type:         hclsyntax.TokenNewline,
    77  					Bytes:        []byte{'\n'},
    78  					SpacesBefore: 0,
    79  				},
    80  			},
    81  		},
    82  		{
    83  			"a = 1\nb = 2\nc = 3\n",
    84  			"b",
    85  			Tokens{
    86  				{
    87  					Type:         hclsyntax.TokenIdent,
    88  					Bytes:        []byte{'b'},
    89  					SpacesBefore: 0,
    90  				},
    91  				{
    92  					Type:         hclsyntax.TokenEqual,
    93  					Bytes:        []byte{'='},
    94  					SpacesBefore: 1,
    95  				},
    96  				{
    97  					Type:         hclsyntax.TokenNumberLit,
    98  					Bytes:        []byte{'2'},
    99  					SpacesBefore: 1,
   100  				},
   101  				{
   102  					Type:         hclsyntax.TokenNewline,
   103  					Bytes:        []byte{'\n'},
   104  					SpacesBefore: 0,
   105  				},
   106  			},
   107  		},
   108  		{
   109  			"a = 1\nb = 2\nc = 3\n",
   110  			"c",
   111  			Tokens{
   112  				{
   113  					Type:         hclsyntax.TokenIdent,
   114  					Bytes:        []byte{'c'},
   115  					SpacesBefore: 0,
   116  				},
   117  				{
   118  					Type:         hclsyntax.TokenEqual,
   119  					Bytes:        []byte{'='},
   120  					SpacesBefore: 1,
   121  				},
   122  				{
   123  					Type:         hclsyntax.TokenNumberLit,
   124  					Bytes:        []byte{'3'},
   125  					SpacesBefore: 1,
   126  				},
   127  				{
   128  					Type:         hclsyntax.TokenNewline,
   129  					Bytes:        []byte{'\n'},
   130  					SpacesBefore: 0,
   131  				},
   132  			},
   133  		},
   134  		{
   135  			"a = 1\n# b is a b\nb = 2\nc = 3\n",
   136  			"b",
   137  			Tokens{
   138  				{
   139  					// Recognized as a lead comment and so attached to the attribute
   140  					Type:         hclsyntax.TokenComment,
   141  					Bytes:        []byte("# b is a b\n"),
   142  					SpacesBefore: 0,
   143  				},
   144  				{
   145  					Type:         hclsyntax.TokenIdent,
   146  					Bytes:        []byte{'b'},
   147  					SpacesBefore: 0,
   148  				},
   149  				{
   150  					Type:         hclsyntax.TokenEqual,
   151  					Bytes:        []byte{'='},
   152  					SpacesBefore: 1,
   153  				},
   154  				{
   155  					Type:         hclsyntax.TokenNumberLit,
   156  					Bytes:        []byte{'2'},
   157  					SpacesBefore: 1,
   158  				},
   159  				{
   160  					Type:         hclsyntax.TokenNewline,
   161  					Bytes:        []byte{'\n'},
   162  					SpacesBefore: 0,
   163  				},
   164  			},
   165  		},
   166  		{
   167  			"a = 1\n# not attached to a or b\n\nb = 2\nc = 3\n",
   168  			"b",
   169  			Tokens{
   170  				{
   171  					Type:         hclsyntax.TokenIdent,
   172  					Bytes:        []byte{'b'},
   173  					SpacesBefore: 0,
   174  				},
   175  				{
   176  					Type:         hclsyntax.TokenEqual,
   177  					Bytes:        []byte{'='},
   178  					SpacesBefore: 1,
   179  				},
   180  				{
   181  					Type:         hclsyntax.TokenNumberLit,
   182  					Bytes:        []byte{'2'},
   183  					SpacesBefore: 1,
   184  				},
   185  				{
   186  					Type:         hclsyntax.TokenNewline,
   187  					Bytes:        []byte{'\n'},
   188  					SpacesBefore: 0,
   189  				},
   190  			},
   191  		},
   192  	}
   193  
   194  	for _, test := range tests {
   195  		t.Run(fmt.Sprintf("%s in %s", test.name, test.src), func(t *testing.T) {
   196  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   197  			if len(diags) != 0 {
   198  				for _, diag := range diags {
   199  					t.Logf("- %s", diag.Error())
   200  				}
   201  				t.Fatalf("unexpected diagnostics")
   202  			}
   203  
   204  			attr := f.Body().GetAttribute(test.name)
   205  			if attr == nil {
   206  				if test.want != nil {
   207  					t.Fatal("attribute not found, but want it to exist")
   208  				}
   209  			} else {
   210  				if test.want == nil {
   211  					t.Fatal("attribute found, but expecting not found")
   212  				}
   213  
   214  				got := attr.BuildTokens(nil)
   215  				if !reflect.DeepEqual(got, test.want) {
   216  					t.Errorf("wrong result\ngot:  %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.want))
   217  				}
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func TestBodyFirstMatchingBlock(t *testing.T) {
   224  	src := `a = "b"
   225  service {
   226    attr0 = "val0"
   227  }
   228  service "label1" {
   229    attr1 = "val1"
   230  }
   231  service "label1" "label2" {
   232    attr2 = "val2"
   233  }
   234  parent {
   235    attr3 = "val3"
   236    child {
   237      attr4 = "val4"
   238    }
   239  }
   240  `
   241  
   242  	tests := []struct {
   243  		src      string
   244  		typeName string
   245  		labels   []string
   246  		want     string
   247  	}{
   248  		{
   249  			src,
   250  			"service",
   251  			[]string{},
   252  			`service {
   253    attr0 = "val0"
   254  }
   255  `,
   256  		},
   257  		{
   258  			src,
   259  			"service",
   260  			[]string{"label1"},
   261  			`service "label1" {
   262    attr1 = "val1"
   263  }
   264  `,
   265  		},
   266  		{
   267  			src,
   268  			"service",
   269  			[]string{"label1", "label2"},
   270  			`service "label1" "label2" {
   271    attr2 = "val2"
   272  }
   273  `,
   274  		},
   275  		{
   276  			src,
   277  			"parent",
   278  			[]string{},
   279  			`parent {
   280    attr3 = "val3"
   281    child {
   282      attr4 = "val4"
   283    }
   284  }
   285  `,
   286  		},
   287  		{
   288  			src,
   289  			"hoge",
   290  			[]string{},
   291  			"",
   292  		},
   293  		{
   294  			src,
   295  			"hoge",
   296  			[]string{"label1"},
   297  			"",
   298  		},
   299  		{
   300  			src,
   301  			"service",
   302  			[]string{"label2"},
   303  			"",
   304  		},
   305  		{
   306  			src,
   307  			"service",
   308  			[]string{"label2", "label1"},
   309  			"",
   310  		},
   311  		{
   312  			src,
   313  			"child",
   314  			[]string{},
   315  			"",
   316  		},
   317  	}
   318  
   319  	for _, test := range tests {
   320  		t.Run(fmt.Sprintf("%s %s", test.typeName, strings.Join(test.labels, " ")), func(t *testing.T) {
   321  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   322  			if len(diags) != 0 {
   323  				for _, diag := range diags {
   324  					t.Logf("- %s", diag.Error())
   325  				}
   326  				t.Fatalf("unexpected diagnostics")
   327  			}
   328  
   329  			block := f.Body().FirstMatchingBlock(test.typeName, test.labels)
   330  			if block == nil {
   331  				if test.want != "" {
   332  					t.Fatal("block not found, but want it to exist")
   333  				}
   334  			} else {
   335  				if test.want == "" {
   336  					t.Fatal("block found, but expecting not found")
   337  				}
   338  
   339  				got := string(block.BuildTokens(nil).Bytes())
   340  				if got != test.want {
   341  					t.Errorf("wrong result\ngot:  %s\nwant: %s", got, test.want)
   342  				}
   343  			}
   344  		})
   345  	}
   346  }
   347  
   348  func TestBodySetAttributeValue(t *testing.T) {
   349  	tests := []struct {
   350  		src  string
   351  		name string
   352  		val  cty.Value
   353  		want Tokens
   354  	}{
   355  		{
   356  			"",
   357  			"a",
   358  			cty.True,
   359  			Tokens{
   360  				{
   361  					Type:         hclsyntax.TokenIdent,
   362  					Bytes:        []byte{'a'},
   363  					SpacesBefore: 0,
   364  				},
   365  				{
   366  					Type:         hclsyntax.TokenEqual,
   367  					Bytes:        []byte{'='},
   368  					SpacesBefore: 1,
   369  				},
   370  				{
   371  					Type:         hclsyntax.TokenIdent,
   372  					Bytes:        []byte("true"),
   373  					SpacesBefore: 1,
   374  				},
   375  				{
   376  					Type:         hclsyntax.TokenNewline,
   377  					Bytes:        []byte{'\n'},
   378  					SpacesBefore: 0,
   379  				},
   380  				{
   381  					Type:         hclsyntax.TokenEOF,
   382  					Bytes:        []byte{},
   383  					SpacesBefore: 0,
   384  				},
   385  			},
   386  		},
   387  		{
   388  			"b = false\n",
   389  			"a",
   390  			cty.True,
   391  			Tokens{
   392  				{
   393  					Type:         hclsyntax.TokenIdent,
   394  					Bytes:        []byte{'b'},
   395  					SpacesBefore: 0,
   396  				},
   397  				{
   398  					Type:         hclsyntax.TokenEqual,
   399  					Bytes:        []byte{'='},
   400  					SpacesBefore: 1,
   401  				},
   402  				{
   403  					Type:         hclsyntax.TokenIdent,
   404  					Bytes:        []byte("false"),
   405  					SpacesBefore: 1,
   406  				},
   407  				{
   408  					Type:         hclsyntax.TokenNewline,
   409  					Bytes:        []byte{'\n'},
   410  					SpacesBefore: 0,
   411  				},
   412  				{
   413  					Type:         hclsyntax.TokenIdent,
   414  					Bytes:        []byte{'a'},
   415  					SpacesBefore: 0,
   416  				},
   417  				{
   418  					Type:         hclsyntax.TokenEqual,
   419  					Bytes:        []byte{'='},
   420  					SpacesBefore: 1,
   421  				},
   422  				{
   423  					Type:         hclsyntax.TokenIdent,
   424  					Bytes:        []byte("true"),
   425  					SpacesBefore: 1,
   426  				},
   427  				{
   428  					Type:         hclsyntax.TokenNewline,
   429  					Bytes:        []byte{'\n'},
   430  					SpacesBefore: 0,
   431  				},
   432  				{
   433  					Type:         hclsyntax.TokenEOF,
   434  					Bytes:        []byte{},
   435  					SpacesBefore: 0,
   436  				},
   437  			},
   438  		},
   439  		{
   440  			"a = false\n",
   441  			"a",
   442  			cty.True,
   443  			Tokens{
   444  				{
   445  					Type:         hclsyntax.TokenIdent,
   446  					Bytes:        []byte{'a'},
   447  					SpacesBefore: 0,
   448  				},
   449  				{
   450  					Type:         hclsyntax.TokenEqual,
   451  					Bytes:        []byte{'='},
   452  					SpacesBefore: 1,
   453  				},
   454  				{
   455  					Type:         hclsyntax.TokenIdent,
   456  					Bytes:        []byte("true"),
   457  					SpacesBefore: 1,
   458  				},
   459  				{
   460  					Type:         hclsyntax.TokenNewline,
   461  					Bytes:        []byte{'\n'},
   462  					SpacesBefore: 0,
   463  				},
   464  				{
   465  					Type:         hclsyntax.TokenEOF,
   466  					Bytes:        []byte{},
   467  					SpacesBefore: 0,
   468  				},
   469  			},
   470  		},
   471  		{
   472  			"a = 1\nb = false\n",
   473  			"a",
   474  			cty.True,
   475  			Tokens{
   476  				{
   477  					Type:         hclsyntax.TokenIdent,
   478  					Bytes:        []byte{'a'},
   479  					SpacesBefore: 0,
   480  				},
   481  				{
   482  					Type:         hclsyntax.TokenEqual,
   483  					Bytes:        []byte{'='},
   484  					SpacesBefore: 1,
   485  				},
   486  				{
   487  					Type:         hclsyntax.TokenIdent,
   488  					Bytes:        []byte("true"),
   489  					SpacesBefore: 1,
   490  				},
   491  				{
   492  					Type:         hclsyntax.TokenNewline,
   493  					Bytes:        []byte{'\n'},
   494  					SpacesBefore: 0,
   495  				},
   496  				{
   497  					Type:         hclsyntax.TokenIdent,
   498  					Bytes:        []byte{'b'},
   499  					SpacesBefore: 0,
   500  				},
   501  				{
   502  					Type:         hclsyntax.TokenEqual,
   503  					Bytes:        []byte{'='},
   504  					SpacesBefore: 1,
   505  				},
   506  				{
   507  					Type:         hclsyntax.TokenIdent,
   508  					Bytes:        []byte("false"),
   509  					SpacesBefore: 1,
   510  				},
   511  				{
   512  					Type:         hclsyntax.TokenNewline,
   513  					Bytes:        []byte{'\n'},
   514  					SpacesBefore: 0,
   515  				},
   516  				{
   517  					Type:         hclsyntax.TokenEOF,
   518  					Bytes:        []byte{},
   519  					SpacesBefore: 0,
   520  				},
   521  			},
   522  		},
   523  	}
   524  
   525  	for _, test := range tests {
   526  		t.Run(fmt.Sprintf("%s = %#v in %s", test.name, test.val, test.src), func(t *testing.T) {
   527  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   528  			if len(diags) != 0 {
   529  				for _, diag := range diags {
   530  					t.Logf("- %s", diag.Error())
   531  				}
   532  				t.Fatalf("unexpected diagnostics")
   533  			}
   534  
   535  			f.Body().SetAttributeValue(test.name, test.val)
   536  			got := f.BuildTokens(nil)
   537  			format(got)
   538  			if !reflect.DeepEqual(got, test.want) {
   539  				diff := cmp.Diff(test.want, got)
   540  				t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff)
   541  			}
   542  		})
   543  	}
   544  }
   545  
   546  func TestBodySetAttributeTraversal(t *testing.T) {
   547  	tests := []struct {
   548  		src  string
   549  		name string
   550  		trav string
   551  		want Tokens
   552  	}{
   553  		{
   554  			"",
   555  			"a",
   556  			`b`,
   557  			Tokens{
   558  				{
   559  					Type:         hclsyntax.TokenIdent,
   560  					Bytes:        []byte{'a'},
   561  					SpacesBefore: 0,
   562  				},
   563  				{
   564  					Type:         hclsyntax.TokenEqual,
   565  					Bytes:        []byte{'='},
   566  					SpacesBefore: 1,
   567  				},
   568  				{
   569  					Type:         hclsyntax.TokenIdent,
   570  					Bytes:        []byte("b"),
   571  					SpacesBefore: 1,
   572  				},
   573  				{
   574  					Type:         hclsyntax.TokenNewline,
   575  					Bytes:        []byte{'\n'},
   576  					SpacesBefore: 0,
   577  				},
   578  				{
   579  					Type:         hclsyntax.TokenEOF,
   580  					Bytes:        []byte{},
   581  					SpacesBefore: 0,
   582  				},
   583  			},
   584  		},
   585  		{
   586  			"",
   587  			"a",
   588  			`b.c.d`,
   589  			Tokens{
   590  				{
   591  					Type:         hclsyntax.TokenIdent,
   592  					Bytes:        []byte{'a'},
   593  					SpacesBefore: 0,
   594  				},
   595  				{
   596  					Type:         hclsyntax.TokenEqual,
   597  					Bytes:        []byte{'='},
   598  					SpacesBefore: 1,
   599  				},
   600  				{
   601  					Type:         hclsyntax.TokenIdent,
   602  					Bytes:        []byte("b"),
   603  					SpacesBefore: 1,
   604  				},
   605  				{
   606  					Type:         hclsyntax.TokenDot,
   607  					Bytes:        []byte("."),
   608  					SpacesBefore: 0,
   609  				},
   610  				{
   611  					Type:         hclsyntax.TokenIdent,
   612  					Bytes:        []byte("c"),
   613  					SpacesBefore: 0,
   614  				},
   615  				{
   616  					Type:         hclsyntax.TokenDot,
   617  					Bytes:        []byte("."),
   618  					SpacesBefore: 0,
   619  				},
   620  				{
   621  					Type:         hclsyntax.TokenIdent,
   622  					Bytes:        []byte("d"),
   623  					SpacesBefore: 0,
   624  				},
   625  				{
   626  					Type:         hclsyntax.TokenNewline,
   627  					Bytes:        []byte{'\n'},
   628  					SpacesBefore: 0,
   629  				},
   630  				{
   631  					Type:         hclsyntax.TokenEOF,
   632  					Bytes:        []byte{},
   633  					SpacesBefore: 0,
   634  				},
   635  			},
   636  		},
   637  		{
   638  			"",
   639  			"a",
   640  			`b[0]`,
   641  			Tokens{
   642  				{
   643  					Type:         hclsyntax.TokenIdent,
   644  					Bytes:        []byte{'a'},
   645  					SpacesBefore: 0,
   646  				},
   647  				{
   648  					Type:         hclsyntax.TokenEqual,
   649  					Bytes:        []byte{'='},
   650  					SpacesBefore: 1,
   651  				},
   652  				{
   653  					Type:         hclsyntax.TokenIdent,
   654  					Bytes:        []byte("b"),
   655  					SpacesBefore: 1,
   656  				},
   657  				{
   658  					Type:         hclsyntax.TokenOBrack,
   659  					Bytes:        []byte("["),
   660  					SpacesBefore: 0,
   661  				},
   662  				{
   663  					Type:         hclsyntax.TokenNumberLit,
   664  					Bytes:        []byte("0"),
   665  					SpacesBefore: 0,
   666  				},
   667  				{
   668  					Type:         hclsyntax.TokenCBrack,
   669  					Bytes:        []byte("]"),
   670  					SpacesBefore: 0,
   671  				},
   672  				{
   673  					Type:         hclsyntax.TokenNewline,
   674  					Bytes:        []byte{'\n'},
   675  					SpacesBefore: 0,
   676  				},
   677  				{
   678  					Type:         hclsyntax.TokenEOF,
   679  					Bytes:        []byte{},
   680  					SpacesBefore: 0,
   681  				},
   682  			},
   683  		},
   684  		{
   685  			"",
   686  			"a",
   687  			`b[0].c`,
   688  			Tokens{
   689  				{
   690  					Type:         hclsyntax.TokenIdent,
   691  					Bytes:        []byte{'a'},
   692  					SpacesBefore: 0,
   693  				},
   694  				{
   695  					Type:         hclsyntax.TokenEqual,
   696  					Bytes:        []byte{'='},
   697  					SpacesBefore: 1,
   698  				},
   699  				{
   700  					Type:         hclsyntax.TokenIdent,
   701  					Bytes:        []byte("b"),
   702  					SpacesBefore: 1,
   703  				},
   704  				{
   705  					Type:         hclsyntax.TokenOBrack,
   706  					Bytes:        []byte("["),
   707  					SpacesBefore: 0,
   708  				},
   709  				{
   710  					Type:         hclsyntax.TokenNumberLit,
   711  					Bytes:        []byte("0"),
   712  					SpacesBefore: 0,
   713  				},
   714  				{
   715  					Type:         hclsyntax.TokenCBrack,
   716  					Bytes:        []byte("]"),
   717  					SpacesBefore: 0,
   718  				},
   719  				{
   720  					Type:         hclsyntax.TokenDot,
   721  					Bytes:        []byte("."),
   722  					SpacesBefore: 0,
   723  				},
   724  				{
   725  					Type:         hclsyntax.TokenIdent,
   726  					Bytes:        []byte("c"),
   727  					SpacesBefore: 0,
   728  				},
   729  				{
   730  					Type:         hclsyntax.TokenNewline,
   731  					Bytes:        []byte{'\n'},
   732  					SpacesBefore: 0,
   733  				},
   734  				{
   735  					Type:         hclsyntax.TokenEOF,
   736  					Bytes:        []byte{},
   737  					SpacesBefore: 0,
   738  				},
   739  			},
   740  		},
   741  	}
   742  
   743  	for _, test := range tests {
   744  		t.Run(fmt.Sprintf("%s = %s in %s", test.name, test.trav, test.src), func(t *testing.T) {
   745  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   746  			if len(diags) != 0 {
   747  				for _, diag := range diags {
   748  					t.Logf("- %s", diag.Error())
   749  				}
   750  				t.Fatalf("unexpected diagnostics")
   751  			}
   752  
   753  			traversal, diags := hclsyntax.ParseTraversalAbs([]byte(test.trav), "", hcl.Pos{Line: 1, Column: 1})
   754  			if len(diags) != 0 {
   755  				for _, diag := range diags {
   756  					t.Logf("- %s", diag.Error())
   757  				}
   758  				t.Fatalf("unexpected diagnostics from traversal")
   759  			}
   760  
   761  			f.Body().SetAttributeTraversal(test.name, traversal)
   762  			got := f.BuildTokens(nil)
   763  			format(got)
   764  			if !reflect.DeepEqual(got, test.want) {
   765  				diff := cmp.Diff(test.want, got)
   766  				t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff)
   767  			}
   768  		})
   769  	}
   770  }
   771  
   772  func TestBodySetAttributeRaw(t *testing.T) {
   773  	tests := []struct {
   774  		src    string
   775  		name   string
   776  		tokens Tokens
   777  		want   Tokens
   778  	}{
   779  		{
   780  			"",
   781  			"a",
   782  			Tokens{
   783  				{
   784  					Type:         hclsyntax.TokenIdent,
   785  					Bytes:        []byte(`true`),
   786  					SpacesBefore: 0,
   787  				},
   788  			},
   789  			Tokens{
   790  				{
   791  					Type:         hclsyntax.TokenIdent,
   792  					Bytes:        []byte{'a'},
   793  					SpacesBefore: 0,
   794  				},
   795  				{
   796  					Type:         hclsyntax.TokenEqual,
   797  					Bytes:        []byte{'='},
   798  					SpacesBefore: 1,
   799  				},
   800  				{
   801  					Type:         hclsyntax.TokenIdent,
   802  					Bytes:        []byte("true"),
   803  					SpacesBefore: 1,
   804  				},
   805  				{
   806  					Type:         hclsyntax.TokenNewline,
   807  					Bytes:        []byte{'\n'},
   808  					SpacesBefore: 0,
   809  				},
   810  				{
   811  					Type:         hclsyntax.TokenEOF,
   812  					Bytes:        []byte{},
   813  					SpacesBefore: 0,
   814  				},
   815  			},
   816  		},
   817  		{
   818  			"a = 23\n",
   819  			"a",
   820  			Tokens{
   821  				{
   822  					Type:         hclsyntax.TokenIdent,
   823  					Bytes:        []byte(`true`),
   824  					SpacesBefore: 0,
   825  				},
   826  			},
   827  			Tokens{
   828  				{
   829  					Type:         hclsyntax.TokenIdent,
   830  					Bytes:        []byte{'a'},
   831  					SpacesBefore: 0,
   832  				},
   833  				{
   834  					Type:         hclsyntax.TokenEqual,
   835  					Bytes:        []byte{'='},
   836  					SpacesBefore: 1,
   837  				},
   838  				{
   839  					Type:         hclsyntax.TokenIdent,
   840  					Bytes:        []byte("true"),
   841  					SpacesBefore: 1,
   842  				},
   843  				{
   844  					Type:         hclsyntax.TokenNewline,
   845  					Bytes:        []byte{'\n'},
   846  					SpacesBefore: 0,
   847  				},
   848  				{
   849  					Type:         hclsyntax.TokenEOF,
   850  					Bytes:        []byte{},
   851  					SpacesBefore: 0,
   852  				},
   853  			},
   854  		},
   855  		{
   856  			"b = 23\n",
   857  			"a",
   858  			Tokens{
   859  				{
   860  					Type:         hclsyntax.TokenIdent,
   861  					Bytes:        []byte(`true`),
   862  					SpacesBefore: 0,
   863  				},
   864  			},
   865  			Tokens{
   866  				{
   867  					Type:         hclsyntax.TokenIdent,
   868  					Bytes:        []byte{'b'},
   869  					SpacesBefore: 0,
   870  				},
   871  				{
   872  					Type:         hclsyntax.TokenEqual,
   873  					Bytes:        []byte{'='},
   874  					SpacesBefore: 1,
   875  				},
   876  				{
   877  					Type:         hclsyntax.TokenNumberLit,
   878  					Bytes:        []byte("23"),
   879  					SpacesBefore: 1,
   880  				},
   881  				{
   882  					Type:         hclsyntax.TokenNewline,
   883  					Bytes:        []byte{'\n'},
   884  					SpacesBefore: 0,
   885  				},
   886  				{
   887  					Type:         hclsyntax.TokenIdent,
   888  					Bytes:        []byte{'a'},
   889  					SpacesBefore: 0,
   890  				},
   891  				{
   892  					Type:         hclsyntax.TokenEqual,
   893  					Bytes:        []byte{'='},
   894  					SpacesBefore: 1,
   895  				},
   896  				{
   897  					Type:         hclsyntax.TokenIdent,
   898  					Bytes:        []byte("true"),
   899  					SpacesBefore: 1,
   900  				},
   901  				{
   902  					Type:         hclsyntax.TokenNewline,
   903  					Bytes:        []byte{'\n'},
   904  					SpacesBefore: 0,
   905  				},
   906  				{
   907  					Type:         hclsyntax.TokenEOF,
   908  					Bytes:        []byte{},
   909  					SpacesBefore: 0,
   910  				},
   911  			},
   912  		},
   913  	}
   914  
   915  	for _, test := range tests {
   916  		t.Run(fmt.Sprintf("%s = %s in %s", test.name, test.tokens.Bytes(), test.src), func(t *testing.T) {
   917  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   918  			if len(diags) != 0 {
   919  				for _, diag := range diags {
   920  					t.Logf("- %s", diag.Error())
   921  				}
   922  				t.Fatalf("unexpected diagnostics")
   923  			}
   924  
   925  			f.Body().SetAttributeRaw(test.name, test.tokens)
   926  			got := f.BuildTokens(nil)
   927  			format(got)
   928  			if !reflect.DeepEqual(got, test.want) {
   929  				diff := cmp.Diff(test.want, got)
   930  				t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff)
   931  			}
   932  		})
   933  	}
   934  }
   935  
   936  func TestBodySetAttributeValueInBlock(t *testing.T) {
   937  	src := `service "label1" {
   938    attr1 = "val1"
   939  }
   940  `
   941  	tests := []struct {
   942  		src      string
   943  		typeName string
   944  		labels   []string
   945  		attr     string
   946  		val      cty.Value
   947  		want     string
   948  	}{
   949  		{
   950  			src,
   951  			"service",
   952  			[]string{"label1"},
   953  			"attr1",
   954  			cty.StringVal("updated1"),
   955  			`service "label1" {
   956    attr1 = "updated1"
   957  }
   958  `,
   959  		},
   960  	}
   961  
   962  	for _, test := range tests {
   963  		t.Run(fmt.Sprintf("%s = %#v in %s %s", test.attr, test.val, test.typeName, strings.Join(test.labels, " ")), func(t *testing.T) {
   964  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
   965  			if len(diags) != 0 {
   966  				for _, diag := range diags {
   967  					t.Logf("- %s", diag.Error())
   968  				}
   969  				t.Fatalf("unexpected diagnostics")
   970  			}
   971  
   972  			b := f.Body().FirstMatchingBlock(test.typeName, test.labels)
   973  			b.Body().SetAttributeValue(test.attr, test.val)
   974  			tokens := f.BuildTokens(nil)
   975  			format(tokens)
   976  			got := string(tokens.Bytes())
   977  			if got != test.want {
   978  				t.Errorf("wrong result\ngot:  %s\nwant: %s\n", got, test.want)
   979  			}
   980  		})
   981  	}
   982  }
   983  
   984  func TestBodySetAttributeValueInNestedBlock(t *testing.T) {
   985  	src := `parent {
   986    attr1 = "val1"
   987    child {
   988      attr2 = "val2"
   989    }
   990  }
   991  `
   992  	tests := []struct {
   993  		src            string
   994  		parentTypeName string
   995  		childTypeName  string
   996  		attr           string
   997  		val            cty.Value
   998  		want           string
   999  	}{
  1000  		{
  1001  			src,
  1002  			"parent",
  1003  			"child",
  1004  			"attr2",
  1005  			cty.StringVal("updated2"),
  1006  			`parent {
  1007    attr1 = "val1"
  1008    child {
  1009      attr2 = "updated2"
  1010    }
  1011  }
  1012  `,
  1013  		},
  1014  	}
  1015  
  1016  	for _, test := range tests {
  1017  		t.Run(fmt.Sprintf("%s = %#v in %s in %s", test.attr, test.val, test.childTypeName, test.parentTypeName), func(t *testing.T) {
  1018  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
  1019  			if len(diags) != 0 {
  1020  				for _, diag := range diags {
  1021  					t.Logf("- %s", diag.Error())
  1022  				}
  1023  				t.Fatalf("unexpected diagnostics")
  1024  			}
  1025  
  1026  			parent := f.Body().FirstMatchingBlock(test.parentTypeName, []string{})
  1027  			child := parent.Body().FirstMatchingBlock(test.childTypeName, []string{})
  1028  			child.Body().SetAttributeValue(test.attr, test.val)
  1029  			tokens := f.BuildTokens(nil)
  1030  			format(tokens)
  1031  			got := string(tokens.Bytes())
  1032  			if got != test.want {
  1033  				t.Errorf("wrong result\ngot:  %s\nwant: %s\n", got, test.want)
  1034  			}
  1035  		})
  1036  	}
  1037  }
  1038  
  1039  func TestBodyRemoveAttribute(t *testing.T) {
  1040  	tests := []struct {
  1041  		src  string
  1042  		name string
  1043  		want Tokens
  1044  	}{
  1045  		{
  1046  			"",
  1047  			"a",
  1048  			Tokens{
  1049  				{
  1050  					Type:         hclsyntax.TokenEOF,
  1051  					Bytes:        []byte{},
  1052  					SpacesBefore: 0,
  1053  				},
  1054  			},
  1055  		},
  1056  		{
  1057  			"b = false\n",
  1058  			"a",
  1059  			Tokens{
  1060  				{
  1061  					Type:         hclsyntax.TokenIdent,
  1062  					Bytes:        []byte{'b'},
  1063  					SpacesBefore: 0,
  1064  				},
  1065  				{
  1066  					Type:         hclsyntax.TokenEqual,
  1067  					Bytes:        []byte{'='},
  1068  					SpacesBefore: 1,
  1069  				},
  1070  				{
  1071  					Type:         hclsyntax.TokenIdent,
  1072  					Bytes:        []byte("false"),
  1073  					SpacesBefore: 1,
  1074  				},
  1075  				{
  1076  					Type:         hclsyntax.TokenNewline,
  1077  					Bytes:        []byte{'\n'},
  1078  					SpacesBefore: 0,
  1079  				},
  1080  				{
  1081  					Type:         hclsyntax.TokenEOF,
  1082  					Bytes:        []byte{},
  1083  					SpacesBefore: 0,
  1084  				},
  1085  			},
  1086  		},
  1087  		{
  1088  			"a = false\n",
  1089  			"a",
  1090  			Tokens{
  1091  				{
  1092  					Type:         hclsyntax.TokenEOF,
  1093  					Bytes:        []byte{},
  1094  					SpacesBefore: 0,
  1095  				},
  1096  			},
  1097  		},
  1098  		{
  1099  			"a = 1\nb = false\n",
  1100  			"a",
  1101  			Tokens{
  1102  				{
  1103  					Type:         hclsyntax.TokenIdent,
  1104  					Bytes:        []byte{'b'},
  1105  					SpacesBefore: 0,
  1106  				},
  1107  				{
  1108  					Type:         hclsyntax.TokenEqual,
  1109  					Bytes:        []byte{'='},
  1110  					SpacesBefore: 1,
  1111  				},
  1112  				{
  1113  					Type:         hclsyntax.TokenIdent,
  1114  					Bytes:        []byte("false"),
  1115  					SpacesBefore: 1,
  1116  				},
  1117  				{
  1118  					Type:         hclsyntax.TokenNewline,
  1119  					Bytes:        []byte{'\n'},
  1120  					SpacesBefore: 0,
  1121  				},
  1122  				{
  1123  					Type:         hclsyntax.TokenEOF,
  1124  					Bytes:        []byte{},
  1125  					SpacesBefore: 0,
  1126  				},
  1127  			},
  1128  		},
  1129  	}
  1130  
  1131  	for _, test := range tests {
  1132  		t.Run(fmt.Sprintf("%s in %s", test.name, test.src), func(t *testing.T) {
  1133  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
  1134  			if len(diags) != 0 {
  1135  				for _, diag := range diags {
  1136  					t.Logf("- %s", diag.Error())
  1137  				}
  1138  				t.Fatalf("unexpected diagnostics")
  1139  			}
  1140  
  1141  			f.Body().RemoveAttribute(test.name)
  1142  			got := f.BuildTokens(nil)
  1143  			format(got)
  1144  			if !reflect.DeepEqual(got, test.want) {
  1145  				diff := cmp.Diff(test.want, got)
  1146  				t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff)
  1147  			}
  1148  		})
  1149  	}
  1150  }
  1151  
  1152  func TestBodyAppendBlock(t *testing.T) {
  1153  	tests := []struct {
  1154  		src       string
  1155  		blockType string
  1156  		labels    []string
  1157  		want      Tokens
  1158  	}{
  1159  		{
  1160  			"",
  1161  			"foo",
  1162  			nil,
  1163  			Tokens{
  1164  				{
  1165  					Type:         hclsyntax.TokenIdent,
  1166  					Bytes:        []byte(`foo`),
  1167  					SpacesBefore: 0,
  1168  				},
  1169  				{
  1170  					Type:         hclsyntax.TokenOBrace,
  1171  					Bytes:        []byte{'{'},
  1172  					SpacesBefore: 1,
  1173  				},
  1174  				{
  1175  					Type:         hclsyntax.TokenNewline,
  1176  					Bytes:        []byte{'\n'},
  1177  					SpacesBefore: 0,
  1178  				},
  1179  				{
  1180  					Type:         hclsyntax.TokenCBrace,
  1181  					Bytes:        []byte{'}'},
  1182  					SpacesBefore: 0,
  1183  				},
  1184  				{
  1185  					Type:         hclsyntax.TokenNewline,
  1186  					Bytes:        []byte{'\n'},
  1187  					SpacesBefore: 0,
  1188  				},
  1189  				{
  1190  					Type:         hclsyntax.TokenEOF,
  1191  					Bytes:        []byte{},
  1192  					SpacesBefore: 0,
  1193  				},
  1194  			},
  1195  		},
  1196  		{
  1197  			"",
  1198  			"foo",
  1199  			[]string{"bar"},
  1200  			Tokens{
  1201  				{
  1202  					Type:         hclsyntax.TokenIdent,
  1203  					Bytes:        []byte(`foo`),
  1204  					SpacesBefore: 0,
  1205  				},
  1206  				{
  1207  					Type:         hclsyntax.TokenOQuote,
  1208  					Bytes:        []byte(`"`),
  1209  					SpacesBefore: 1,
  1210  				},
  1211  				{
  1212  					Type:         hclsyntax.TokenQuotedLit,
  1213  					Bytes:        []byte(`bar`),
  1214  					SpacesBefore: 0,
  1215  				},
  1216  				{
  1217  					Type:         hclsyntax.TokenCQuote,
  1218  					Bytes:        []byte(`"`),
  1219  					SpacesBefore: 0,
  1220  				},
  1221  				{
  1222  					Type:         hclsyntax.TokenOBrace,
  1223  					Bytes:        []byte{'{'},
  1224  					SpacesBefore: 1,
  1225  				},
  1226  				{
  1227  					Type:         hclsyntax.TokenNewline,
  1228  					Bytes:        []byte{'\n'},
  1229  					SpacesBefore: 0,
  1230  				},
  1231  				{
  1232  					Type:         hclsyntax.TokenCBrace,
  1233  					Bytes:        []byte{'}'},
  1234  					SpacesBefore: 0,
  1235  				},
  1236  				{
  1237  					Type:         hclsyntax.TokenNewline,
  1238  					Bytes:        []byte{'\n'},
  1239  					SpacesBefore: 0,
  1240  				},
  1241  				{
  1242  					Type:         hclsyntax.TokenEOF,
  1243  					Bytes:        []byte{},
  1244  					SpacesBefore: 0,
  1245  				},
  1246  			},
  1247  		},
  1248  		{
  1249  			"",
  1250  			"foo",
  1251  			[]string{"bar", "baz"},
  1252  			Tokens{
  1253  				{
  1254  					Type:         hclsyntax.TokenIdent,
  1255  					Bytes:        []byte(`foo`),
  1256  					SpacesBefore: 0,
  1257  				},
  1258  				{
  1259  					Type:         hclsyntax.TokenOQuote,
  1260  					Bytes:        []byte(`"`),
  1261  					SpacesBefore: 1,
  1262  				},
  1263  				{
  1264  					Type:         hclsyntax.TokenQuotedLit,
  1265  					Bytes:        []byte(`bar`),
  1266  					SpacesBefore: 0,
  1267  				},
  1268  				{
  1269  					Type:         hclsyntax.TokenCQuote,
  1270  					Bytes:        []byte(`"`),
  1271  					SpacesBefore: 0,
  1272  				},
  1273  				{
  1274  					Type:         hclsyntax.TokenOQuote,
  1275  					Bytes:        []byte(`"`),
  1276  					SpacesBefore: 1,
  1277  				},
  1278  				{
  1279  					Type:         hclsyntax.TokenQuotedLit,
  1280  					Bytes:        []byte(`baz`),
  1281  					SpacesBefore: 0,
  1282  				},
  1283  				{
  1284  					Type:         hclsyntax.TokenCQuote,
  1285  					Bytes:        []byte(`"`),
  1286  					SpacesBefore: 0,
  1287  				},
  1288  				{
  1289  					Type:         hclsyntax.TokenOBrace,
  1290  					Bytes:        []byte{'{'},
  1291  					SpacesBefore: 1,
  1292  				},
  1293  				{
  1294  					Type:         hclsyntax.TokenNewline,
  1295  					Bytes:        []byte{'\n'},
  1296  					SpacesBefore: 0,
  1297  				},
  1298  				{
  1299  					Type:         hclsyntax.TokenCBrace,
  1300  					Bytes:        []byte{'}'},
  1301  					SpacesBefore: 0,
  1302  				},
  1303  				{
  1304  					Type:         hclsyntax.TokenNewline,
  1305  					Bytes:        []byte{'\n'},
  1306  					SpacesBefore: 0,
  1307  				},
  1308  				{
  1309  					Type:         hclsyntax.TokenEOF,
  1310  					Bytes:        []byte{},
  1311  					SpacesBefore: 0,
  1312  				},
  1313  			},
  1314  		},
  1315  		{
  1316  			"bar {}\n",
  1317  			"foo",
  1318  			nil,
  1319  			Tokens{
  1320  				{
  1321  					Type:         hclsyntax.TokenIdent,
  1322  					Bytes:        []byte(`bar`),
  1323  					SpacesBefore: 0,
  1324  				},
  1325  				{
  1326  					Type:         hclsyntax.TokenOBrace,
  1327  					Bytes:        []byte{'{'},
  1328  					SpacesBefore: 1,
  1329  				},
  1330  				{
  1331  					Type:         hclsyntax.TokenCBrace,
  1332  					Bytes:        []byte{'}'},
  1333  					SpacesBefore: 0,
  1334  				},
  1335  				{
  1336  					Type:         hclsyntax.TokenNewline,
  1337  					Bytes:        []byte{'\n'},
  1338  					SpacesBefore: 0,
  1339  				},
  1340  				{
  1341  					Type:         hclsyntax.TokenIdent,
  1342  					Bytes:        []byte(`foo`),
  1343  					SpacesBefore: 0,
  1344  				},
  1345  				{
  1346  					Type:         hclsyntax.TokenOBrace,
  1347  					Bytes:        []byte{'{'},
  1348  					SpacesBefore: 1,
  1349  				},
  1350  				{
  1351  					Type:         hclsyntax.TokenNewline,
  1352  					Bytes:        []byte{'\n'},
  1353  					SpacesBefore: 0,
  1354  				},
  1355  				{
  1356  					Type:         hclsyntax.TokenCBrace,
  1357  					Bytes:        []byte{'}'},
  1358  					SpacesBefore: 0,
  1359  				},
  1360  				{
  1361  					Type:         hclsyntax.TokenNewline,
  1362  					Bytes:        []byte{'\n'},
  1363  					SpacesBefore: 0,
  1364  				},
  1365  				{
  1366  					Type:         hclsyntax.TokenEOF,
  1367  					Bytes:        []byte{},
  1368  					SpacesBefore: 0,
  1369  				},
  1370  			},
  1371  		},
  1372  	}
  1373  
  1374  	for _, test := range tests {
  1375  		t.Run(fmt.Sprintf("%s %#v in %s", test.blockType, test.blockType, test.src), func(t *testing.T) {
  1376  			f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
  1377  			if len(diags) != 0 {
  1378  				for _, diag := range diags {
  1379  					t.Logf("- %s", diag.Error())
  1380  				}
  1381  				t.Fatalf("unexpected diagnostics")
  1382  			}
  1383  
  1384  			f.Body().AppendNewBlock(test.blockType, test.labels)
  1385  			got := f.BuildTokens(nil)
  1386  			format(got)
  1387  			if !reflect.DeepEqual(got, test.want) {
  1388  				diff := cmp.Diff(test.want, got)
  1389  				t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff)
  1390  			}
  1391  		})
  1392  	}
  1393  }
  1394  
  1395  func TestBodyRemoveBlock(t *testing.T) {
  1396  	src := strings.TrimSpace(`
  1397  a = 1
  1398  
  1399  # Foo
  1400  foo {
  1401    b = 1
  1402  }
  1403  foo {
  1404    b = 2
  1405  }
  1406  bar {}
  1407  `)
  1408  	f, diags := ParseConfig([]byte(src), "", hcl.Pos{Line: 1, Column: 1})
  1409  	if len(diags) != 0 {
  1410  		for _, diag := range diags {
  1411  			t.Logf("- %s", diag.Error())
  1412  		}
  1413  		t.Fatalf("unexpected diagnostics")
  1414  	}
  1415  
  1416  	t.Logf("Removing the first block")
  1417  	t.Logf("initial content:\n%s", f.Bytes())
  1418  	body := f.Body()
  1419  	block := body.FirstMatchingBlock("foo", nil)
  1420  	if block == nil {
  1421  		t.Fatalf("didn't find a 'foo' block")
  1422  	}
  1423  	removed := body.RemoveBlock(block)
  1424  	if !removed {
  1425  		t.Fatalf("didn't remove first block")
  1426  	}
  1427  	t.Logf("updated content:\n%s", f.Bytes())
  1428  	got := f.BuildTokens(nil)
  1429  	want := Tokens{
  1430  		0: {
  1431  			Type:         hclsyntax.TokenIdent,
  1432  			Bytes:        []byte(`a`),
  1433  			SpacesBefore: 0,
  1434  		},
  1435  		1: {
  1436  			Type:         hclsyntax.TokenEqual,
  1437  			Bytes:        []byte(`=`),
  1438  			SpacesBefore: 1,
  1439  		},
  1440  		2: {
  1441  			Type:         hclsyntax.TokenNumberLit,
  1442  			Bytes:        []byte(`1`),
  1443  			SpacesBefore: 1,
  1444  		},
  1445  		3: {
  1446  			Type:         hclsyntax.TokenNewline,
  1447  			Bytes:        []byte("\n"),
  1448  			SpacesBefore: 0,
  1449  		},
  1450  		4: {
  1451  			Type:         hclsyntax.TokenNewline,
  1452  			Bytes:        []byte("\n"),
  1453  			SpacesBefore: 0,
  1454  		},
  1455  		5: {
  1456  			Type:         hclsyntax.TokenIdent,
  1457  			Bytes:        []byte(`foo`),
  1458  			SpacesBefore: 0,
  1459  		},
  1460  		6: {
  1461  			Type:         hclsyntax.TokenOBrace,
  1462  			Bytes:        []byte(`{`),
  1463  			SpacesBefore: 1,
  1464  		},
  1465  		7: {
  1466  			Type:         hclsyntax.TokenNewline,
  1467  			Bytes:        []byte("\n"),
  1468  			SpacesBefore: 0,
  1469  		},
  1470  		8: {
  1471  			Type:         hclsyntax.TokenIdent,
  1472  			Bytes:        []byte(`b`),
  1473  			SpacesBefore: 2,
  1474  		},
  1475  		9: {
  1476  			Type:         hclsyntax.TokenEqual,
  1477  			Bytes:        []byte(`=`),
  1478  			SpacesBefore: 1,
  1479  		},
  1480  		10: {
  1481  			Type:         hclsyntax.TokenNumberLit,
  1482  			Bytes:        []byte(`2`),
  1483  			SpacesBefore: 1,
  1484  		},
  1485  		11: {
  1486  			Type:         hclsyntax.TokenNewline,
  1487  			Bytes:        []byte("\n"),
  1488  			SpacesBefore: 0,
  1489  		},
  1490  		12: {
  1491  			Type:         hclsyntax.TokenCBrace,
  1492  			Bytes:        []byte(`}`),
  1493  			SpacesBefore: 0,
  1494  		},
  1495  		13: {
  1496  			Type:         hclsyntax.TokenNewline,
  1497  			Bytes:        []byte("\n"),
  1498  			SpacesBefore: 0,
  1499  		},
  1500  		14: {
  1501  			Type:         hclsyntax.TokenIdent,
  1502  			Bytes:        []byte(`bar`),
  1503  			SpacesBefore: 0,
  1504  		},
  1505  		15: {
  1506  			Type:         hclsyntax.TokenOBrace,
  1507  			Bytes:        []byte(`{`),
  1508  			SpacesBefore: 1,
  1509  		},
  1510  		16: {
  1511  			Type:         hclsyntax.TokenCBrace,
  1512  			Bytes:        []byte(`}`),
  1513  			SpacesBefore: 0,
  1514  		},
  1515  		17: {
  1516  			Type:         hclsyntax.TokenEOF,
  1517  			Bytes:        []byte(""),
  1518  			SpacesBefore: 0,
  1519  		},
  1520  	}
  1521  	format(got)
  1522  	if !reflect.DeepEqual(got, want) {
  1523  		diff := cmp.Diff(want, got)
  1524  		t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(want), diff)
  1525  	}
  1526  
  1527  	t.Logf("removing the second block")
  1528  	t.Logf("initial content:\n%s", f.Bytes())
  1529  	block = body.FirstMatchingBlock("foo", nil)
  1530  	if block == nil {
  1531  		t.Fatalf("didn't find a 'foo' block")
  1532  	}
  1533  	removed = body.RemoveBlock(block)
  1534  	if !removed {
  1535  		t.Fatalf("didn't remove second block")
  1536  	}
  1537  	t.Logf("updated content:\n%s", f.Bytes())
  1538  	got = f.BuildTokens(nil)
  1539  	want = Tokens{
  1540  		0: {
  1541  			Type:         hclsyntax.TokenIdent,
  1542  			Bytes:        []byte(`a`),
  1543  			SpacesBefore: 0,
  1544  		},
  1545  		1: {
  1546  			Type:         hclsyntax.TokenEqual,
  1547  			Bytes:        []byte(`=`),
  1548  			SpacesBefore: 1,
  1549  		},
  1550  		2: {
  1551  			Type:         hclsyntax.TokenNumberLit,
  1552  			Bytes:        []byte(`1`),
  1553  			SpacesBefore: 1,
  1554  		},
  1555  		3: {
  1556  			Type:         hclsyntax.TokenNewline,
  1557  			Bytes:        []byte("\n"),
  1558  			SpacesBefore: 0,
  1559  		},
  1560  		4: {
  1561  			Type:         hclsyntax.TokenNewline,
  1562  			Bytes:        []byte("\n"),
  1563  			SpacesBefore: 0,
  1564  		},
  1565  		5: {
  1566  			Type:         hclsyntax.TokenIdent,
  1567  			Bytes:        []byte(`bar`),
  1568  			SpacesBefore: 0,
  1569  		},
  1570  		6: {
  1571  			Type:         hclsyntax.TokenOBrace,
  1572  			Bytes:        []byte(`{`),
  1573  			SpacesBefore: 1,
  1574  		},
  1575  		7: {
  1576  			Type:         hclsyntax.TokenCBrace,
  1577  			Bytes:        []byte(`}`),
  1578  			SpacesBefore: 0,
  1579  		},
  1580  		8: {
  1581  			Type:         hclsyntax.TokenEOF,
  1582  			Bytes:        []byte(""),
  1583  			SpacesBefore: 0,
  1584  		},
  1585  	}
  1586  	format(got)
  1587  	if !reflect.DeepEqual(got, want) {
  1588  		diff := cmp.Diff(want, got)
  1589  		t.Errorf("wrong result\ngot:  %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(want), diff)
  1590  	}
  1591  
  1592  }