github.com/cheshirekow/buildtools@v0.0.0-20200224190056-5d637702fe81/edit/edit_test.go (about)

     1  /*
     2  Copyright 2016 Google Inc. All Rights Reserved.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10   See the License for the specific language governing permissions and
    11   limitations under the License.
    12  */
    13  package edit
    14  
    15  import (
    16  	"fmt"
    17  	"io/ioutil"
    18  	"os"
    19  	"path/filepath"
    20  	"reflect"
    21  	"regexp"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/cheshirekow/buildtools/build"
    26  )
    27  
    28  var parseLabelTests = []struct {
    29  	in   string
    30  	repo string
    31  	pkg  string
    32  	rule string
    33  }{
    34  	{"//devtools/buildozer:rule", "", "devtools/buildozer", "rule"},
    35  	{"devtools/buildozer:rule", "", "devtools/buildozer", "rule"},
    36  	{"//devtools/buildozer", "", "devtools/buildozer", "buildozer"},
    37  	{"//base", "", "base", "base"},
    38  	{"//base:", "", "base", "base"},
    39  	{"@r//devtools/buildozer:rule", "r", "devtools/buildozer", "rule"},
    40  	{"@r//devtools/buildozer", "r", "devtools/buildozer", "buildozer"},
    41  	{"@r//base", "r", "base", "base"},
    42  	{"@r//base:", "r", "base", "base"},
    43  	{"@foo", "foo", "", "foo"},
    44  	{":label", "", "", "label"},
    45  	{"label", "", "", "label"},
    46  	{"/abs/path/to/WORKSPACE:rule", "", "/abs/path/to/WORKSPACE", "rule"},
    47  }
    48  
    49  func TestParseLabel(t *testing.T) {
    50  	for i, tt := range parseLabelTests {
    51  		repo, pkg, rule := ParseLabel(tt.in)
    52  		if repo != tt.repo || pkg != tt.pkg || rule != tt.rule {
    53  			t.Errorf("%d. ParseLabel(%q) => (%q, %q, %q), want (%q, %q, %q)",
    54  				i, tt.in, repo, pkg, rule, tt.repo, tt.pkg, tt.rule)
    55  		}
    56  	}
    57  }
    58  
    59  var shortenLabelTests = []struct {
    60  	in     string
    61  	pkg    string
    62  	result string
    63  }{
    64  	{"//devtools/buildozer:rule", "devtools/buildozer", ":rule"},
    65  	{"@//devtools/buildozer:rule", "devtools/buildozer", ":rule"},
    66  	{"//devtools/buildozer:rule", "devtools", "//devtools/buildozer:rule"},
    67  	{"//base:rule", "devtools", "//base:rule"},
    68  	{"//base:base", "devtools", "//base"},
    69  	{"//base", "base", ":base"},
    70  	{"//devtools/buildozer:buildozer", "", "//devtools/buildozer"},
    71  	{"@r//devtools/buildozer:buildozer", "devtools/buildozer", "@r//devtools/buildozer"},
    72  	{"@r//devtools/buildozer", "devtools/buildozer", "@r//devtools/buildozer"},
    73  	{"@r//devtools", "devtools", "@r//devtools"},
    74  	{"@r:rule", "", "@r:rule"},
    75  	{"@r", "", "@r"},
    76  	{"@foo//:foo", "", "@foo"},
    77  	{"@foo//devtools:foo", "", "@foo//devtools:foo"},
    78  	{"@foo//devtools:foo", "devtools", "@foo//devtools:foo"},
    79  	{"@foo//foo:foo", "", "@foo//foo"},
    80  	{":local", "", ":local"},
    81  	{"something else", "", "something else"},
    82  	{"/path/to/file", "path/to", "/path/to/file"},
    83  }
    84  
    85  func TestShortenLabel(t *testing.T) {
    86  	for i, tt := range shortenLabelTests {
    87  		result := ShortenLabel(tt.in, tt.pkg)
    88  		if result != tt.result {
    89  			t.Errorf("%d. ShortenLabel(%q, %q) => %q, want %q",
    90  				i, tt.in, tt.pkg, result, tt.result)
    91  		}
    92  	}
    93  }
    94  
    95  var labelsEqualTests = []struct {
    96  	label1   string
    97  	label2   string
    98  	pkg      string
    99  	expected bool
   100  }{
   101  	{"//devtools/buildozer:rule", "rule", "devtools/buildozer", true},
   102  	{"//devtools/buildozer:rule", "rule:jar", "devtools", false},
   103  }
   104  
   105  func TestLabelsEqual(t *testing.T) {
   106  	for i, tt := range labelsEqualTests {
   107  		if got := LabelsEqual(tt.label1, tt.label2, tt.pkg); got != tt.expected {
   108  			t.Errorf("%d. LabelsEqual(%q, %q, %q) => %v, want %v",
   109  				i, tt.label1, tt.label2, tt.pkg, got, tt.expected)
   110  		}
   111  	}
   112  }
   113  
   114  var splitOnSpacesTests = []struct {
   115  	in  string
   116  	out []string
   117  }{
   118  	{"a", []string{"a"}},
   119  	{"  abc def ", []string{"abc", "def"}},
   120  	{`  abc\ def `, []string{"abc def"}},
   121  	{"  abc def\nghi", []string{"abc", "def", "ghi"}},
   122  }
   123  
   124  func TestSplitOnSpaces(t *testing.T) {
   125  	for i, tt := range splitOnSpacesTests {
   126  		result := SplitOnSpaces(tt.in)
   127  		if !reflect.DeepEqual(result, tt.out) {
   128  			t.Errorf("%d. SplitOnSpaces(%q) => %q, want %q",
   129  				i, tt.in, result, tt.out)
   130  		}
   131  	}
   132  }
   133  
   134  func TestInsertLoad(t *testing.T) {
   135  	tests := []struct{ input, expected string }{
   136  		{``, `load("location", "symbol")`},
   137  		{`load("location", "symbol")`, `load("location", "symbol")`},
   138  		{`load("location", "other", "symbol")`, `load("location", "other", "symbol")`},
   139  		{`load("location", "other")`, `load("location", "other", "symbol")`},
   140  		{
   141  			`load("other loc", "symbol")`,
   142  			`load("location", "symbol")
   143  load("other loc", "symbol")`,
   144  		},
   145  	}
   146  
   147  	for _, tst := range tests {
   148  		bld, err := build.Parse("BUILD", []byte(tst.input))
   149  		if err != nil {
   150  			t.Error(err)
   151  			continue
   152  		}
   153  		bld.Stmt = InsertLoad(bld.Stmt, "location", []string{"symbol"}, []string{"symbol"})
   154  		got := strings.TrimSpace(string(build.Format(bld)))
   155  		if got != tst.expected {
   156  			t.Errorf("maybeInsertLoad(%s): got %s, expected %s", tst.input, got, tst.expected)
   157  		}
   158  	}
   159  }
   160  
   161  func TestReplaceLoad(t *testing.T) {
   162  	tests := []struct{ input, expected string }{
   163  		{
   164  			``,
   165  			`load("new_location", "symbol")`,
   166  		},
   167  		{
   168  			`load("location", "symbol")`,
   169  			`load("new_location", "symbol")`,
   170  		},
   171  		{
   172  			`load("location", "other", "symbol")`,
   173  			`load("new_location", "symbol")
   174  load("location", "other")`,
   175  		},
   176  		{
   177  			`load("location", symbol = "other")`,
   178  			`load("new_location", "symbol")`,
   179  		},
   180  		{
   181  			`load("other loc", "symbol")
   182  load("location", "symbol")`,
   183  			`load("new_location", "symbol")`,
   184  		},
   185  	}
   186  
   187  	for _, tst := range tests {
   188  		bld, err := build.Parse("BUILD", []byte(tst.input))
   189  		if err != nil {
   190  			t.Error(err)
   191  			continue
   192  		}
   193  		bld.Stmt = ReplaceLoad(bld.Stmt, "new_location", []string{"symbol"}, []string{"symbol"})
   194  		got := strings.TrimSpace(string(build.Format(bld)))
   195  		if got != tst.expected {
   196  			t.Errorf("maybeReplaceLoad(%s): got %s, expected %s", tst.input, got, tst.expected)
   197  		}
   198  	}
   199  }
   200  
   201  func TestAddValueToListAttribute(t *testing.T) {
   202  	tests := []struct{ input, expected string }{
   203  		{`rule(name="rule")`, `rule(name="rule", attr=["foo"])`},
   204  		{`rule(name="rule", attr=["foo"])`, `rule(name="rule", attr=["foo"])`},
   205  		{`rule(name="rule", attr=IDENT)`, `rule(name="rule", attr=IDENT+["foo"])`},
   206  		{`rule(name="rule", attr=["foo"] + IDENT)`, `rule(name="rule", attr=["foo"] + IDENT)`},
   207  		{`rule(name="rule", attr=["bar"] + IDENT)`, `rule(name="rule", attr=["bar", "foo"] + IDENT)`},
   208  		{`rule(name="rule", attr=IDENT + ["foo"])`, `rule(name="rule", attr=IDENT + ["foo"])`},
   209  		{`rule(name="rule", attr=IDENT + ["bar"])`, `rule(name="rule", attr=IDENT + ["bar", "foo"])`},
   210  	}
   211  
   212  	for _, tst := range tests {
   213  		bld, err := build.Parse("BUILD", []byte(tst.input))
   214  		if err != nil {
   215  			t.Error(err)
   216  			continue
   217  		}
   218  		rule := bld.RuleAt(1)
   219  		AddValueToListAttribute(rule, "attr", "", &build.StringExpr{Value: "foo"}, nil)
   220  		got := strings.TrimSpace(string(build.Format(bld)))
   221  
   222  		wantBld, err := build.Parse("BUILD", []byte(tst.expected))
   223  		if err != nil {
   224  			t.Error(err)
   225  			continue
   226  		}
   227  		want := strings.TrimSpace(string(build.Format(wantBld)))
   228  		if got != want {
   229  			t.Errorf("AddValueToListAttribute(%s): got %s, expected %s", tst.input, got, want)
   230  		}
   231  	}
   232  }
   233  
   234  func TestSelectListsIntersection(t *testing.T) {
   235  	tests := []struct {
   236  		input    string
   237  		expected []build.Expr
   238  	}{
   239  		{`rule(
   240  			name = "rule",
   241  			attr = select()
   242  		)`, nil},
   243  		{`rule(
   244  			name = "rule",
   245  			attr = select({})
   246  		)`, nil},
   247  		{`rule(
   248  			name = "rule",
   249  			attr = select(CONFIGS)
   250  		)`, nil},
   251  		{`rule(
   252  			name = "rule",
   253  			attr = select({
   254  				"config": "string",
   255  				"DEFAULT": "default"
   256  			})
   257  		)`, nil},
   258  		{`rule(
   259  			name = "rule",
   260  			attr = select({
   261  				"config": LIST,
   262  				"DEFAULT": DEFAULT
   263  			})
   264  		)`, nil},
   265  		{`rule(
   266  			name = "rule",
   267  			attr = select({
   268  				"config": ":1 :2 :3".split(" "),
   269  				"DEFAULT": ":2 :3".split(" ")
   270  			})
   271  		)`, nil},
   272  		{`rule(
   273  			name = "rule",
   274  			attr = select({
   275  				"config1": [":1"],
   276  				"config2": [":2"],
   277  				"DEFAULT": []
   278  			})
   279  		)`, []build.Expr{}},
   280  		{`rule(
   281  			name = "rule",
   282  			attr = select({
   283  				"config1": [],
   284  				"config2": [":1"],
   285  				"DEFAULT": [":1"]
   286  			})
   287  		)`, []build.Expr{}},
   288  		{`rule(
   289  			name = "rule",
   290  			attr = select({
   291  				"config1": [":1", ":2", ":3"],
   292  				"config2": [":2"],
   293  				"config3": [":2", ":3"],
   294  				"DEFAULT": [":1", ":2"]
   295  			})
   296  		)`, []build.Expr{&build.StringExpr{Value: ":2"}}},
   297  		{`rule(
   298  			name = "rule",
   299  			attr = select({
   300  				"config1": [":4", ":3", ":1", ":5", ":2", ":6"],
   301  				"config2": [":5", ":2", ":6", ":1"],
   302  				"config3": [":1", ":2", ":3", ":4", ":5", ":6"],
   303  				"config4": [":2", ":1"],
   304  				"DEFAULT": [":3", ":4", ":1", ":2"]
   305  			})
   306  		)`, []build.Expr{&build.StringExpr{Value: ":1"}, &build.StringExpr{Value: ":2"}}},
   307  	}
   308  
   309  	for _, tst := range tests {
   310  		bld, err := build.Parse("BUILD", []byte(tst.input))
   311  		if err != nil {
   312  			t.Error(err)
   313  			continue
   314  		}
   315  		rule := bld.RuleAt(1)
   316  
   317  		got := SelectListsIntersection(rule.Attr("attr").(*build.CallExpr), "")
   318  		errStr := fmt.Sprintf("TestSelectListsIntersection(%s): got %s, expected %s", tst.input, got, tst.expected)
   319  
   320  		if len(got) != len(tst.expected) {
   321  			t.Error(errStr)
   322  		}
   323  
   324  		for i := range got {
   325  			if got[i].(*build.StringExpr).Value != tst.expected[i].(*build.StringExpr).Value {
   326  				t.Error(errStr)
   327  			}
   328  		}
   329  	}
   330  }
   331  
   332  func TestRemoveEmptySelectsAndConcatLists(t *testing.T) {
   333  	tests := []struct{ input, expected string }{
   334  		{`rule(
   335  			name = "rule",
   336  			attr = select({
   337  				"config1": [],
   338  				"config2": [],
   339  				"DEFAULT": []
   340  			})
   341  		)`, `rule(
   342  			name = "rule",
   343  			attr = []
   344  		)`},
   345  		{`rule(
   346  			name = "rule",
   347  			attr = select({}) + select() + select({
   348  				"config1": [],
   349  				"config2": [],
   350  				"DEFAULT": []
   351  			})
   352  		)`, `rule(
   353  			name = "rule",
   354  			attr = []
   355  		)`},
   356  		{`rule(
   357  			name = "rule",
   358  			attr = select({
   359  				"config1": [],
   360  				"config2": [],
   361  				"DEFAULT": []
   362  			}) + select(CONFIGS)
   363  		)`, `rule(
   364  			name = "rule",
   365  			attr = select(CONFIGS)
   366  		)`},
   367  		{`rule(
   368  			name = "rule",
   369  			attr = [":1"] + select({
   370  				"config1": [],
   371  				"config2": [],
   372  				"DEFAULT": []
   373  			}) + [":2"]
   374  		)`, `rule(
   375  			name = "rule",
   376  			attr = [":1", ":2"]
   377  		)`},
   378  		{`rule(
   379  			name = "rule",
   380  			attr = [":1"] + select({
   381  				"config1": [],
   382  				"config2": [],
   383  				"DEFAULT": []
   384  			}) + LIST + [":2"]
   385  		)`, `rule(
   386  			name = "rule",
   387  			attr = [":1"] + LIST + [":2"]
   388  		)`},
   389  		{`rule(
   390  			name = "rule",
   391  			attr = [":1"] + [":2", ":3"] + select({
   392  				"config1": [":4"],
   393  				"config2": [],
   394  				"DEFAULT": []
   395  			}) + []
   396  		)`, `rule(
   397  			name = "rule",
   398  			attr = [":1", ":2", ":3"] + select({
   399  				"config1": [":4"],
   400  				"config2": [],
   401  				"DEFAULT": []
   402  			})
   403  		)`},
   404  		{`rule(
   405  			name = "rule",
   406  			attr = [":1"] + [":2", ":3"] + select({
   407  				"config1": [":4"],
   408  				"config2": [],
   409  				"DEFAULT": []
   410  			}) + [] + select({
   411  				"config": LIST,
   412  				"DEFAULT": DEFAULT,
   413  			})
   414  		)`, `rule(
   415  			name = "rule",
   416  			attr = [":1", ":2", ":3"] + select({
   417  				"config1": [":4"],
   418  				"config2": [],
   419  				"DEFAULT": []
   420  			}) + select({
   421  				"config": LIST,
   422  				"DEFAULT": DEFAULT,
   423  			})
   424  		)`},
   425  	}
   426  
   427  	for _, tst := range tests {
   428  		bld, err := build.Parse("BUILD", []byte(tst.input))
   429  		if err != nil {
   430  			t.Error(err)
   431  			continue
   432  		}
   433  		rule := bld.RuleAt(1)
   434  		rule.SetAttr("attr", RemoveEmptySelectsAndConcatLists(rule.Attr("attr")))
   435  		got := strings.TrimSpace(string(build.Format(bld)))
   436  
   437  		wantBld, err := build.Parse("BUILD", []byte(tst.expected))
   438  		if err != nil {
   439  			t.Error(err)
   440  			continue
   441  		}
   442  		want := strings.TrimSpace(string(build.Format(wantBld)))
   443  		if got != want {
   444  			t.Errorf("RemoveEmptySelectsAndConcatLists(%s):\n got: %s,\n expected: %s", tst.input, got, want)
   445  		}
   446  	}
   447  }
   448  
   449  func TestResolveAttr(t *testing.T) {
   450  	tests := []struct{ input, expected string }{
   451  		{`rule(
   452  			name = "rule",
   453  			attr = select({
   454  				"config1": [":1"],
   455  				"config2": [":1"],
   456  				"DEFAULT": [":1"]
   457  			})
   458  		)`, `rule(
   459  			name = "rule",
   460  			attr = [":1"]
   461  		)`},
   462  		{`rule(
   463  			name = "rule",
   464  			attr = select({
   465  				"config1": [":1"],
   466  				"config2": [":1"],
   467  				"DEFAULT": [":1"]
   468  			}) + select() + select({})
   469  		)`, `rule(
   470  			name = "rule",
   471  			attr = [":1"]
   472  		)`},
   473  		{`rule(
   474  			name = "rule",
   475  			attr = select({
   476  				"config1": [":1"],
   477  				"config2": [":1"],
   478  				"DEFAULT": [":1"]
   479  			}) + LIST
   480  		)`, `rule(
   481  			name = "rule",
   482  			attr = LIST + [":1"]
   483  		)`},
   484  		{`rule(
   485  			name = "rule",
   486  			attr = select({
   487  				"config1": [":1"],
   488  				"config2": [":1"],
   489  				"DEFAULT": [":1"]
   490  			}) + select({
   491  				"config": LIST,
   492  				"DEFAULT": DEFAULT
   493  			}) + select({
   494  				"config": ":2 :3".split(" "),
   495  				"DEFAULT": ":3".split(" ")
   496  			})
   497  		)`, `rule(
   498  			name = "rule",
   499  			attr = select({
   500  				"config": LIST,
   501  				"DEFAULT": DEFAULT
   502  			}) + select({
   503  				"config": ":2 :3".split(" "),
   504  				"DEFAULT": ":3".split(" ")
   505  			}) + [":1"]
   506  		)`},
   507  		{`rule(
   508  			name = "rule",
   509  			attr = [":1"] + select({
   510  				"config1": [":2"],
   511  				"config2": [":2"],
   512  				"DEFAULT": [":2"]
   513  			}) + [":3"] + select({
   514  				"config1": [":4", ":2"],
   515  				"DEFAULT": [":2"]
   516  			})
   517  		)`, `rule(
   518  			name = "rule",
   519  			attr = [":1", ":2", ":3"] + select({
   520  				"config1": [":4"],
   521  				"DEFAULT": []
   522  			})
   523  		)`},
   524  		{`rule(
   525  			name = "rule",
   526  			attr = [":1"] + select({
   527  				"config1": [":2"],
   528  				"config2": [":2"],
   529  				"DEFAULT": [":2"]
   530  			}) + [":3"] + select({
   531  				"config1": [":4", ":2"],
   532  				"DEFAULT": [":4", ":2"]
   533  			})
   534  		)`, `rule(
   535  			name = "rule",
   536  			attr = [":1", ":2", ":4", ":3"]
   537  		)`},
   538  	}
   539  
   540  	for _, tst := range tests {
   541  		bld, err := build.Parse("BUILD", []byte(tst.input))
   542  		if err != nil {
   543  			t.Error(err)
   544  			continue
   545  		}
   546  		rule := bld.RuleAt(1)
   547  		ResolveAttr(rule, "attr", "")
   548  		got := strings.TrimSpace(string(build.Format(bld)))
   549  
   550  		wantBld, err := build.Parse("BUILD", []byte(tst.expected))
   551  		if err != nil {
   552  			t.Error(err)
   553  			continue
   554  		}
   555  		want := strings.TrimSpace(string(build.Format(wantBld)))
   556  		if got != want {
   557  			t.Errorf("ResolveAttr(%s):\n got: %s\n expected: %s", tst.input, got, want)
   558  		}
   559  	}
   560  }
   561  
   562  func TestListSubstitute(t *testing.T) {
   563  	tests := []struct {
   564  		desc, input, oldPattern, newTemplate, want string
   565  	}{
   566  		{
   567  			desc:        "no_match",
   568  			input:       `["abc"]`,
   569  			oldPattern:  `!!`,
   570  			newTemplate: `xx`,
   571  			want:        `["abc"]`,
   572  		}, {
   573  			desc:        "full_match",
   574  			input:       `["abc"]`,
   575  			oldPattern:  `.*`,
   576  			newTemplate: `xx`,
   577  			want:        `["xx"]`,
   578  		}, {
   579  			desc:        "partial_match",
   580  			input:       `["abcde"]`,
   581  			oldPattern:  `bcd`,
   582  			newTemplate: `xyz`,
   583  			want:        `["axyze"]`,
   584  		}, {
   585  			desc:        "number_group",
   586  			input:       `["abcde"]`,
   587  			oldPattern:  `a(bcd)`,
   588  			newTemplate: `$1 $1`,
   589  			want:        `["bcd bcde"]`,
   590  		}, {
   591  			desc:        "name_group",
   592  			input:       `["abcde"]`,
   593  			oldPattern:  `a(?P<x>bcd)`,
   594  			newTemplate: `$x $x`,
   595  			want:        `["bcd bcde"]`,
   596  		},
   597  	}
   598  
   599  	for _, tst := range tests {
   600  		t.Run(tst.desc, func(t *testing.T) {
   601  			f, err := build.ParseBuild("BUILD", []byte(tst.input))
   602  			if err != nil {
   603  				t.Fatalf("parse error: %v", err)
   604  			}
   605  			lst := f.Stmt[0]
   606  			oldRegexp, err := regexp.Compile(tst.oldPattern)
   607  			if err != nil {
   608  				t.Fatalf("error compiling regexp %q: %v", tst.oldPattern, err)
   609  			}
   610  			ListSubstitute(lst, oldRegexp, tst.newTemplate)
   611  			if got := build.FormatString(lst); got != tst.want {
   612  				t.Errorf("ListSubstitute(%q, %q, %q) = %q ; want %q", tst.input, tst.oldPattern, tst.newTemplate, got, tst.want)
   613  			}
   614  		})
   615  	}
   616  }
   617  
   618  func compareKeyValue(a, b build.Expr) bool {
   619  	if a == nil && b == nil {
   620  		return true
   621  	}
   622  	if a == nil || b == nil {
   623  		return false
   624  	}
   625  	aKeyVal := a.(*build.KeyValueExpr)
   626  	bKeyVal := b.(*build.KeyValueExpr)
   627  	return aKeyVal.Key.(*build.StringExpr).Value == bKeyVal.Key.(*build.StringExpr).Value &&
   628  		aKeyVal.Value.(*build.StringExpr).Value == bKeyVal.Value.(*build.StringExpr).Value
   629  }
   630  
   631  func TestDictionaryDelete(t *testing.T) {
   632  	tests := []struct {
   633  		input, expected string
   634  		expectedReturn  build.Expr
   635  	}{
   636  		{
   637  			`rule(attr = {"deletekey": "value"})`,
   638  			`rule(attr = {})`,
   639  			&build.KeyValueExpr{
   640  				Key:   &build.StringExpr{Value: "deletekey"},
   641  				Value: &build.StringExpr{Value: "value"},
   642  			},
   643  		}, {
   644  			`rule(attr = {"nodeletekey": "value", "deletekey": "value"})`,
   645  			`rule(attr = {"nodeletekey": "value"})`,
   646  			&build.KeyValueExpr{
   647  				Key:   &build.StringExpr{Value: "deletekey"},
   648  				Value: &build.StringExpr{Value: "value"},
   649  			},
   650  		}, {
   651  			`rule(attr = {"nodeletekey": "value"})`,
   652  			`rule(attr = {"nodeletekey": "value"})`,
   653  			nil,
   654  		},
   655  	}
   656  
   657  	for _, tst := range tests {
   658  		bld, err := build.ParseBuild("BUILD", []byte(tst.input))
   659  		if err != nil {
   660  			t.Error(err)
   661  			continue
   662  		}
   663  		rule := bld.RuleAt(1)
   664  		dict := rule.Call.List[0].(*build.AssignExpr).RHS.(*build.DictExpr)
   665  		returnVal := DictionaryDelete(dict, "deletekey")
   666  		got := strings.TrimSpace(string(build.Format(bld)))
   667  		wantBld, err := build.Parse("BUILD", []byte(tst.expected))
   668  		if err != nil {
   669  			t.Error(err)
   670  			continue
   671  		}
   672  		want := strings.TrimSpace(string(build.Format(wantBld)))
   673  		if got != want {
   674  			t.Errorf("TestDictionaryDelete(%s): got %s, expected %s", tst.input, got, want)
   675  		}
   676  		if !compareKeyValue(returnVal, tst.expectedReturn) {
   677  			t.Errorf("TestDictionaryDelete(%s): returned %v, expected %v", tst.input, returnVal, tst.expectedReturn)
   678  		}
   679  	}
   680  }
   681  
   682  func TestPackageDeclaration(t *testing.T) {
   683  	tests := []struct{ input, expected string }{
   684  		{``, `package(attr = "val")`},
   685  		{`"""Docstring."""
   686  
   687  load(":path.bzl", "x")
   688  
   689  # package() comes here
   690  
   691  x = 2`,
   692  			`"""Docstring."""
   693  
   694  load(":path.bzl", "x")
   695  
   696  # package() comes here
   697  
   698  package(attr = "val")
   699  
   700  x = 2`,
   701  		},
   702  	}
   703  
   704  	for _, tst := range tests {
   705  		bld, err := build.Parse("BUILD", []byte(tst.input))
   706  		if err != nil {
   707  			t.Error(err)
   708  			continue
   709  		}
   710  		pkg := PackageDeclaration(bld)
   711  		pkg.SetAttr("attr", &build.StringExpr{Value: "val"})
   712  		got := strings.TrimSpace(string(build.Format(bld)))
   713  		want := strings.TrimSpace(tst.expected)
   714  
   715  		if got != want {
   716  			t.Errorf("TestPackageDeclaration: got:\n%s\nexpected:\n%s", got, want)
   717  		}
   718  	}
   719  }
   720  
   721  type testCase struct {
   722  	inputRoot, inputTarget                       string
   723  	expectedBuildFile, expectedPkg, expectedRule string
   724  }
   725  
   726  func runTestInterpretLabelForWorkspaceLocation(t *testing.T, buildFileName string) {
   727  	tmp, err := ioutil.TempDir("", "")
   728  	if err != nil {
   729  		t.Fatal(err)
   730  	}
   731  	defer os.RemoveAll(tmp)
   732  	if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil {
   733  		t.Fatal(err)
   734  	}
   735  	if err := ioutil.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil {
   736  		t.Fatal(err)
   737  	}
   738  	if err := ioutil.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil {
   739  		t.Fatal(err)
   740  	}
   741  	if err := ioutil.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil {
   742  		t.Fatal(err)
   743  	}
   744  	if err := ioutil.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil {
   745  		t.Fatal(err)
   746  	}
   747  
   748  	for _, tc := range []testCase{
   749  		{tmp, "//", filepath.Join(tmp, buildFileName), "", "."},
   750  		{tmp, "//a", filepath.Join(tmp, "a", buildFileName), "a", "a"},
   751  		{tmp, "//a:a", filepath.Join(tmp, "a", buildFileName), "a", "a"},
   752  		{tmp, "//a/b", filepath.Join(tmp, "a", "b", buildFileName), "a/b", "b"},
   753  		{tmp, "//a/b:b", filepath.Join(tmp, "a", "b", buildFileName), "a/b", "b"},
   754  	} {
   755  		buildFile, pkg, rule := InterpretLabelForWorkspaceLocation(tc.inputRoot, tc.inputTarget)
   756  		if buildFile != tc.expectedBuildFile || pkg != tc.expectedPkg || rule != tc.expectedRule {
   757  			t.Errorf("InterpretLabelForWorkspaceLocation(%q, %q) = %q, %q, %q; want %q, %q, %q", tc.inputRoot, tc.inputTarget, buildFile, pkg, rule, tc.expectedBuildFile, tc.expectedPkg, tc.expectedRule)
   758  		}
   759  	}
   760  }
   761  
   762  func TestInterpretLabelForWorkspaceLocation(t *testing.T) {
   763  	runTestInterpretLabelForWorkspaceLocation(t, "BUILD")
   764  	runTestInterpretLabelForWorkspaceLocation(t, "BUILD.bazel")
   765  }