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

     1  /*
     2  Copyright 2019 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  
    14  package edit
    15  
    16  import (
    17  	"io/ioutil"
    18  	"os"
    19  	"path/filepath"
    20  	"reflect"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/cheshirekow/buildtools/build"
    25  )
    26  
    27  var removeCommentTests = []struct {
    28  	args      []string
    29  	buildFile string
    30  	expected  string
    31  }{
    32  	{[]string{},
    33  		`# comment
    34  	foo(
    35  		name = "foo",
    36  	)`,
    37  		`foo(
    38      name = "foo",
    39  )`,
    40  	},
    41  	{[]string{
    42  		"name",
    43  	},
    44  		`foo(
    45  		# comment
    46  		name = "foo",
    47  	)`,
    48  		`foo(
    49      name = "foo",
    50  )`,
    51  	},
    52  	{[]string{
    53  		"name",
    54  	},
    55  		`foo(
    56  		name = "foo"  # comment,
    57  	)`,
    58  		`foo(
    59      name = "foo",
    60  )`,
    61  	},
    62  	{[]string{
    63  		"deps", "bar",
    64  	},
    65  		`foo(
    66  		name = "foo",
    67  		deps = [
    68  				# comment
    69  				"bar",
    70  				"baz",
    71  		],
    72  	)`,
    73  		`foo(
    74      name = "foo",
    75      deps = [
    76          "bar",
    77          "baz",
    78      ],
    79  )`,
    80  	},
    81  	{[]string{
    82  		"deps", "bar",
    83  	},
    84  		`foo(
    85  		name = "foo",
    86  		deps = [
    87  				"bar",  # comment
    88  				"baz",
    89  		],
    90  	)`,
    91  		`foo(
    92      name = "foo",
    93      deps = [
    94          "bar",
    95          "baz",
    96      ],
    97  )`,
    98  	},
    99  }
   100  
   101  func TestCmdRemoveComment(t *testing.T) {
   102  	for i, tt := range removeCommentTests {
   103  		bld, err := build.Parse("BUILD", []byte(tt.buildFile))
   104  		if err != nil {
   105  			t.Error(err)
   106  			continue
   107  		}
   108  		rl := bld.Rules("foo")[0]
   109  		env := CmdEnvironment{
   110  			File: bld,
   111  			Rule: rl,
   112  			Args: tt.args,
   113  		}
   114  		bld, _ = cmdRemoveComment(NewOpts(), env)
   115  		got := strings.TrimSpace(string(build.Format(bld)))
   116  		if got != tt.expected {
   117  			t.Errorf("cmdRemoveComment(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
   118  		}
   119  	}
   120  }
   121  
   122  type targetExpressionToBuildFilesTestCase struct {
   123  	rootDir, target string
   124  	buildFiles      []string
   125  }
   126  
   127  func runTestTargetExpressionToBuildFiles(t *testing.T, buildFileName string) {
   128  	tmp, err := ioutil.TempDir("", "")
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	// On MacOS "/tmp" is a symlink to "/private/tmp". Resolve it to make the testing easier
   134  	tmp, err = filepath.EvalSymlinks(tmp)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	defer os.RemoveAll(tmp)
   140  	if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	if err := os.MkdirAll(filepath.Join(tmp, "a", "c"), 0755); err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	if err := ioutil.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	if err := ioutil.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil {
   150  		t.Fatal(err)
   151  	}
   152  	if err := ioutil.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if err := ioutil.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	if err := ioutil.WriteFile(filepath.Join(tmp, "a", "c", buildFileName), nil, 0755); err != nil {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	for _, tc := range []targetExpressionToBuildFilesTestCase{
   163  		{tmp, "//", []string{filepath.Join(tmp, buildFileName)}},
   164  		{tmp, "//:foo", []string{filepath.Join(tmp, buildFileName)}},
   165  		{tmp, "//a", []string{filepath.Join(tmp, "a", buildFileName)}},
   166  		{tmp, "//a:foo", []string{filepath.Join(tmp, "a", buildFileName)}},
   167  		{tmp, "//a/b", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
   168  		{tmp, "//a/b:foo", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
   169  		{tmp, "//...", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
   170  		{tmp, "//a/...", []string{filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
   171  		{tmp, "//a/b/...", []string{filepath.Join(tmp, "a", "b", buildFileName)}},
   172  		{tmp, "//a/c/...", []string{filepath.Join(tmp, "a", "c", buildFileName)}},
   173  		{tmp, "//a/c/...:foo", []string{filepath.Join(tmp, "a", "c", buildFileName)}},
   174  		{"", "...:foo", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}},
   175  	} {
   176  		if tc.rootDir == "" {
   177  			cwd, err := os.Getwd()
   178  			if err != nil {
   179  				t.Fatal(err)
   180  			}
   181  			// buildozer should be able to find the WORKSPACE file in the current wd
   182  			if err := os.Chdir(tmp); err != nil {
   183  				t.Fatal(err)
   184  			}
   185  			defer os.Chdir(cwd)
   186  		}
   187  
   188  		buildFiles := targetExpressionToBuildFiles(tc.rootDir, tc.target)
   189  		expectedBuildFilesMap := make(map[string]bool)
   190  		buildFilesMap := make(map[string]bool)
   191  		for _, buildFile := range buildFiles {
   192  			buildFilesMap[buildFile] = true
   193  		}
   194  		for _, buildFile := range tc.buildFiles {
   195  			expectedBuildFilesMap[buildFile] = true
   196  		}
   197  		if !reflect.DeepEqual(expectedBuildFilesMap, buildFilesMap) {
   198  			t.Errorf("TargetExpressionToBuildFiles(%q, %q) = %q want %q", tc.rootDir, tc.target, buildFiles, tc.buildFiles)
   199  		}
   200  	}
   201  }
   202  
   203  func TestTargetExpressionToBuildFiles(t *testing.T) {
   204  	runTestTargetExpressionToBuildFiles(t, "BUILD")
   205  	runTestTargetExpressionToBuildFiles(t, "BUILD.bazel")
   206  }
   207  
   208  var dictListAddTests = []struct {
   209  	args      []string
   210  	buildFile string
   211  	expected  string
   212  }{
   213  	{[]string{
   214  		"attr", "key1", "value1",
   215  	},
   216  		`foo(
   217  		name = "foo",
   218  	)`,
   219  		`foo(
   220      name = "foo",
   221      attr = {"key1": ["value1"]},
   222  )`,
   223  	},
   224  	{[]string{
   225  		"attr", "key1", "value2",
   226  	},
   227  		`foo(
   228  		name = "foo",
   229  		attr = {"key1": ["value1"]},
   230  	)`,
   231  		`foo(
   232      name = "foo",
   233      attr = {"key1": [
   234          "value1",
   235          "value2",
   236      ]},
   237  )`,
   238  	},
   239  	{[]string{
   240  		"attr", "key1", "value1", "value2",
   241  	},
   242  		`foo(
   243  		name = "foo",
   244  	)`,
   245  		`foo(
   246      name = "foo",
   247      attr = {"key1": [
   248          "value1",
   249          "value2",
   250      ]},
   251  )`,
   252  	},
   253  	{[]string{
   254  		"attr", "key2", "value2",
   255  	},
   256  		`foo(
   257  		name = "foo",
   258  		attr = {"key1": ["value1"]},
   259  	)`,
   260  		`foo(
   261      name = "foo",
   262      attr = {
   263          "key1": ["value1"],
   264          "key2": ["value2"],
   265      },
   266  )`,
   267  	},
   268  	{[]string{
   269  		"attr", "key1", "value1",
   270  	},
   271  		`foo(
   272  		name = "foo",
   273  		attr = {"key1": ["value1"]},
   274  	)`,
   275  		`foo(
   276      name = "foo",
   277      attr = {"key1": ["value1"]},
   278  )`,
   279  	},
   280  }
   281  
   282  func TestCmdDictListAdd(t *testing.T) {
   283  	for i, tt := range dictListAddTests {
   284  		bld, err := build.Parse("BUILD", []byte(tt.buildFile))
   285  		if err != nil {
   286  			t.Error(err)
   287  			continue
   288  		}
   289  		rl := bld.Rules("foo")[0]
   290  		env := CmdEnvironment{
   291  			File: bld,
   292  			Rule: rl,
   293  			Args: tt.args,
   294  		}
   295  		bld, _ = cmdDictListAdd(NewOpts(), env)
   296  		got := strings.TrimSpace(string(build.Format(bld)))
   297  		if got != tt.expected {
   298  			t.Errorf("cmdDictListAdd(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
   299  		}
   300  	}
   301  }
   302  
   303  var substituteLoadsTests = []struct {
   304  	args      []string
   305  	buildFile string
   306  	expected  string
   307  }{
   308  	{[]string{
   309  		"^(.*)$", "${1}",
   310  	},
   311  		`load("//foo:foo.bzl", "foo")`,
   312  		`load("//foo:foo.bzl", "foo")`,
   313  	},
   314  	{[]string{
   315  		"^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl",
   316  	},
   317  		`load("@rules_bar//bar:defs.bzl", "bar")`,
   318  		`load("@rules_bar//bar:defs.bzl", "bar")`,
   319  	},
   320  	{[]string{
   321  		"^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl",
   322  	},
   323  		`load("@rules_foo//foo:defs.bzl", "foo", "foo2")
   324  load("@rules_bar//bar:defs.bzl", "bar")`,
   325  		`load("//build/rules/foo:defs.bzl", "foo", "foo2")
   326  load("@rules_bar//bar:defs.bzl", "bar")`,
   327  	},
   328  	{[]string{
   329  		":foo.bzl$", ":defs.bzl",
   330  	},
   331  		`load("//foo:foo.bzl", "foo")`,
   332  		`load("//foo:defs.bzl", "foo")`,
   333  	},
   334  	{[]string{
   335  		// Keep in sync with the example in `//buildozer:README.md`.
   336  		"^@([^/]*)//([^:].*)$", "//third_party/build_defs/${1}/${2}",
   337  	},
   338  		`load("@rules_foo//foo:defs.bzl", "foo", "foo2")
   339  load("@rules_bar//bar:defs.bzl", "bar")
   340  load("@rules_bar//:defs.bzl", legacy_bar = "bar")`,
   341  		`load("//third_party/build_defs/rules_foo/foo:defs.bzl", "foo", "foo2")
   342  load("//third_party/build_defs/rules_bar/bar:defs.bzl", "bar")
   343  load("@rules_bar//:defs.bzl", legacy_bar = "bar")`,
   344  	},
   345  }
   346  
   347  func TestCmdSubstituteLoad(t *testing.T) {
   348  	for i, tt := range substituteLoadsTests {
   349  		bld, err := build.Parse("BUILD", []byte(tt.buildFile))
   350  		if err != nil {
   351  			t.Error(err)
   352  			continue
   353  		}
   354  		env := CmdEnvironment{
   355  			File: bld,
   356  			Args: tt.args,
   357  		}
   358  		bld, _ = cmdSubstituteLoad(NewOpts(), env)
   359  		got := strings.TrimSpace(string(build.Format(bld)))
   360  		if got != tt.expected {
   361  			t.Errorf("cmdSubstituteLoad(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected)
   362  		}
   363  	}
   364  }