...

Source file src/github.com/bazelbuild/buildtools/edit/bzlmod/bzlmod_test.go

Documentation: github.com/bazelbuild/buildtools/edit/bzlmod

     1  /*
     2  Copyright 2023 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bzlmod
    18  
    19  import (
    20  	"reflect"
    21  	"strconv"
    22  	"testing"
    23  
    24  	"github.com/bazelbuild/buildtools/build"
    25  )
    26  
    27  const proxiesNoModuleHeader = ``
    28  
    29  const proxiesModuleNameHeader = `
    30  module(name = "name")
    31  `
    32  
    33  const proxiesModuleRepoNameHeader = `
    34  module(name = "name", repo_name = "repo_name")
    35  `
    36  
    37  const proxiesBody = `
    38  prox1 = use_extension("@name//bzl:extensions.bzl", "ext")
    39  prox1.use(name = "foo")
    40  isolated_prox1 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = False, isolate = True)
    41  isolated_prox1.use(name = "foo")
    42  prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True)
    43  prox2.use(list = ["foo", "bar"])
    44  # some comment
    45  prox3 = use_extension("@repo_name//bzl:extensions.bzl", "ext")
    46  prox3.use(label = "@dep//:bar")
    47  isolated_prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True)
    48  isolated_prox2.use(name = "foo")
    49  prox4 = use_extension("@repo_name//bzl:extensions.bzl", "ext", dev_dependency = True)
    50  prox4.use(dict = {"foo": "bar"})
    51  isolated_prox3 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True)
    52  isolated_prox3.use(name = "foo")
    53  prox5 = use_extension("@//bzl:extensions.bzl", "ext")
    54  prox5.use(name = "foo")
    55  prox6 = use_extension("@//bzl:extensions.bzl", "ext", dev_dependency = True)
    56  prox6.use(list = ["foo", "bar"])
    57  prox7 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = False)
    58  prox7.use(label = "@foo//:bar")
    59  isolated_prox4 = use_extension("@name//bzl:extensions.bzl", "ext", isolate = True)
    60  isolated_prox4.use(name = "foo")
    61  prox8 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True)
    62  prox8.use(dict = {"foo": "bar"})
    63  prox9 = use_extension(
    64      # comment
    65      "@dep//:extensions.bzl", "other_ext")
    66  prox9.use(label = "@name//:bar")
    67  prox10 = use_extension("@dep//:extensions.bzl", "other_ext", dev_dependency = bool(1))
    68  prox10.use(dict = {"foo": "bar"})
    69  `
    70  
    71  func TestProxies(t *testing.T) {
    72  	for i, tc := range []struct {
    73  		content         string
    74  		extBzlFiles     []string
    75  		extName         string
    76  		dev             bool
    77  		expectedProxies []string
    78  	}{
    79  		{
    80  			proxiesNoModuleHeader + proxiesBody,
    81  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"},
    82  			"ext",
    83  			false,
    84  			[]string{"prox5", "prox7"},
    85  		},
    86  		{
    87  			proxiesNoModuleHeader + proxiesBody,
    88  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"},
    89  			"ext",
    90  			true,
    91  			[]string{"prox6", "prox8"},
    92  		},
    93  		{
    94  			proxiesModuleNameHeader + proxiesBody,
    95  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"},
    96  			"ext",
    97  			false,
    98  			[]string{"prox1", "prox5", "prox7"},
    99  		},
   100  		{
   101  			proxiesModuleNameHeader + proxiesBody,
   102  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"},
   103  			"ext",
   104  			true,
   105  			[]string{"prox2", "prox6", "prox8"},
   106  		},
   107  		{
   108  			proxiesModuleRepoNameHeader + proxiesBody,
   109  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"},
   110  			"ext",
   111  			false,
   112  			[]string{"prox3", "prox5", "prox7"},
   113  		},
   114  		{
   115  			proxiesModuleRepoNameHeader + proxiesBody,
   116  			[]string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"},
   117  			"ext",
   118  			true,
   119  			[]string{"prox4", "prox6", "prox8"},
   120  		},
   121  		{
   122  			proxiesModuleRepoNameHeader + proxiesBody,
   123  			[]string{"@name//bzl:extensions.bzl"},
   124  			"ext",
   125  			false,
   126  			[]string{"prox1"},
   127  		},
   128  		{
   129  			proxiesModuleRepoNameHeader + proxiesBody,
   130  			[]string{"@dep//:extensions.bzl"},
   131  			"other_ext",
   132  			false,
   133  			[]string{"prox9"},
   134  		},
   135  		{
   136  			proxiesModuleRepoNameHeader + proxiesBody,
   137  			[]string{"@dep//:extensions.bzl"},
   138  			"other_ext",
   139  			true,
   140  			[]string{"prox10"},
   141  		},
   142  	} {
   143  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   144  			for _, extBzlFile := range tc.extBzlFiles {
   145  				t.Run("label_"+extBzlFile, func(t *testing.T) {
   146  
   147  					f, err := build.ParseModule("MODULE.bazel", []byte(tc.content))
   148  					if err != nil {
   149  						t.Fatal(err)
   150  					}
   151  					actualProxies := Proxies(f, extBzlFile, tc.extName, tc.dev)
   152  					if !reflect.DeepEqual(actualProxies, tc.expectedProxies) {
   153  						t.Error("want: ", tc.expectedProxies, ", got: ", actualProxies)
   154  					}
   155  				})
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  func TestAllProxies(t *testing.T) {
   162  	for i, tc := range []struct {
   163  		proxy           string
   164  		expectedProxies []string
   165  	}{
   166  		{
   167  			"invalid_proxy",
   168  			nil,
   169  		},
   170  		{
   171  			"isolated_prox1",
   172  			[]string{"isolated_prox1"},
   173  		},
   174  		{
   175  			"isolated_prox2",
   176  			[]string{"isolated_prox2"},
   177  		},
   178  		{
   179  			"isolated_prox3",
   180  			[]string{"isolated_prox3"},
   181  		},
   182  		{
   183  			"isolated_prox4",
   184  			[]string{"isolated_prox4"},
   185  		},
   186  		{
   187  			"prox1",
   188  			[]string{"prox1", "prox5", "prox7"},
   189  		},
   190  		{
   191  			"prox2",
   192  			[]string{"prox2", "prox6", "prox8"},
   193  		},
   194  		{
   195  			"prox9",
   196  			[]string{"prox9"},
   197  		},
   198  		{
   199  			"prox10",
   200  			[]string{"prox10"},
   201  		},
   202  	} {
   203  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   204  			f, err := build.ParseModule("MODULE.bazel", []byte(proxiesModuleNameHeader+proxiesBody))
   205  			if err != nil {
   206  				t.Fatal(err)
   207  			}
   208  			proxies := AllProxies(f, tc.proxy)
   209  			if !reflect.DeepEqual(proxies, tc.expectedProxies) {
   210  				t.Error("want: ", tc.expectedProxies, ", got: ", proxies)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  const useReposFile = `
   217  prox1 = use_extension("@mod//bzl:extensions.bzl", "ext")
   218  prox1.use()
   219  prox1.config()
   220  prox2 = use_extension("//bzl:extensions.bzl", "ext")
   221  use_repo(prox2)
   222  use_repo(prox1, "repo5")
   223  prox3 = use_extension("@dep//bzl:extensions.bzl", "ext")
   224  prox2_dev = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True)
   225  use_repo(prox1, "repo1")
   226  use_repo(prox3, "repo2")
   227  use_repo(prox2, "repo3", "repo4")
   228  `
   229  
   230  func TestUseRepos(t *testing.T) {
   231  	for i, tc := range []struct {
   232  		content       string
   233  		proxies       []string
   234  		expectedStmts []int
   235  	}{
   236  		{
   237  			useReposFile,
   238  			[]string{"prox2"},
   239  			[]int{4, 10},
   240  		},
   241  		{
   242  			useReposFile,
   243  			[]string{"prox1", "prox3"},
   244  			[]int{5, 8, 9},
   245  		},
   246  		{
   247  			useReposFile,
   248  			[]string{"prox2_dev"},
   249  			[]int{},
   250  		},
   251  	} {
   252  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   253  			f, err := build.ParseModule("MODULE.bazel", []byte(tc.content))
   254  			if err != nil {
   255  				t.Fatal(err)
   256  			}
   257  			var expectedUseRepos []*build.CallExpr
   258  			for _, stmt := range tc.expectedStmts {
   259  				expectedUseRepos = append(expectedUseRepos, f.Stmt[stmt].(*build.CallExpr))
   260  			}
   261  			actualUseRepos := UseRepos(f, tc.proxies)
   262  			if !reflect.DeepEqual(actualUseRepos, expectedUseRepos) {
   263  				t.Error("want: ", expectedUseRepos, ", got: ", actualUseRepos)
   264  			}
   265  		})
   266  	}
   267  }
   268  
   269  func TestNewUseRepos(t *testing.T) {
   270  	for i, tc := range []struct {
   271  		content         string
   272  		proxies         []string
   273  		expectedContent string
   274  		expectedUseRepo int
   275  	}{
   276  		{
   277  			``,
   278  			[]string{"prox1"},
   279  			``,
   280  			-1,
   281  		},
   282  		{
   283  			`prox1 = use_extension("@module//:lib.bzl", "ext")`,
   284  			[]string{"prox2"},
   285  			`prox1 = use_extension("@module//:lib.bzl", "ext")
   286  `,
   287  			-1,
   288  		},
   289  		{
   290  			`prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True)`,
   291  			[]string{"prox1"},
   292  			`prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True)
   293  use_repo(prox1)
   294  `,
   295  			1,
   296  		},
   297  		{
   298  			`prox1 = use_extension("@mod//bzl:extensions.bzl", "ext")
   299  prox1.config()
   300  prox1.download(name = "foo")`,
   301  			[]string{"prox1"},
   302  			`prox1 = use_extension("@mod//bzl:extensions.bzl", "ext")
   303  prox1.config()
   304  prox1.download(name = "foo")
   305  use_repo(prox1)
   306  `,
   307  			3,
   308  		},
   309  		{
   310  			`go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
   311  go_deps.from_file(go_mod = "//:go.mod")
   312  
   313  pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps")
   314  pull.oci_pull(name = "distroless_base")
   315  `,
   316  			[]string{"go_deps"},
   317  			`go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
   318  go_deps.from_file(go_mod = "//:go.mod")
   319  use_repo(go_deps)
   320  
   321  pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps")
   322  pull.oci_pull(name = "distroless_base")
   323  `,
   324  			2,
   325  		},
   326  		{
   327  			`go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
   328  go_deps.from_file(go_mod = "//:go.mod")
   329  
   330  pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps")
   331  `,
   332  			[]string{"go_deps"},
   333  			`go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
   334  go_deps.from_file(go_mod = "//:go.mod")
   335  use_repo(go_deps)
   336  
   337  pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps")
   338  `,
   339  			2,
   340  		},
   341  	} {
   342  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   343  			f, err := build.ParseModule("MODULE.bazel", []byte(tc.content))
   344  			if err != nil {
   345  				t.Fatal(err)
   346  			}
   347  			f, actualNewUseRepo := NewUseRepo(f, tc.proxies)
   348  			actualContent := string(build.Format(f))
   349  			if actualNewUseRepo != nil {
   350  				if !reflect.DeepEqual(actualNewUseRepo, f.Stmt[tc.expectedUseRepo]) {
   351  					t.Error("want: ", f.Stmt[tc.expectedUseRepo], ", got: ", actualNewUseRepo)
   352  				}
   353  			} else {
   354  				if tc.expectedUseRepo != -1 {
   355  					t.Error("wanted a nil new use_repo")
   356  				}
   357  			}
   358  			if !reflect.DeepEqual(actualContent, tc.expectedContent) {
   359  				t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent)
   360  			}
   361  		})
   362  	}
   363  }
   364  
   365  func TestAddRepoUsages(t *testing.T) {
   366  	for i, tc := range []struct {
   367  		content         string
   368  		repos           []string
   369  		expectedContent string
   370  	}{
   371  		{
   372  			``,
   373  			[]string{},
   374  			``,
   375  		},
   376  		{
   377  			`use_repo(prox)`,
   378  			[]string{"repo2", "repo1"},
   379  			`use_repo(prox, "repo1", "repo2")
   380  `,
   381  		},
   382  		{
   383  			`use_repo(
   384      prox,
   385  )`,
   386  			[]string{"repo2", "repo1"},
   387  			`use_repo(
   388      prox,
   389      "repo1",
   390      "repo2",
   391  )
   392  `,
   393  		},
   394  		{
   395  			`use_repo(prox, "repo2")`,
   396  			[]string{"repo2", "repo1"},
   397  			`use_repo(prox, "repo1", "repo2")
   398  `,
   399  		},
   400  		{
   401  			`use_repo(
   402      prox,
   403      "repo2",
   404  )`,
   405  			[]string{"repo2", "repo1"},
   406  			`use_repo(
   407      prox,
   408      "repo1",
   409      "repo2",
   410  )
   411  `,
   412  		},
   413  		{
   414  			`use_repo(prox, "repo1")
   415  use_repo(
   416      prox2,
   417      my_repo = "repo2",
   418      "repo5",
   419  )
   420  use_repo(prox, "repo3")`,
   421  			[]string{"repo3", "repo2", "repo4"},
   422  			`use_repo(prox, "repo1")
   423  
   424  use_repo(
   425      prox2,
   426      "repo5",
   427      my_repo = "repo2",
   428  )
   429  
   430  use_repo(prox, "repo3", "repo4")
   431  `,
   432  		},
   433  	} {
   434  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   435  			f, err := build.ParseModule("MODULE.bazel", []byte(tc.content))
   436  			if err != nil {
   437  				t.Fatal(err)
   438  			}
   439  			var useRepos []*build.CallExpr
   440  			for _, stmt := range f.Stmt {
   441  				useRepos = append(useRepos, stmt.(*build.CallExpr))
   442  			}
   443  			AddRepoUsages(useRepos, tc.repos...)
   444  			actualContent := string(build.Format(f))
   445  			if !reflect.DeepEqual(actualContent, tc.expectedContent) {
   446  				t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent)
   447  			}
   448  		})
   449  	}
   450  }
   451  
   452  func TestRemoveRepoUsages(t *testing.T) {
   453  	for i, tc := range []struct {
   454  		content         string
   455  		repos           []string
   456  		expectedContent string
   457  	}{
   458  		{
   459  			``,
   460  			[]string{"repo1"},
   461  			``,
   462  		},
   463  		{
   464  			`use_repo(prox)`,
   465  			[]string{"repo2", "repo1"},
   466  			`use_repo(prox)
   467  `,
   468  		},
   469  		{
   470  			`use_repo(
   471      prox,
   472  )`,
   473  			[]string{"repo2", "repo1"},
   474  			`use_repo(
   475      prox,
   476  )
   477  `,
   478  		},
   479  		{
   480  			`use_repo(prox, "repo2")`,
   481  			[]string{"repo2", "repo1"},
   482  			`use_repo(prox)
   483  `,
   484  		},
   485  		{
   486  			`use_repo(
   487      prox,
   488      "repo2",
   489  )`,
   490  			[]string{"repo2", "repo1"},
   491  			`use_repo(prox)
   492  `,
   493  		},
   494  		{
   495  			`use_repo(prox, "repo1")
   496  use_repo(
   497      prox2,
   498      my_repo = "repo2",
   499      "repo5",
   500  )
   501  use_repo(prox, "repo3")`,
   502  			[]string{"repo3", "repo2", "repo4"},
   503  			`use_repo(prox, "repo1")
   504  
   505  use_repo(
   506      prox2,
   507      "repo5",
   508  )
   509  
   510  use_repo(prox)
   511  `,
   512  		},
   513  	} {
   514  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   515  			f, err := build.ParseModule("MODULE.bazel", []byte(tc.content))
   516  			if err != nil {
   517  				t.Fatal(err)
   518  			}
   519  			var useRepos []*build.CallExpr
   520  			for _, stmt := range f.Stmt {
   521  				useRepos = append(useRepos, stmt.(*build.CallExpr))
   522  			}
   523  			RemoveRepoUsages(useRepos, tc.repos...)
   524  			actualContent := string(build.Format(f))
   525  			if !reflect.DeepEqual(actualContent, tc.expectedContent) {
   526  				t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent)
   527  			}
   528  		})
   529  	}
   530  }
   531  

View as plain text